src/main/java/de/uapcore/lightpit/PathPattern.java

changeset 179
623c340058f3
parent 178
88207b860cba
child 180
009700915269
equal deleted inserted replaced
178:88207b860cba 179:623c340058f3
1 package de.uapcore.lightpit;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 public final class PathPattern {
7
8 private final List<String> nodePatterns;
9 private final boolean collection;
10
11 /**
12 * Constructs a new path pattern.
13 * The special directories . and .. are disallowed in the pattern.
14 *
15 * @param pattern
16 */
17 public PathPattern(String pattern) {
18 nodePatterns = parse(pattern);
19 collection = pattern.endsWith("/");
20 }
21
22 private List<String> parse(String pattern) {
23
24 var nodes = new ArrayList<String>();
25 var parts = pattern.split("/");
26
27 for (var part : parts) {
28 if (part.isBlank()) continue;
29 if (part.equals(".") || part.equals(".."))
30 throw new IllegalArgumentException("Path must not contain '.' or '..' nodes.");
31 nodes.add(part);
32 }
33
34 return nodes;
35 }
36
37 /**
38 * Matches a path against this pattern.
39 * The path must be canonical in the sense that no . or .. parts occur.
40 *
41 * @param path the path to match
42 * @return true if the path matches the pattern, false otherwise
43 */
44 public boolean matches(String path) {
45 if (collection ^ path.endsWith("/"))
46 return false;
47
48 var nodes = parse(path);
49 if (nodePatterns.size() != nodes.size())
50 return false;
51
52 for (int i = 0 ; i < nodePatterns.size() ; i++) {
53 var pattern = nodePatterns.get(i);
54 var node = nodes.get(i);
55 if (pattern.startsWith("$"))
56 continue;
57 if (!pattern.equals(node))
58 return false;
59 }
60
61 return true;
62 }
63
64 /**
65 * Returns the path parameters found in the specified path using this pattern.
66 * The return value of this method is undefined, if the patter does not match.
67 *
68 * @param path the path
69 * @return the path parameters, if any, or an empty map
70 * @see #matches(String)
71 */
72 public PathParameters obtainPathParameters(String path) {
73 var params = new PathParameters();
74
75 var nodes = parse(path);
76
77 for (int i = 0 ; i < Math.min(nodes.size(), nodePatterns.size()) ; i++) {
78 var pattern = nodePatterns.get(i);
79 var node = nodes.get(i);
80 if (pattern.startsWith("$")) {
81 params.put(pattern.substring(1), node);
82 }
83 }
84
85 return params;
86 }
87
88 @Override
89 public int hashCode() {
90 var str = new StringBuilder();
91 for (var node : nodePatterns) {
92 if (node.startsWith("$")) {
93 str.append("/$");
94 } else {
95 str.append('/');
96 str.append(node);
97 }
98 }
99 if (collection)
100 str.append('/');
101
102 return str.toString().hashCode();
103 }
104
105 @Override
106 public boolean equals(Object obj) {
107 if (!obj.getClass().equals(PathPattern.class))
108 return false;
109
110 var other = (PathPattern) obj;
111 if (collection ^ other.collection || nodePatterns.size() != other.nodePatterns.size())
112 return false;
113
114 for (int i = 0 ; i < nodePatterns.size() ; i++) {
115 var left = nodePatterns.get(i);
116 var right = other.nodePatterns.get(i);
117 if (left.startsWith("$") && right.startsWith("$"))
118 continue;
119 if (!left.equals(right))
120 return false;
121 }
122
123 return true;
124 }
125 }

mercurial