1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.repository.sequencer;
25
26 import java.io.Serializable;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.regex.Pattern;
30 import net.jcip.annotations.Immutable;
31 import org.modeshape.common.util.CheckArg;
32 import org.modeshape.common.util.HashCode;
33 import org.modeshape.graph.property.PathExpression;
34 import org.modeshape.repository.RepositoryI18n;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 @Immutable
54 public class SequencerPathExpression implements Serializable {
55
56
57
58 private static final long serialVersionUID = 229464314137494765L;
59
60
61
62
63
64 private static final Pattern TWO_PART_PATTERN = Pattern.compile("((?:[^=]|=(?!>))+)(?:=>(.+))?");
65
66 protected static final String DEFAULT_OUTPUT_EXPRESSION = ".";
67
68 private static final String PARENT_PATTERN_STRING = "[^/]+/\\.\\./";
69 private static final Pattern PARENT_PATTERN = Pattern.compile(PARENT_PATTERN_STRING);
70
71 private static final String REPLACEMENT_VARIABLE_PATTERN_STRING = "(?<!\\\\)\\$(\\d+)";
72 private static final Pattern REPLACEMENT_VARIABLE_PATTERN = Pattern.compile(REPLACEMENT_VARIABLE_PATTERN_STRING);
73
74
75
76
77
78
79
80
81
82 public static final SequencerPathExpression compile( String expression ) throws InvalidSequencerPathExpression {
83 CheckArg.isNotNull(expression, "sequencer path expression");
84 expression = expression.trim();
85 if (expression.length() == 0) {
86 throw new InvalidSequencerPathExpression(RepositoryI18n.pathExpressionMayNotBeBlank.text());
87 }
88 java.util.regex.Matcher matcher = TWO_PART_PATTERN.matcher(expression);
89 if (!matcher.matches()) {
90 throw new InvalidSequencerPathExpression(RepositoryI18n.pathExpressionIsInvalid.text(expression));
91 }
92 String selectExpression = matcher.group(1);
93 String outputExpression = matcher.group(2);
94 return new SequencerPathExpression(PathExpression.compile(selectExpression), outputExpression);
95 }
96
97 private final PathExpression selectExpression;
98 private final String outputExpression;
99 private final int hc;
100
101 protected SequencerPathExpression( PathExpression selectExpression,
102 String outputExpression ) throws InvalidSequencerPathExpression {
103 CheckArg.isNotNull(selectExpression, "select expression");
104 this.selectExpression = selectExpression;
105 this.outputExpression = outputExpression != null ? outputExpression.trim() : DEFAULT_OUTPUT_EXPRESSION;
106 this.hc = HashCode.compute(this.selectExpression, this.outputExpression);
107 }
108
109
110
111
112 public String getSelectExpression() {
113 return this.selectExpression.getSelectExpression();
114 }
115
116
117
118
119 public String getOutputExpression() {
120 return this.outputExpression;
121 }
122
123
124
125
126 @Override
127 public int hashCode() {
128 return this.hc;
129 }
130
131
132
133
134 @Override
135 public boolean equals( Object obj ) {
136 if (obj == this) return true;
137 if (obj instanceof SequencerPathExpression) {
138 SequencerPathExpression that = (SequencerPathExpression)obj;
139 if (!this.selectExpression.equals(that.selectExpression)) return false;
140 if (!this.outputExpression.equalsIgnoreCase(that.outputExpression)) return false;
141 return true;
142 }
143 return false;
144 }
145
146
147
148
149 @Override
150 public String toString() {
151 return this.selectExpression + "=>" + this.outputExpression;
152 }
153
154
155
156
157
158 public Matcher matcher( String absolutePath ) {
159 PathExpression.Matcher inputMatcher = selectExpression.matcher(absolutePath);
160 String outputPath = null;
161 if (inputMatcher.matches()) {
162
163 Map<Integer, String> replacements = new HashMap<Integer, String>();
164 for (int i = 0, count = inputMatcher.groupCount(); i <= count; ++i) {
165 replacements.put(i, inputMatcher.group(i));
166 }
167
168
169 String selectedPath = inputMatcher.getSelectedNodePath();
170
171
172 outputPath = this.outputExpression;
173 if (!DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
174 java.util.regex.Matcher replacementMatcher = REPLACEMENT_VARIABLE_PATTERN.matcher(outputPath);
175 StringBuffer sb = new StringBuffer();
176 if (replacementMatcher.find()) {
177 do {
178 String variable = replacementMatcher.group(1);
179 String replacement = replacements.get(Integer.valueOf(variable));
180 if (replacement == null) replacement = replacementMatcher.group(0);
181 replacementMatcher.appendReplacement(sb, replacement);
182 } while (replacementMatcher.find());
183 replacementMatcher.appendTail(sb);
184 outputPath = sb.toString();
185 }
186
187 if (!outputPath.endsWith("/")) outputPath = outputPath + "/";
188
189
190 outputPath = outputPath.replaceAll("/\\./", "/");
191
192
193 java.util.regex.Matcher parentMatcher = PARENT_PATTERN.matcher(outputPath);
194 while (parentMatcher.find()) {
195 outputPath = parentMatcher.replaceAll("");
196
197 if (!outputPath.endsWith("/")) outputPath = outputPath + "/";
198 parentMatcher = PARENT_PATTERN.matcher(outputPath);
199 }
200
201
202 outputPath = outputPath.replaceAll("/{2,}", "/");
203
204
205 outputPath = outputPath.replaceAll("/@[^/\\[\\]]+$", "");
206
207
208 outputPath = outputPath.replaceAll("/$", "");
209
210
211 if (outputPath.length() == 0) outputPath = DEFAULT_OUTPUT_EXPRESSION;
212
213 }
214 if (DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
215
216 outputPath = selectedPath;
217 }
218 }
219
220 return new Matcher(inputMatcher, outputPath);
221 }
222
223 @Immutable
224 public static class Matcher {
225
226 private final PathExpression.Matcher inputMatcher;
227 private final String outputPath;
228 private final int hc;
229
230 protected Matcher( PathExpression.Matcher inputMatcher,
231 String outputPath ) {
232 this.inputMatcher = inputMatcher;
233 this.outputPath = outputPath;
234 this.hc = HashCode.compute(super.hashCode(), this.outputPath);
235 }
236
237 public boolean matches() {
238 return inputMatcher.matches() && this.outputPath != null;
239 }
240
241
242
243
244 public String getInputPath() {
245 return inputMatcher.getInputPath();
246 }
247
248
249
250
251 public String getSelectedPath() {
252 return inputMatcher.getSelectedNodePath();
253 }
254
255
256
257
258 public String getOutputPath() {
259 return this.outputPath;
260 }
261
262
263
264
265 @Override
266 public int hashCode() {
267 return this.hc;
268 }
269
270
271
272
273 @Override
274 public boolean equals( Object obj ) {
275 if (obj == this) return true;
276 if (obj instanceof SequencerPathExpression.Matcher) {
277 SequencerPathExpression.Matcher that = (SequencerPathExpression.Matcher)obj;
278 if (!super.equals(that)) return false;
279 if (!this.outputPath.equalsIgnoreCase(that.outputPath)) return false;
280 return true;
281 }
282 return false;
283 }
284
285
286
287
288 @Override
289 public String toString() {
290 return inputMatcher + " => " + this.outputPath;
291 }
292 }
293
294 }