Author: rhauch
Date: 2008-07-09 16:37:01 -0400 (Wed, 09 Jul 2008)
New Revision: 349
Added:
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/InvalidPathExpressionException.java
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathExpression.java
trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/PathExpressionTest.java
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java
trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java
trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties
Log:
DNA-173 - Create reusable PathExpression class in the SPI
http://jira.jboss.com/jira/browse/DNA-173
Moved the path expression functionality out of the SequencerPathExpression (which now
contains a PathExpression for the input along with an output expression) and into
PathExpression in the SPI. Added several test cases, including some new cases. There is
no change in the behavior, and all existing test cases still work.
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java 2008-07-09
15:04:15 UTC (rev 348)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java 2008-07-09
20:37:01 UTC (rev 349)
@@ -25,11 +25,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
import net.jcip.annotations.Immutable;
import org.jboss.dna.common.util.ArgCheck;
import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.repository.RepositoryI18n;
+import org.jboss.dna.spi.graph.PathExpression;
/**
* An expression that defines a selection of some change in the repository that signals a
sequencing operation should be run, and
@@ -47,6 +47,7 @@
* <code>title</code> property on the <code>/a/b/c</code> node,
and that the output of the sequencing should be placed at
* <code>/d/e/f</code>.
* </p>
+ *
* @author Randall Hauch
*/
@Immutable
@@ -64,40 +65,15 @@
protected static final String DEFAULT_OUTPUT_EXPRESSION = ".";
- private static final String REPLACEMENT_VARIABLE_PATTERN_STRING =
"(?<!\\\\)\\$(\\d+)"; // (?<!\\)\$(\d+)
- private static final Pattern REPLACEMENT_VARIABLE_PATTERN =
Pattern.compile(REPLACEMENT_VARIABLE_PATTERN_STRING);
-
private static final String PARENT_PATTERN_STRING = "[^/]+/\\.\\./"; //
[^/]+/\.\./
private static final Pattern PARENT_PATTERN =
Pattern.compile(PARENT_PATTERN_STRING);
- private static final String SEQUENCE_PATTERN_STRING =
"\\[(\\d+(?:,\\d+)*)\\]"; // \[(\d+(,\d+)*)\]
- private static final Pattern SEQUENCE_PATTERN =
Pattern.compile(SEQUENCE_PATTERN_STRING);
+ private static final String REPLACEMENT_VARIABLE_PATTERN_STRING =
"(?<!\\\\)\\$(\\d+)"; // (?<!\\)\$(\d+)
+ private static final Pattern REPLACEMENT_VARIABLE_PATTERN =
Pattern.compile(REPLACEMENT_VARIABLE_PATTERN_STRING);
/**
- * Regular expression used to find unusable XPath predicates within an expression.
This pattern results in unusable predicates
- * in group 1. Note that some predicates may be valid at the end but not valid
elsewhere.
- * <p>
- * Currently, only index-like predicates (including sequences) are allowed
everywhere. Predicates with paths and properties
- * are allowed only as the last predicate. Predicates with any operators are unused.
- * </p>
- * <p>
- * Nested predicates are not currently allowed.
- * </p>
- */
- //
\[(?:(?:\d+(?:,\d+)*)|\*)\]|(?:\[[^\]\+\-\*=\!><'"\s]+\])$|(\[[^\]]+\])
- private static final String UNUSABLE_PREDICATE_PATTERN_STRING =
"\\[(?:(?:\\d+(?:,\\d+)*)|\\*)\\]|(?:\\[[^\\]\\+\\-\\*=\\!><'\"\\s]+\\])$|(\\[[^\\]]+\\])";
- private static final Pattern UNUSABLE_PREDICATE_PATTERN =
Pattern.compile(UNUSABLE_PREDICATE_PATTERN_STRING);
-
- /**
- * Regular expression used to find all XPath predicates except index and sequence
patterns. This pattern results in the
- * predicates to be removed in group 1.
- */
- // \[(?:(?:\d+(?:,\d+)*)|\*)\]|(\[[^\]]+\])
- private static final String NON_INDEX_PREDICATE_PATTERN_STRING =
"\\[(?:(?:\\d+(?:,\\d+)*)|\\*)\\]|(\\[[^\\]]+\\])";
- private static final Pattern NON_INDEX_PREDICATE_PATTERN =
Pattern.compile(NON_INDEX_PREDICATE_PATTERN_STRING);
-
- /**
- * Compile the supplied expression and return the resulting SequencerPathExpression2
instance.
+ * Compile the supplied expression and return the resulting SequencerPathExpression
instance.
+ *
* @param expression the expression
* @return the path expression; never null
* @throws IllegalArgumentException if the expression is null
@@ -115,171 +91,26 @@
}
String selectExpression = matcher.group(1);
String outputExpression = matcher.group(2);
- return new SequencerPathExpression(selectExpression, outputExpression);
+ return new SequencerPathExpression(PathExpression.compile(selectExpression),
outputExpression);
}
- private final String selectExpression;
+ private final PathExpression selectExpression;
private final String outputExpression;
- private final Pattern matchPattern;
- private final Pattern selectPattern;
private final int hc;
- protected SequencerPathExpression( String selectExpression, String outputExpression )
throws InvalidSequencerPathExpression {
+ protected SequencerPathExpression( PathExpression selectExpression,
+ String outputExpression ) throws
InvalidSequencerPathExpression {
ArgCheck.isNotNull(selectExpression, "select expression");
- this.selectExpression = selectExpression.trim();
+ this.selectExpression = selectExpression;
this.outputExpression = outputExpression != null ? outputExpression.trim() :
DEFAULT_OUTPUT_EXPRESSION;
this.hc = HashCode.compute(this.selectExpression, this.outputExpression);
-
- // Build the match pattern, which determines whether a path matches the condition
...
- String matchString = this.selectExpression;
- try {
- matchString = removeUnusedPredicates(matchString);
- matchString = replaceXPathPatterns(matchString);
- this.matchPattern = Pattern.compile(matchString, Pattern.CASE_INSENSITIVE);
- } catch (PatternSyntaxException e) {
- String msg = RepositoryI18n.pathExpressionHasInvalidMatch.text(matchString,
this.selectExpression, this.outputExpression);
- throw new InvalidSequencerPathExpression(msg, e);
- }
- // Build the select pattern, which determines the path that will be selected ...
- String selectString = this.selectExpression.trim();
- try {
- selectString = removeAllPredicatesExceptIndexes(selectString);
- selectString = replaceXPathPatterns(selectString);
- selectString = "(" + selectString + ").*"; // group 1
will have selected path ...
- this.selectPattern = Pattern.compile(selectString,
Pattern.CASE_INSENSITIVE);
- } catch (PatternSyntaxException e) {
- String msg = RepositoryI18n.pathExpressionHasInvalidSelect.text(selectString,
this.selectExpression, this.outputExpression);
- throw new InvalidSequencerPathExpression(msg, e);
- }
}
/**
- * Replace certain XPath patterns that are not used or understood.
- * @param expression the input regular expressions string; may not be null
- * @return the regular expression with all unused XPath patterns removed; never null
- */
- protected String removeUnusedPredicates( String expression ) {
- assert expression != null;
- java.util.regex.Matcher matcher =
UNUSABLE_PREDICATE_PATTERN.matcher(expression);
- StringBuffer sb = new StringBuffer();
- if (matcher.find()) {
- do {
- // Remove those predicates that show up in group 1 ...
- String predicateStr = matcher.group(0);
- String unusablePredicateStr = matcher.group(1);
- if (unusablePredicateStr != null) {
- predicateStr = "";
- }
- matcher.appendReplacement(sb, predicateStr);
- } while (matcher.find());
- matcher.appendTail(sb);
- expression = sb.toString();
- }
- return expression;
- }
-
- /**
- * Remove all XPath predicates from the supplied regular expression string.
- * @param expression the input regular expressions string; may not be null
- * @return the regular expression with all XPath predicates removed; never null
- */
- protected String removeAllPredicatesExceptIndexes( String expression ) {
- assert expression != null;
- java.util.regex.Matcher matcher =
NON_INDEX_PREDICATE_PATTERN.matcher(expression);
- StringBuffer sb = new StringBuffer();
- if (matcher.find()) {
- do {
- // Remove those predicates that show up in group 1 ...
- String predicateStr = matcher.group(0);
- String unusablePredicateStr = matcher.group(1);
- if (unusablePredicateStr != null) {
- predicateStr = "";
- }
- matcher.appendReplacement(sb, predicateStr);
- } while (matcher.find());
- matcher.appendTail(sb);
- expression = sb.toString();
- }
- return expression;
- }
-
- /**
- * Replace certain XPath patterns, including some predicates, with substrings that
are compatible with regular expressions.
- * @param expression the input regular expressions string; may not be null
- * @return the regular expression with XPath patterns replaced with regular
expression fragments; never null
- */
- protected String replaceXPathPatterns( String expression ) {
- assert expression != null;
- // replace 2 or more sequential '|' characters in an OR expression
- expression = expression.replaceAll("[\\|]{2,}", "|");
- // if there is an empty expression in an OR expression, make the whole segment
optional ...
- // (e.g., "/a/b/(c|)/d" => "a/b(/(c))?/d"
- expression = expression.replaceAll("/(\\([^|]+)(\\|){2,}([^)]+\\))",
"(/$1$2$3)?");
- expression = expression.replaceAll("/\\(\\|+([^)]+)\\)",
"(/($1))?");
- expression = expression.replaceAll("/\\((([^|]+)(\\|[^|]+)*)\\|+\\)",
"(/($1))?");
-
- // // Allow any path (that doesn't contain an explicit counter) to contain a
counter,
- // // done by replacing any '/' or '|' that isn't preceded by
']' or '*' or '/' or '(' with '(\[\d+\])?/'...
- // input = input.replaceAll("(?<=[^\\]\\*/(])([/|])",
"(?:\\\\[\\\\d+\\\\])?$1");
-
- // Does the path contain any '[]' or '[*]' or '[0]' or
'[n]' (where n is any positive integers)...
- // '[*]/' => '(\[\d+\])?/'
- expression = expression.replaceAll("\\[\\]",
"(?:\\\\[\\\\d+\\\\])?"); // index is optional
- // '[]/' => '(\[\d+\])?/'
- expression = expression.replaceAll("\\[[*]\\]",
"(?:\\\\[\\\\d+\\\\])?"); // index is optional
- // '[0]/' => '(\[0\])?/'
- expression = expression.replaceAll("\\[0\\]",
"(?:\\\\[0\\\\])?"); // index is optional
- // '[n]/' => '\[n\]/'
- expression = expression.replaceAll("\\[([1-9]\\d*)\\]",
"\\\\[$1\\\\]"); // index is required
-
- // Change any other end predicates to not be wrapped by braces but to begin with
a slash ...
- // ...'[x]' => ...'/x'
- expression = expression.replaceAll("(?<!\\\\)\\[([^\\]]*)\\]$",
"/$1");
-
- // Replace all '[n,m,o,p]' type sequences with '[(n|m|o|p)]'
- java.util.regex.Matcher matcher = SEQUENCE_PATTERN.matcher(expression);
- StringBuffer sb = new StringBuffer();
- boolean result = matcher.find();
- if (result) {
- do {
- String sequenceStr = matcher.group(1);
- boolean optional = false;
- if (sequenceStr.startsWith("0,")) {
- sequenceStr = sequenceStr.replaceFirst("^0,",
"");
- optional = true;
- }
- if (sequenceStr.endsWith(",0")) {
- sequenceStr = sequenceStr.replaceFirst(",0$",
"");
- optional = true;
- }
- if (sequenceStr.contains(",0,")) {
- sequenceStr = sequenceStr.replaceAll(",0,",
",");
- optional = true;
- }
- sequenceStr = sequenceStr.replaceAll(",", "|");
- String replacement = "\\\\[(?:" + sequenceStr +
")\\\\]";
- if (optional) {
- replacement = "(?:" + replacement + ")?";
- }
- matcher.appendReplacement(sb, replacement);
- result = matcher.find();
- } while (result);
- matcher.appendTail(sb);
- expression = sb.toString();
- }
-
- // Order is important here
- expression = expression.replaceAll("[*]([^/])",
"[^/$1]*$1");
- expression = expression.replaceAll("(?<!\\[\\^/\\])[*]",
"[^/]*");
- expression = expression.replaceAll("[/]{2,}",
"(?:/[^/]*)*/");
- return expression;
- }
-
- /**
* @return selectExpression
*/
public String getSelectExpression() {
- return this.selectExpression;
+ return this.selectExpression.getSelectExpression();
}
/**
@@ -305,7 +136,7 @@
if (obj == this) return true;
if (obj instanceof SequencerPathExpression) {
SequencerPathExpression that = (SequencerPathExpression)obj;
- if (!this.selectExpression.equalsIgnoreCase(that.selectExpression)) return
false;
+ if (!this.selectExpression.equals(that.selectExpression)) return false;
if (!this.outputExpression.equalsIgnoreCase(that.outputExpression)) return
false;
return true;
}
@@ -325,117 +156,100 @@
* @return the matcher
*/
public Matcher matcher( String absolutePath ) {
- // Determine if the input path match the select expression ...
- String originalAbsolutePath = absolutePath;
- // if (!absolutePath.endsWith("/")) absolutePath = absolutePath +
"/";
- // Remove all trailing '/' ...
- absolutePath = absolutePath.replaceAll("/+$", "");
-
- // See if the supplied absolute path matches the pattern ...
- final java.util.regex.Matcher matcher = this.matchPattern.matcher(absolutePath);
- if (!matcher.matches()) {
- // No match, so return immediately ...
- return new Matcher(originalAbsolutePath, null, null);
- }
- Map<Integer, String> replacements = new HashMap<Integer, String>();
- for (int i = 0, count = matcher.groupCount(); i <= count; ++i) {
- replacements.put(i, matcher.group(i));
- }
-
- // The absolute path does match the pattern, so use the select pattern and try to
grab the selected path ...
- final java.util.regex.Matcher selectMatcher =
this.selectPattern.matcher(absolutePath);
- if (!selectMatcher.matches()) {
- // Nothing can be selected, so return immediately ...
- return new Matcher(originalAbsolutePath, null, null);
- }
- // Grab the selected path ...
- String selectedPath = selectMatcher.group(1);
-
- // Remove the trailing '/@property' ...
- selectedPath = selectedPath.replaceAll("/@[^/\\[\\]]+$",
"");
-
- // Find the output path using the groups from the match pattern ...
- String outputPath = this.outputExpression;
- if (!DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
- java.util.regex.Matcher replacementMatcher =
REPLACEMENT_VARIABLE_PATTERN.matcher(outputPath);
- StringBuffer sb = new StringBuffer();
- if (replacementMatcher.find()) {
- do {
- String variable = replacementMatcher.group(1);
- String replacement = replacements.get(Integer.valueOf(variable));
- if (replacement == null) replacement = replacementMatcher.group(0);
- replacementMatcher.appendReplacement(sb, replacement);
- } while (replacementMatcher.find());
- replacementMatcher.appendTail(sb);
- outputPath = sb.toString();
+ PathExpression.Matcher inputMatcher = selectExpression.matcher(absolutePath);
+ String outputPath = null;
+ if (inputMatcher.matches()) {
+ // Grab the named groups ...
+ Map<Integer, String> replacements = new HashMap<Integer,
String>();
+ for (int i = 0, count = inputMatcher.groupCount(); i <= count; ++i) {
+ replacements.put(i, inputMatcher.group(i));
}
- // Make sure there is a trailing '/' ...
- if (!outputPath.endsWith("/")) outputPath = outputPath +
"/";
- // Replace all references to "/./" with "/" ...
- outputPath = outputPath.replaceAll("/\\./", "/");
+ // Grab the selected path ...
+ String selectedPath = inputMatcher.getSelectedNodePath();
- // Remove any path segment followed by a parent reference ...
- java.util.regex.Matcher parentMatcher = PARENT_PATTERN.matcher(outputPath);
- while (parentMatcher.find()) {
- outputPath = parentMatcher.replaceAll("");
+ // Find the output path using the groups from the match pattern ...
+ outputPath = this.outputExpression;
+ if (!DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
+ java.util.regex.Matcher replacementMatcher =
REPLACEMENT_VARIABLE_PATTERN.matcher(outputPath);
+ StringBuffer sb = new StringBuffer();
+ if (replacementMatcher.find()) {
+ do {
+ String variable = replacementMatcher.group(1);
+ String replacement =
replacements.get(Integer.valueOf(variable));
+ if (replacement == null) replacement =
replacementMatcher.group(0);
+ replacementMatcher.appendReplacement(sb, replacement);
+ } while (replacementMatcher.find());
+ replacementMatcher.appendTail(sb);
+ outputPath = sb.toString();
+ }
// Make sure there is a trailing '/' ...
if (!outputPath.endsWith("/")) outputPath = outputPath +
"/";
- parentMatcher = PARENT_PATTERN.matcher(outputPath);
- }
- // Remove all multiple occurrences of '/' ...
- outputPath = outputPath.replaceAll("/{2,}", "/");
+ // Replace all references to "/./" with "/" ...
+ outputPath = outputPath.replaceAll("/\\./", "/");
- // Remove the trailing '/@property' ...
- outputPath = outputPath.replaceAll("/@[^/\\[\\]]+$",
"");
+ // Remove any path segment followed by a parent reference ...
+ java.util.regex.Matcher parentMatcher =
PARENT_PATTERN.matcher(outputPath);
+ while (parentMatcher.find()) {
+ outputPath = parentMatcher.replaceAll("");
+ // Make sure there is a trailing '/' ...
+ if (!outputPath.endsWith("/")) outputPath = outputPath +
"/";
+ parentMatcher = PARENT_PATTERN.matcher(outputPath);
+ }
- // Remove a trailing '/' ...
- outputPath = outputPath.replaceAll("/$", "");
+ // Remove all multiple occurrences of '/' ...
+ outputPath = outputPath.replaceAll("/{2,}", "/");
- // If the output path is blank, then use the default output expression ...
- if (outputPath.length() == 0) outputPath = DEFAULT_OUTPUT_EXPRESSION;
+ // Remove the trailing '/@property' ...
+ outputPath = outputPath.replaceAll("/@[^/\\[\\]]+$",
"");
+ // Remove a trailing '/' ...
+ outputPath = outputPath.replaceAll("/$", "");
+
+ // If the output path is blank, then use the default output expression
...
+ if (outputPath.length() == 0) outputPath = DEFAULT_OUTPUT_EXPRESSION;
+
+ }
+ if (DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
+ // The output path is the default expression, so use the selected path
...
+ outputPath = selectedPath;
+ }
}
- if (DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
- // The output path is the default expression, so use the selected path ...
- outputPath = selectedPath;
- }
- return new Matcher(originalAbsolutePath, selectedPath, outputPath);
+ return new Matcher(inputMatcher, outputPath);
}
@Immutable
public static class Matcher {
- private final String inputPath;
- private final String selectedPath;
+ private final PathExpression.Matcher inputMatcher;
private final String outputPath;
private final int hc;
- protected Matcher( String inputPath, String selectedPath, String outputPath ) {
- this.inputPath = inputPath;
- this.selectedPath = selectedPath;
+ protected Matcher( PathExpression.Matcher inputMatcher,
+ String outputPath ) {
+ this.inputMatcher = inputMatcher;
this.outputPath = outputPath;
- this.hc = HashCode.compute(this.inputPath, this.selectedPath,
this.outputPath);
+ this.hc = HashCode.compute(super.hashCode(), this.outputPath);
}
public boolean matches() {
- return this.selectedPath != null && this.outputPath != null;
+ return inputMatcher.matches() && this.outputPath != null;
}
/**
* @return inputPath
*/
public String getInputPath() {
- return this.inputPath;
+ return inputMatcher.getInputPath();
}
/**
* @return selectPattern
*/
public String getSelectedPath() {
- return this.selectedPath;
+ return inputMatcher.getSelectedNodePath();
}
/**
@@ -461,8 +275,7 @@
if (obj == this) return true;
if (obj instanceof SequencerPathExpression.Matcher) {
SequencerPathExpression.Matcher that =
(SequencerPathExpression.Matcher)obj;
- if (!this.inputPath.equalsIgnoreCase(that.inputPath)) return false;
- if (!this.selectedPath.equalsIgnoreCase(that.selectedPath)) return
false;
+ if (!super.equals(that)) return false;
if (!this.outputPath.equalsIgnoreCase(that.outputPath)) return false;
return true;
}
@@ -474,7 +287,7 @@
*/
@Override
public String toString() {
- return this.selectedPath + " => " + this.outputPath;
+ return inputMatcher + " => " + this.outputPath;
}
}
Modified:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java 2008-07-09
15:04:15 UTC (rev 348)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java 2008-07-09
20:37:01 UTC (rev 349)
@@ -25,8 +25,7 @@
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
-import org.jboss.dna.repository.sequencers.InvalidSequencerPathExpression;
-import org.jboss.dna.repository.sequencers.SequencerPathExpression;
+import org.jboss.dna.spi.graph.PathExpression;
import org.junit.Before;
import org.junit.Test;
@@ -39,7 +38,7 @@
@Before
public void beforeEach() throws Exception {
- expr = new SequencerPathExpression(".*", "/output");
+ expr = new SequencerPathExpression(new PathExpression(".*"),
"/output");
}
@Test( expected = IllegalArgumentException.class )
@@ -102,86 +101,6 @@
assertThat(SequencerPathExpression.compile("/a/b[0]/c[1]/d/e[2]"),
is(notNullValue()));
}
- @Test
- public void shouldNotRemoveUsedPredicates() {
- assertThat(expr.removeUnusedPredicates("/a/b/c"),
is("/a/b/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[0]/c"),
is("/a/b[0]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[1]/c"),
is("/a/b[1]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[10]/c"),
is("/a/b[10]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[100]/c"),
is("/a/b[100]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[1000]/c"),
is("/a/b[1000]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[]/c"),
is("/a/b[]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[*]/c"),
is("/a/b[*]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[1,2]/c"),
is("/a/b[1,2]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[1,2,3,4,5]/c"),
is("/a/b[1,2,3,4,5]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b/c[@title]"),
is("/a/b/c[@title]"));
- assertThat(expr.removeUnusedPredicates("/a/b/c[d/e/@title]"),
is("/a/b/c[d/e/@title]"));
-
assertThat(expr.removeUnusedPredicates("/a/(b/c)[(d|e)/(f|g)/@something]"),
is("/a/(b/c)[(d|e)/(f|g)/@something]"));
- // These are legal, but aren't really useful ...
- assertThat(expr.removeUnusedPredicates("/a/b[1][2][3]/c"),
is("/a/b[1][2][3]/c"));
- }
-
- @Test
- public void shouldRemoveUnusedPredicates() {
- assertThat(expr.removeUnusedPredicates("/a/b[-1]/c"),
is("/a/b/c"));
-
assertThat(expr.removeUnusedPredicates("/a/b[@name='wacky']/c"),
is("/a/b/c"));
-
assertThat(expr.removeUnusedPredicates("/a/b[3][@name='wacky']/c"),
is("/a/b[3]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[3][@name]/c"),
is("/a/b[3]/c"));
- assertThat(expr.removeUnusedPredicates("/a/b[length(@name)=3]/c"),
is("/a/b/c"));
- }
-
- @Test
- public void shouldRemoveAllPredicates() {
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b/c"),
is("/a/b/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[0]/c"),
is("/a/b[0]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1]/c"),
is("/a/b[1]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[10]/c"),
is("/a/b[10]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[100]/c"),
is("/a/b[100]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1000]/c"),
is("/a/b[1000]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[]/c"),
is("/a/b[]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[*]/c"),
is("/a/b[*]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b/c[@title]"),
is("/a/b/c"));
- // These are legal, but aren't really useful ...
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1][2][3]/c"),
is("/a/b[1][2][3]/c"));
-
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[-1]/c"),
is("/a/b/c"));
-
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[@name='wacky']/c"),
is("/a/b/c"));
-
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[3][@name='wacky']/c"),
is("/a/b[3]/c"));
- assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[3][@name]/c"),
is("/a/b[3]/c"));
-
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[length(@name)=3]/c"),
is("/a/b/c"));
- }
-
- @Test
- public void shouldReplaceAllXPathPatterns() {
- assertThat(expr.replaceXPathPatterns("/a/b[3]/c"),
is("/a/b\\[3\\]/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[*]/c"),
is("/a/b(?:\\[\\d+\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[]/c"),
is("/a/b(?:\\[\\d+\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[0]/c"),
is("/a/b(?:\\[0\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[0,1,2,4]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[1,2,4,0]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[1,2,0,4]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[0,1,2,0,4,0]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[1,2,4]/c"),
is("/a/b\\[(?:1|2|4)\\]/c"));
- assertThat(expr.replaceXPathPatterns("/a/b[@param]"),
is("/a/b/@param"));
- assertThat(expr.replaceXPathPatterns("/a/b[3][@param]"),
is("/a/b\\[3\\]/@param"));
- assertThat(expr.replaceXPathPatterns("/a/b[c/d/@param]"),
is("/a/b/c/d/@param"));
-
- assertThat(expr.replaceXPathPatterns("/a/(b|c|d)/e"),
is("/a/(b|c|d)/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b||c|d)/e"),
is("/a/(b|c|d)/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b|||c|d)/e"),
is("/a/(b|c|d)/e"));
- assertThat(expr.replaceXPathPatterns("/a/(|b|c|d)/e"),
is("/a(/(b|c|d))?/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b|c|d|)/e"),
is("/a(/(b|c|d))?/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b|c|d)[]/e"),
is("/a/(b|c|d)(?:\\[\\d+\\])?/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b|c[2]|d[])/e"),
is("/a/(b|c\\[2\\]|d(?:\\[\\d+\\])?)/e"));
- assertThat(expr.replaceXPathPatterns("/a/(b|c/d|e)/f"),
is("/a/(b|c/d|e)/f"));
-
assertThat(expr.replaceXPathPatterns("/a/(b/c)[(d|e)/(f|g)/@something]"),
is("/a/(b/c)/(d|e)/(f|g)/@something"));
-
- assertThat(expr.replaceXPathPatterns("/a/*/f"),
is("/a/[^/]*/f"));
- assertThat(expr.replaceXPathPatterns("/a//f"),
is("/a(?:/[^/]*)*/f"));
- assertThat(expr.replaceXPathPatterns("/a///f"),
is("/a(?:/[^/]*)*/f"));
- assertThat(expr.replaceXPathPatterns("/a/////f"),
is("/a(?:/[^/]*)*/f"));
- }
-
protected void assertNotMatches( SequencerPathExpression.Matcher matcher ) {
assertThat(matcher, is(notNullValue()));
assertThat(matcher.getSelectedPath(), is(nullValue()));
@@ -189,7 +108,9 @@
assertThat(matcher.matches(), is(false));
}
- protected void assertMatches( SequencerPathExpression.Matcher matcher, String
selectedPath, String outputPath ) {
+ protected void assertMatches( SequencerPathExpression.Matcher matcher,
+ String selectedPath,
+ String outputPath ) {
assertThat(matcher, is(notNullValue()));
assertThat(matcher.getSelectedPath(), is(selectedPath));
assertThat(matcher.getOutputPath(), is(outputPath));
@@ -371,7 +292,9 @@
@Test
public void shouldMatchExpressionWithFilenamePatternAndChildProperty() {
expr =
SequencerPathExpression.compile("//(*.(jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd))[*]/jcr:content[@jcr:data]=>/images/$1");
- assertMatches(expr.matcher("/a/b/caution.png/jcr:content/@jcr:data"),
"/a/b/caution.png/jcr:content", "/images/caution.png");
+ assertMatches(expr.matcher("/a/b/caution.png/jcr:content/@jcr:data"),
+ "/a/b/caution.png/jcr:content",
+ "/images/caution.png");
}
}
Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java 2008-07-09 15:04:15 UTC
(rev 348)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java 2008-07-09 20:37:01 UTC
(rev 349)
@@ -1,77 +1,81 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
- */
-package org.jboss.dna.spi;
-
-import java.util.Locale;
-import java.util.Set;
-import org.jboss.dna.common.CommonI18n;
-import org.jboss.dna.common.i18n.I18n;
-
-/**
- * @author Randall Hauch
- * @author John Verhaeg
- */
-public final class SpiI18n {
-
- public static I18n closedConnectionMayNotBeUsed;
- public static I18n errorConvertingIo;
- public static I18n errorConvertingType;
- public static I18n errorReadingPropertyValueBytes;
- public static I18n invalidIndexInSegmentName;
- public static I18n invalidQualifiedNameString;
- public static I18n maximumPoolSizeMayNotBeSmallerThanCorePoolSize;
- public static I18n missingEndBracketInSegmentName;
- public static I18n noNamespaceRegisteredForPrefix;
- public static I18n pathAncestorDegreeIsInvalid;
- public static I18n pathCannotBeNormalized;
- public static I18n pathIsAlreadyAbsolute;
- public static I18n pathIsNotAbsolute;
- public static I18n pathIsNotRelative;
- public static I18n repositoryConnectionPoolIsNotRunning;
- public static I18n unableToCreateSubpathBeginIndexGreaterThanOrEqualToEndingIndex;
- public static I18n unableToCreateSubpathBeginIndexGreaterThanOrEqualToSize;
- public static I18n unableToCreateValue;
- public static I18n unableToDiscoverPropertyTypeForNullValue;
- public static I18n unableToObtainValidRepositoryAfterAttempts;
- public static I18n validPathMayNotContainEmptySegment;
- public static I18n valueJavaTypeNotCompatibleWithPropertyType;
-
- static {
- try {
- I18n.initialize(SpiI18n.class);
- } catch (final Exception err) {
- System.err.println(err);
- }
- }
-
- public static Set<Locale> getLocalizationProblemLocales() {
- return I18n.getLocalizationProblemLocales(CommonI18n.class);
- }
-
- public static Set<String> getLocalizationProblems() {
- return I18n.getLocalizationProblems(CommonI18n.class);
- }
-
- public static Set<String> getLocalizationProblems( Locale locale ) {
- return I18n.getLocalizationProblems(CommonI18n.class, locale);
- }
-}
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.spi;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.CommonI18n;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ * @author John Verhaeg
+ */
+public final class SpiI18n {
+
+ public static I18n closedConnectionMayNotBeUsed;
+ public static I18n errorConvertingIo;
+ public static I18n errorConvertingType;
+ public static I18n errorReadingPropertyValueBytes;
+ public static I18n invalidIndexInSegmentName;
+ public static I18n invalidQualifiedNameString;
+ public static I18n maximumPoolSizeMayNotBeSmallerThanCorePoolSize;
+ public static I18n missingEndBracketInSegmentName;
+ public static I18n noNamespaceRegisteredForPrefix;
+ public static I18n pathAncestorDegreeIsInvalid;
+ public static I18n pathCannotBeNormalized;
+ public static I18n pathIsAlreadyAbsolute;
+ public static I18n pathIsNotAbsolute;
+ public static I18n pathIsNotRelative;
+ public static I18n repositoryConnectionPoolIsNotRunning;
+ public static I18n unableToCreateSubpathBeginIndexGreaterThanOrEqualToEndingIndex;
+ public static I18n unableToCreateSubpathBeginIndexGreaterThanOrEqualToSize;
+ public static I18n unableToCreateValue;
+ public static I18n unableToDiscoverPropertyTypeForNullValue;
+ public static I18n unableToObtainValidRepositoryAfterAttempts;
+ public static I18n validPathMayNotContainEmptySegment;
+ public static I18n valueJavaTypeNotCompatibleWithPropertyType;
+ public static I18n pathExpressionMayNotBeBlank;
+ public static I18n pathExpressionIsInvalid;
+ public static I18n pathExpressionHasInvalidSelect;
+ public static I18n pathExpressionHasInvalidMatch;
+
+ static {
+ try {
+ I18n.initialize(SpiI18n.class);
+ } catch (final Exception err) {
+ System.err.println(err);
+ }
+ }
+
+ public static Set<Locale> getLocalizationProblemLocales() {
+ return I18n.getLocalizationProblemLocales(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems() {
+ return I18n.getLocalizationProblems(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems( Locale locale ) {
+ return I18n.getLocalizationProblems(CommonI18n.class, locale);
+ }
+}
Added:
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/InvalidPathExpressionException.java
===================================================================
---
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/InvalidPathExpressionException.java
(rev 0)
+++
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/InvalidPathExpressionException.java 2008-07-09
20:37:01 UTC (rev 349)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.spi.graph;
+
+/**
+ * @author Randall Hauch
+ */
+public class InvalidPathExpressionException extends RuntimeException {
+
+ /**
+ */
+ private static final long serialVersionUID = 358951801604727022L;
+
+ /**
+ *
+ */
+ public InvalidPathExpressionException() {
+ }
+
+ /**
+ * @param message
+ */
+ public InvalidPathExpressionException( String message ) {
+ super(message);
+
+ }
+
+ /**
+ * @param cause
+ */
+ public InvalidPathExpressionException( Throwable cause ) {
+ super(cause);
+
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public InvalidPathExpressionException( String message,
+ Throwable cause ) {
+ super(message, cause);
+
+ }
+
+}
Property changes on:
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/InvalidPathExpressionException.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathExpression.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathExpression.java
(rev 0)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathExpression.java 2008-07-09
20:37:01 UTC (rev 349)
@@ -0,0 +1,436 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.spi.graph;
+
+import java.io.Serializable;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.ArgCheck;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.spi.SpiI18n;
+
+/**
+ * An expression that defines an acceptable path using a regular-expression-like
language. Path expressions can be used to
+ * represent node paths or properties.
+ * <p>
+ * Here are some simple examples:
+ * <ul>
+ * <li><code>/a/b/c</code> - selects the node "c" that is a
child of node "b" that is a child of node "a".</li>
+ * <li><code>//a</code> - selects any node named "a" that is
at any location (with any ancestors).</li>
+ * <li><code></code> -</li>
+ * <li><code></code> -</li>
+ * </ul>
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@Immutable
+public class PathExpression implements Serializable {
+
+ /**
+ * Initial version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Compile the supplied expression and return the resulting path expression
instance.
+ *
+ * @param expression the expression
+ * @return the path expression; never null
+ * @throws IllegalArgumentException if the expression is null
+ * @throws InvalidPathExpressionException if the expression is blank or is not a
valid expression
+ */
+ public static final PathExpression compile( String expression ) throws
InvalidPathExpressionException {
+ return new PathExpression(expression);
+ }
+
+ private static final String SEQUENCE_PATTERN_STRING =
"\\[(\\d+(?:,\\d+)*)\\]"; // \[(\d+(,\d+)*)\]
+ private static final Pattern SEQUENCE_PATTERN =
Pattern.compile(SEQUENCE_PATTERN_STRING);
+
+ /**
+ * Regular expression used to find unusable XPath predicates within an expression.
This pattern results in unusable predicates
+ * in group 1. Note that some predicates may be valid at the end but not valid
elsewhere.
+ * <p>
+ * Currently, only index-like predicates (including sequences) are allowed
everywhere. Predicates with paths and properties
+ * are allowed only as the last predicate. Predicates with any operators are unused.
+ * </p>
+ * <p>
+ * Nested predicates are not currently allowed.
+ * </p>
+ */
+ //
\[(?:(?:\d+(?:,\d+)*)|\*)\]|(?:\[[^\]\+\-\*=\!><'"\s]+\])$|(\[[^\]]+\])
+ private static final String UNUSABLE_PREDICATE_PATTERN_STRING =
"\\[(?:(?:\\d+(?:,\\d+)*)|\\*)\\]|(?:\\[[^\\]\\+\\-\\*=\\!><'\"\\s]+\\])$|(\\[[^\\]]+\\])";
+ private static final Pattern UNUSABLE_PREDICATE_PATTERN =
Pattern.compile(UNUSABLE_PREDICATE_PATTERN_STRING);
+
+ /**
+ * Regular expression used to find all XPath predicates except index and sequence
patterns. This pattern results in the
+ * predicates to be removed in group 1.
+ */
+ // \[(?:(?:\d+(?:,\d+)*)|\*)\]|(\[[^\]]+\])
+ private static final String NON_INDEX_PREDICATE_PATTERN_STRING =
"\\[(?:(?:\\d+(?:,\\d+)*)|\\*)\\]|(\\[[^\\]]+\\])";
+ private static final Pattern NON_INDEX_PREDICATE_PATTERN =
Pattern.compile(NON_INDEX_PREDICATE_PATTERN_STRING);
+
+ private final String expression;
+ private final Pattern matchPattern;
+ private final Pattern selectPattern;
+
+ /**
+ * Create the supplied expression.
+ *
+ * @param expression the expression
+ * @throws IllegalArgumentException if the expression is null
+ * @throws InvalidPathExpressionException if the expression is blank or is not a
valid expression
+ */
+ public PathExpression( String expression ) throws InvalidPathExpressionException {
+ ArgCheck.isNotNull(expression, "path expression");
+ this.expression = expression.trim();
+ if (this.expression.length() == 0) {
+ throw new
InvalidPathExpressionException(SpiI18n.pathExpressionMayNotBeBlank.text());
+ }
+ // Build the match pattern, which determines whether a path matches the condition
...
+ String matchString = this.expression;
+ try {
+ matchString = removeUnusedPredicates(matchString);
+ matchString = replaceXPathPatterns(matchString);
+ this.matchPattern = Pattern.compile(matchString, Pattern.CASE_INSENSITIVE);
+ } catch (PatternSyntaxException e) {
+ String msg = SpiI18n.pathExpressionHasInvalidMatch.text(matchString,
this.expression);
+ throw new InvalidPathExpressionException(msg, e);
+ }
+ // Build the select pattern, which determines the path that will be selected ...
+ String selectString = this.expression;
+ try {
+ selectString = removeAllPredicatesExceptIndexes(selectString);
+ selectString = replaceXPathPatterns(selectString);
+ selectString = "(" + selectString + ").*"; // group 1
will have selected path ...
+ this.selectPattern = Pattern.compile(selectString,
Pattern.CASE_INSENSITIVE);
+ } catch (PatternSyntaxException e) {
+ String msg = SpiI18n.pathExpressionHasInvalidSelect.text(selectString,
this.expression);
+ throw new InvalidPathExpressionException(msg, e);
+ }
+ }
+
+ /**
+ * @return expression
+ */
+ public String getExpression() {
+ return expression;
+ }
+
+ /**
+ * Replace certain XPath patterns that are not used or understood.
+ *
+ * @param expression the input regular expressions string; may not be null
+ * @return the regular expression with all unused XPath patterns removed; never null
+ */
+ protected String removeUnusedPredicates( String expression ) {
+ assert expression != null;
+ java.util.regex.Matcher matcher =
UNUSABLE_PREDICATE_PATTERN.matcher(expression);
+ StringBuffer sb = new StringBuffer();
+ if (matcher.find()) {
+ do {
+ // Remove those predicates that show up in group 1 ...
+ String predicateStr = matcher.group(0);
+ String unusablePredicateStr = matcher.group(1);
+ if (unusablePredicateStr != null) {
+ predicateStr = "";
+ }
+ matcher.appendReplacement(sb, predicateStr);
+ } while (matcher.find());
+ matcher.appendTail(sb);
+ expression = sb.toString();
+ }
+ return expression;
+ }
+
+ /**
+ * Remove all XPath predicates from the supplied regular expression string.
+ *
+ * @param expression the input regular expressions string; may not be null
+ * @return the regular expression with all XPath predicates removed; never null
+ */
+ protected String removeAllPredicatesExceptIndexes( String expression ) {
+ assert expression != null;
+ java.util.regex.Matcher matcher =
NON_INDEX_PREDICATE_PATTERN.matcher(expression);
+ StringBuffer sb = new StringBuffer();
+ if (matcher.find()) {
+ do {
+ // Remove those predicates that show up in group 1 ...
+ String predicateStr = matcher.group(0);
+ String unusablePredicateStr = matcher.group(1);
+ if (unusablePredicateStr != null) {
+ predicateStr = "";
+ }
+ matcher.appendReplacement(sb, predicateStr);
+ } while (matcher.find());
+ matcher.appendTail(sb);
+ expression = sb.toString();
+ }
+ return expression;
+ }
+
+ /**
+ * Replace certain XPath patterns, including some predicates, with substrings that
are compatible with regular expressions.
+ *
+ * @param expression the input regular expressions string; may not be null
+ * @return the regular expression with XPath patterns replaced with regular
expression fragments; never null
+ */
+ protected String replaceXPathPatterns( String expression ) {
+ assert expression != null;
+ // replace 2 or more sequential '|' characters in an OR expression
+ expression = expression.replaceAll("[\\|]{2,}", "|");
+ // if there is an empty expression in an OR expression, make the whole segment
optional ...
+ // (e.g., "/a/b/(c|)/d" => "a/b(/(c))?/d"
+ expression = expression.replaceAll("/(\\([^|]+)(\\|){2,}([^)]+\\))",
"(/$1$2$3)?");
+ expression = expression.replaceAll("/\\(\\|+([^)]+)\\)",
"(?:/($1))?");
+ expression = expression.replaceAll("/\\((([^|]+)(\\|[^|]+)*)\\|+\\)",
"(?:/($1))?");
+
+ // // Allow any path (that doesn't contain an explicit counter) to contain a
counter,
+ // // done by replacing any '/' or '|' that isn't preceded by
']' or '*' or '/' or '(' with '(\[\d+\])?/'...
+ // input = input.replaceAll("(?<=[^\\]\\*/(])([/|])",
"(?:\\\\[\\\\d+\\\\])?$1");
+
+ // Does the path contain any '[]' or '[*]' or '[0]' or
'[n]' (where n is any positive integers)...
+ // '[*]/' => '(\[\d+\])?/'
+ expression = expression.replaceAll("\\[\\]",
"(?:\\\\[\\\\d+\\\\])?"); // index is optional
+ // '[]/' => '(\[\d+\])?/'
+ expression = expression.replaceAll("\\[[*]\\]",
"(?:\\\\[\\\\d+\\\\])?"); // index is optional
+ // '[0]/' => '(\[0\])?/'
+ expression = expression.replaceAll("\\[0\\]",
"(?:\\\\[0\\\\])?"); // index is optional
+ // '[n]/' => '\[n\]/'
+ expression = expression.replaceAll("\\[([1-9]\\d*)\\]",
"\\\\[$1\\\\]"); // index is required
+
+ // Change any other end predicates to not be wrapped by braces but to begin with
a slash ...
+ // ...'[x]' => ...'/x'
+ expression = expression.replaceAll("(?<!\\\\)\\[([^\\]]*)\\]$",
"/$1");
+
+ // Replace all '[n,m,o,p]' type sequences with '[(n|m|o|p)]'
+ java.util.regex.Matcher matcher = SEQUENCE_PATTERN.matcher(expression);
+ StringBuffer sb = new StringBuffer();
+ boolean result = matcher.find();
+ if (result) {
+ do {
+ String sequenceStr = matcher.group(1);
+ boolean optional = false;
+ if (sequenceStr.startsWith("0,")) {
+ sequenceStr = sequenceStr.replaceFirst("^0,",
"");
+ optional = true;
+ }
+ if (sequenceStr.endsWith(",0")) {
+ sequenceStr = sequenceStr.replaceFirst(",0$",
"");
+ optional = true;
+ }
+ if (sequenceStr.contains(",0,")) {
+ sequenceStr = sequenceStr.replaceAll(",0,",
",");
+ optional = true;
+ }
+ sequenceStr = sequenceStr.replaceAll(",", "|");
+ String replacement = "\\\\[(?:" + sequenceStr +
")\\\\]";
+ if (optional) {
+ replacement = "(?:" + replacement + ")?";
+ }
+ matcher.appendReplacement(sb, replacement);
+ result = matcher.find();
+ } while (result);
+ matcher.appendTail(sb);
+ expression = sb.toString();
+ }
+
+ // Order is important here
+ expression = expression.replaceAll("[*]([^/(\\\\])",
"[^/$1]*$1"); // '*' not followed by '/', '\\', or
'('
+ expression = expression.replaceAll("(?<!\\[\\^/\\])[*]",
"[^/]*");
+ expression = expression.replaceAll("[/]{2,}$",
"(?:/[^/]*)*"); // ending '//'
+ expression = expression.replaceAll("[/]{2,}",
"(?:/[^/]*)*/"); // other '//'
+ return expression;
+ }
+
+ /**
+ * @return the expression
+ */
+ public String getSelectExpression() {
+ return this.expression;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return this.expression.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PathExpression) {
+ PathExpression that = (PathExpression)obj;
+ if (!this.expression.equalsIgnoreCase(that.expression)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.expression;
+ }
+
+ /**
+ * @param absolutePath
+ * @return the matcher
+ */
+ public Matcher matcher( String absolutePath ) {
+ // Determine if the input path match the select expression ...
+ String originalAbsolutePath = absolutePath;
+ // if (!absolutePath.endsWith("/")) absolutePath = absolutePath +
"/";
+ // Remove all trailing '/' ...
+ absolutePath = absolutePath.replaceAll("/+$", "");
+
+ // See if the supplied absolute path matches the pattern ...
+ final java.util.regex.Matcher matcher = this.matchPattern.matcher(absolutePath);
+ if (!matcher.matches()) {
+ // No match, so return immediately ...
+ return new Matcher(matcher, originalAbsolutePath, null);
+ }
+
+ // The absolute path does match the pattern, so use the select pattern and try to
grab the selected path ...
+ final java.util.regex.Matcher selectMatcher =
this.selectPattern.matcher(absolutePath);
+ if (!selectMatcher.matches()) {
+ // Nothing can be selected, so return immediately ...
+ return new Matcher(matcher, null, null);
+ }
+ // Grab the selected path ...
+ String selectedPath = selectMatcher.group(1);
+
+ // Remove the trailing '/@property' ...
+ selectedPath = selectedPath.replaceAll("/@[^/\\[\\]]+$",
"");
+
+ return new Matcher(matcher, originalAbsolutePath, selectedPath);
+ }
+
+ @Immutable
+ public static class Matcher {
+
+ private final String inputPath;
+ private final String selectedPath;
+ private final java.util.regex.Matcher inputMatcher;
+ private final int hc;
+
+ protected Matcher( java.util.regex.Matcher inputMatcher,
+ String inputPath,
+ String selectedPath ) {
+ this.inputMatcher = inputMatcher;
+ this.inputPath = inputPath;
+ this.selectedPath = selectedPath;
+ this.hc = HashCode.compute(this.inputPath, this.selectedPath);
+ }
+
+ public boolean matches() {
+ return this.selectedPath != null;
+ }
+
+ /**
+ * @return inputPath
+ */
+ public String getInputPath() {
+ return this.inputPath;
+ }
+
+ /**
+ * @return selectPattern
+ */
+ public String getSelectedNodePath() {
+ return this.selectedPath;
+ }
+
+ public int groupCount() {
+ return this.inputMatcher.groupCount();
+ }
+
+ public String group( int groupNumber ) {
+ return this.inputMatcher.group(groupNumber);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return this.hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PathExpression.Matcher) {
+ PathExpression.Matcher that = (PathExpression.Matcher)obj;
+ if (!this.inputPath.equalsIgnoreCase(that.inputPath)) return false;
+ if (!this.selectedPath.equalsIgnoreCase(that.selectedPath)) return
false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.selectedPath;
+ }
+ }
+
+ /**
+ * Regular expression used to determine if the expression matches any single-level
wildcard.
+ */
+ // /*(?:[*.](?:\[\*?\])?/*)*
+ private static final String ANYTHING_PATTERN_STRING =
"/*(?:[*.](?:\\[\\*?\\])?/*)*";
+ private static final Pattern ANYTHING_PATTERN =
Pattern.compile(ANYTHING_PATTERN_STRING);
+
+ /**
+ * Return whether this expression matches anything and therefore is not restrictive.
These include expressions of any nodes ("
+ * <code>/</code>"), any sequence of nodes
("<code>//</code>"), the self reference
("<code>.</code>"), or wildcard ("
+ * <code>*</code>", "<code>*[]</code>" or
"<code>*[*]</code>"). Combinations of these individual expressions
are also
+ * considered to match anything.
+ *
+ * @return true if the expression matches anything, or false otherwise
+ */
+ public boolean matchesAnything() {
+ return ANYTHING_PATTERN.matcher(expression).matches();
+ }
+
+ public static PathExpression all() {
+ return ALL_PATHS_EXPRESSION;
+ }
+
+ private static final PathExpression ALL_PATHS_EXPRESSION =
PathExpression.compile("//");
+
+}
Property changes on:
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathExpression.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties
===================================================================
--- trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties 2008-07-09
15:04:15 UTC (rev 348)
+++ trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties 2008-07-09
20:37:01 UTC (rev 349)
@@ -41,3 +41,7 @@
unableToObtainValidRepositoryAfterAttempts = Unable to obtain a valid repository after
{0} attempts
validPathMayNotContainEmptySegment = The path "{0}" is not valid because it
contains an empty segment
valueJavaTypeNotCompatibleWithPropertyType = Value is instance of Java type
"{0}" and is not compatible with the "{1}" property type
+pathExpressionMayNotBeBlank = The path expression may not be blank
+pathExpressionIsInvalid = The path expression {0} is not valid
+pathExpressionHasInvalidSelect = Invalid select expression "{0}" in the path
expression "{1}"
+pathExpressionHasInvalidMatch = Invalid match expression "{0}" in the path
expression "{1}"
Added: trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/PathExpressionTest.java
===================================================================
--- trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/PathExpressionTest.java
(rev 0)
+++ trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/PathExpressionTest.java 2008-07-09
20:37:01 UTC (rev 349)
@@ -0,0 +1,583 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.spi.graph;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class PathExpressionTest {
+
+ private PathExpression expr;
+
+ @Before
+ public void beforeEach() throws Exception {
+ expr = new PathExpression(".*");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCompileNullExpression() {
+ PathExpression.compile(null);
+ }
+
+ @Test( expected = InvalidPathExpressionException.class )
+ public void shouldNotCompileZeroLengthExpression() {
+ PathExpression.compile("");
+ }
+
+ @Test( expected = InvalidPathExpressionException.class )
+ public void shouldNotCompileBlankExpression() {
+ PathExpression.compile(" ");
+ }
+
+ @Test
+ public void shouldCompileExpressionWithAnyNode() {
+ expr = PathExpression.compile("/*");
+ assertThat(expr.getSelectExpression(), is("/*"));
+ assertThat(expr.matcher("/a").matches(), is(true));
+ assertThat(expr.matcher("/a").getInputPath(), is("/a"));
+ assertThat(expr.matcher("/a").getSelectedNodePath(),
is("/a"));
+ assertThat(expr.matcher("/a").groupCount(), is(0));
+ }
+
+ @Test
+ public void shouldCompileExpressionWithAnySequenceOfNodes() {
+ expr = PathExpression.compile("//");
+ assertThat(expr.getSelectExpression(), is("//"));
+ assertThat(expr.matcher("/a").matches(), is(true));
+ assertThat(expr.matcher("/a").getInputPath(), is("/a"));
+ assertThat(expr.matcher("/a").getSelectedNodePath(),
is("/a"));
+ assertThat(expr.matcher("/a").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b").matches(), is(true));
+ assertThat(expr.matcher("/a/b").getInputPath(), is("/a/b"));
+ assertThat(expr.matcher("/a/b").getSelectedNodePath(),
is("/a/b"));
+ assertThat(expr.matcher("/a/b").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c").getInputPath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").groupCount(), is(0));
+ }
+
+ @Test
+ public void shouldCompileExpressionWithExtraWhitespace() {
+ expr = PathExpression.compile(" /a/b/c ");
+ assertThat(expr, is(notNullValue()));
+ assertThat(expr.getSelectExpression(), is("/a/b/c"));
+
+ expr = PathExpression.compile(" /a/b/c ");
+ assertThat(expr, is(notNullValue()));
+ assertThat(expr.getSelectExpression(), is("/a/b/c"));
+ }
+
+ @Test
+ public void shouldCompileExpressionWithIndexes() {
+ assertThat(PathExpression.compile("/a/b[0]/c[1]/d/e"),
is(notNullValue()));
+ assertThat(PathExpression.compile("/a/b[0]/c[1]/d/e[2]"),
is(notNullValue()));
+ }
+
+ @Test
+ public void shouldNotRemoveUsedPredicates() {
+ assertThat(expr.removeUnusedPredicates("/a/b/c"),
is("/a/b/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[0]/c"),
is("/a/b[0]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[1]/c"),
is("/a/b[1]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[10]/c"),
is("/a/b[10]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[100]/c"),
is("/a/b[100]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[1000]/c"),
is("/a/b[1000]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[]/c"),
is("/a/b[]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[*]/c"),
is("/a/b[*]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[1,2]/c"),
is("/a/b[1,2]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[1,2,3,4,5]/c"),
is("/a/b[1,2,3,4,5]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b/c[@title]"),
is("/a/b/c[@title]"));
+ assertThat(expr.removeUnusedPredicates("/a/b/c[d/e/@title]"),
is("/a/b/c[d/e/@title]"));
+
assertThat(expr.removeUnusedPredicates("/a/(b/c)[(d|e)/(f|g)/@something]"),
is("/a/(b/c)[(d|e)/(f|g)/@something]"));
+ assertThat(expr.removeUnusedPredicates("/*"), is("/*"));
+ assertThat(expr.removeUnusedPredicates("/*[]"), is("/*[]"));
+ assertThat(expr.removeUnusedPredicates("/*[3]"),
is("/*[3]"));
+ // These are legal, but aren't really useful ...
+ assertThat(expr.removeUnusedPredicates("/a/b[1][2][3]/c"),
is("/a/b[1][2][3]/c"));
+ }
+
+ @Test
+ public void shouldRemoveUnusedPredicates() {
+ assertThat(expr.removeUnusedPredicates("/a/b[-1]/c"),
is("/a/b/c"));
+
assertThat(expr.removeUnusedPredicates("/a/b[@name='wacky']/c"),
is("/a/b/c"));
+
assertThat(expr.removeUnusedPredicates("/a/b[3][@name='wacky']/c"),
is("/a/b[3]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[3][@name]/c"),
is("/a/b[3]/c"));
+ assertThat(expr.removeUnusedPredicates("/a/b[length(@name)=3]/c"),
is("/a/b/c"));
+ }
+
+ @Test
+ public void shouldRemoveAllPredicates() {
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b/c"),
is("/a/b/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[0]/c"),
is("/a/b[0]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1]/c"),
is("/a/b[1]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[10]/c"),
is("/a/b[10]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[100]/c"),
is("/a/b[100]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1000]/c"),
is("/a/b[1000]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[]/c"),
is("/a/b[]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[*]/c"),
is("/a/b[*]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b/c[@title]"),
is("/a/b/c"));
+ // These are legal, but aren't really useful ...
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[1][2][3]/c"),
is("/a/b[1][2][3]/c"));
+
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[-1]/c"),
is("/a/b/c"));
+
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[@name='wacky']/c"),
is("/a/b/c"));
+
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[3][@name='wacky']/c"),
is("/a/b[3]/c"));
+ assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[3][@name]/c"),
is("/a/b[3]/c"));
+
assertThat(expr.removeAllPredicatesExceptIndexes("/a/b[length(@name)=3]/c"),
is("/a/b/c"));
+ }
+
+ @Test
+ public void shouldReplaceAllXPathPatterns() {
+ assertThat(expr.replaceXPathPatterns("/a/b[3]/c"),
is("/a/b\\[3\\]/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[*]/c"),
is("/a/b(?:\\[\\d+\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[]/c"),
is("/a/b(?:\\[\\d+\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[0]/c"),
is("/a/b(?:\\[0\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[0,1,2,4]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[1,2,4,0]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[1,2,0,4]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[0,1,2,0,4,0]/c"),
is("/a/b(?:\\[(?:1|2|4)\\])?/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[1,2,4]/c"),
is("/a/b\\[(?:1|2|4)\\]/c"));
+ assertThat(expr.replaceXPathPatterns("/a/b[@param]"),
is("/a/b/@param"));
+ assertThat(expr.replaceXPathPatterns("/a/b[3][@param]"),
is("/a/b\\[3\\]/@param"));
+ assertThat(expr.replaceXPathPatterns("/a/b[c/d/@param]"),
is("/a/b/c/d/@param"));
+
+ assertThat(expr.replaceXPathPatterns("/a/(b|c|d)/e"),
is("/a/(b|c|d)/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b||c|d)/e"),
is("/a/(b|c|d)/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b|||c|d)/e"),
is("/a/(b|c|d)/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(|b|c|d)/e"),
is("/a(?:/(b|c|d))?/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b|c|d|)/e"),
is("/a(?:/(b|c|d))?/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b|c|d)[]/e"),
is("/a/(b|c|d)(?:\\[\\d+\\])?/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b|c[2]|d[])/e"),
is("/a/(b|c\\[2\\]|d(?:\\[\\d+\\])?)/e"));
+ assertThat(expr.replaceXPathPatterns("/a/(b|c/d|e)/f"),
is("/a/(b|c/d|e)/f"));
+
assertThat(expr.replaceXPathPatterns("/a/(b/c)[(d|e)/(f|g)/@something]"),
is("/a/(b/c)/(d|e)/(f|g)/@something"));
+
+ assertThat(expr.replaceXPathPatterns("/a/*/f"),
is("/a/[^/]*/f"));
+ assertThat(expr.replaceXPathPatterns("/a//f"),
is("/a(?:/[^/]*)*/f"));
+ assertThat(expr.replaceXPathPatterns("/a///f"),
is("/a(?:/[^/]*)*/f"));
+ assertThat(expr.replaceXPathPatterns("/a/////f"),
is("/a(?:/[^/]*)*/f"));
+
+ assertThat(expr.replaceXPathPatterns("/*"), is("/[^/]*"));
+ assertThat(expr.replaceXPathPatterns("/*[]"),
is("/[^/]*(?:\\[\\d+\\])?"));
+ assertThat(expr.replaceXPathPatterns("/*[3]"),
is("/[^/]*\\[3\\]"));
+ }
+
+ @Test
+ public void shouldDetermineIfPatternMatchesAnything() {
+ assertThat(PathExpression.compile("/.").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("//").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("///").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("///").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("/*").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("*").matchesAnything(), is(true));
+ assertThat(PathExpression.compile("*[*]").matchesAnything(),
is(true));
+ assertThat(PathExpression.compile("*[]").matchesAnything(), is(true));
+
+ assertThat(PathExpression.compile("/a").matchesAnything(), is(false));
+ assertThat(PathExpression.compile("/*[3]").matchesAnything(),
is(false));
+ assertThat(PathExpression.compile("/a/b/c").matchesAnything(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithoutRegardToCase() {
+ expr = PathExpression.compile("/a/b/c/d/e[@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something").getInputPath(),
is("/a/b/c/d/E/@something"));
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something").getSelectedNodePath(),
is("/a/b/c/d/E"));
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something").groupCount(), is(0));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithExactFullPath() {
+ expr = PathExpression.compile("/a/b/c/d/e[@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something2").matches(),
is(false));
+ assertThat(expr.matcher("/a/b/c/d/ex/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").matches(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithExactFullPathAndExtraPathInsideMatch() {
+ expr = PathExpression.compile("/a/b/c[d/e/@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/(a)something2").matches(),
is(false));
+ assertThat(expr.matcher("/a/b/c/d/ex/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").matches(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithWildcardSelection() {
+ expr = PathExpression.compile("/a/*/c[d/e/@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getInputPath(),
is("/a/b[2]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[2]/c"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").getInputPath(),
is("/a/rt/c/d/e/@something"));
+
assertThat(expr.matcher("/a/rt/c/d/e/(a)something").getSelectedNodePath(),
is("/a/rt/c"));
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/ac/d/e/(a)something").matches(), is(false));
+ assertThat(expr.matcher("/a/d/e/(a)something").matches(), is(false));
+ assertThat(expr.matcher("/a/b/b2/b3/d/e/(a)something").matches(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithFilenameLikeWildcardSelection() {
+ expr = PathExpression.compile("/a/*.txt[@something]");
+ assertThat(expr.matcher("/a/b.txt/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/(a)something").getInputPath(),
is("/a/b.txt/@something"));
+ assertThat(expr.matcher("/a/b.txt/(a)something").getSelectedNodePath(),
is("/a/b.txt"));
+ assertThat(expr.matcher("/a/b.txt/(a)something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/(a)something").matches(), is(false));
+
+ expr = PathExpression.compile("/a/*.txt/c[@something]");
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").getInputPath(),
is("/a/b.txt/c/@something"));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").getSelectedNodePath(),
is("/a/b.txt/c"));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/c/(a)something").matches(), is(false));
+
+ expr = PathExpression.compile("//*.txt[*]/c[@something]");
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").getInputPath(),
is("/a/b.txt/c/@something"));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").getSelectedNodePath(),
is("/a/b.txt/c"));
+ assertThat(expr.matcher("/a/b.txt/c/(a)something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/c/(a)something").matches(), is(false));
+
+ assertThat(expr.matcher("/z/a/b.txt/c/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/z/a/b.txt/c/(a)something").getInputPath(),
is("/z/a/b.txt/c/@something"));
+
assertThat(expr.matcher("/z/a/b.txt/c/(a)something").getSelectedNodePath(),
is("/z/a/b.txt/c"));
+ assertThat(expr.matcher("/z/a/b.txt/c/(a)something").groupCount(),
is(0));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithSegmentWildcardSelection() {
+ expr = PathExpression.compile("/a//c[d/e/@something]");
+ assertThat(expr.matcher("/a/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/c/d/e/(a)something").getInputPath(),
is("/a/c/d/e/@something"));
+ assertThat(expr.matcher("/a/c/d/e/(a)something").getSelectedNodePath(),
is("/a/c"));
+ assertThat(expr.matcher("/a/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getInputPath(),
is("/a/b[2]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[2]/c"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").getInputPath(),
is("/a/rt/c/d/e/@something"));
+
assertThat(expr.matcher("/a/rt/c/d/e/(a)something").getSelectedNodePath(),
is("/a/rt/c"));
+ assertThat(expr.matcher("/a/rt/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/(a)something").getInputPath(),
is("/a/r/s/t/c/d/e/@something"));
+
assertThat(expr.matcher("/a/r/s/t/c/d/e/(a)something").getSelectedNodePath(),
is("/a/r/s/t/c"));
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/(a)something").groupCount(),
is(0));
+
+
assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/(a)something").matches(),
is(true));
+
assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/(a)something").getInputPath(),
is("/a/r[1]/s[2]/t[33]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/r[1]/s[2]/t[33]/c"));
+
assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a[3]/c/d/e/(a)something").matches(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithSegmentWildcardAtEnd() {
+ expr = PathExpression.compile("/a/b/c//");
+ assertThat(expr.matcher("/a/b/c").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c").getInputPath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d").getInputPath(),
is("/a/b/c/d"));
+ assertThat(expr.matcher("/a/b/c/d").getSelectedNodePath(),
is("/a/b/c/d"));
+ assertThat(expr.matcher("/a/b/c/d").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/a").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/a").getInputPath(),
is("/a/b/c/a"));
+ assertThat(expr.matcher("/a/b/c/a").getSelectedNodePath(),
is("/a/b/c/a"));
+ assertThat(expr.matcher("/a/b/c/a").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/e/f/g/h").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/f/g/h").getInputPath(),
is("/a/b/c/d/e/f/g/h"));
+ assertThat(expr.matcher("/a/b/c/d/e/f/g/h").getSelectedNodePath(),
is("/a/b/c/d/e/f/g/h"));
+ assertThat(expr.matcher("/a/b/c/d/e/f/g/h").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b").matches(), is(false));
+ assertThat(expr.matcher("/a/b/d").matches(), is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithExtraLargeSegmentWildcardAtEnd() {
+ expr = PathExpression.compile("/a/b/c////");
+ assertThat(expr.matcher("/a/b/c").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c").getInputPath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c").groupCount(), is(0));
+
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithIndexesInSelectionPaths() {
+ expr = PathExpression.compile("/a/b[2,3,4,5]/c/d/e[@something]");
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getInputPath(),
is("/a/b[2]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getInputPath(),
is("/a/b[3]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getInputPath(),
is("/a/b[4]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getInputPath(),
is("/a/b[5]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a[1]/b/c/d/e/(a)something").matches(),
is(false));
+
+ expr = PathExpression.compile("/a/b[0,2,3,4,5]/c/d/e[@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getInputPath(),
is("/a/b[2]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getInputPath(),
is("/a/b[3]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getInputPath(),
is("/a/b[4]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getInputPath(),
is("/a/b[5]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[0]/c/d/e/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").matches(),
is(false));
+ assertThat(expr.matcher("/a[1]/b/c/d/e/(a)something").matches(),
is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithAnyIndexesInSelectionPaths() {
+ expr = PathExpression.compile("/a/b[*]/c[]/d/e[@something]");
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getInputPath(),
is("/a/b[2]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getInputPath(),
is("/a/b[3]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getInputPath(),
is("/a/b[4]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getInputPath(),
is("/a/b[5]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").getInputPath(),
is("/a/b[1]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[1]/c/d/e"));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[6]/c/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[6]/c/d/e/(a)something").getInputPath(),
is("/a/b[6]/c/d/e/@something"));
+
assertThat(expr.matcher("/a/b[6]/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b[6]/c/d/e"));
+ assertThat(expr.matcher("/a/b[6]/c/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/(a)something").matches(),
is(true));
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/(a)something").getInputPath(),
is("/a/b[6]/c[1]/d/e/@something"));
+
assertThat(expr.matcher("/a/b[6]/c[1]/d/e/(a)something").getSelectedNodePath(),
is("/a/b[6]/c[1]/d/e"));
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/(a)something").groupCount(),
is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(0));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithRepositoryInSelectionPath() {
+ expr = PathExpression.compile("reposA:/a/b/c[d/e/@something]");
+ assertThat(expr.matcher("reposA:/a/b/c/d/e/@something").matches(),
is(true));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithNamedGroups() {
+ expr = PathExpression.compile("/a(//c)[d/e/@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getInputPath(),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").groupCount(), is(1));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").group(0),
is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/(a)something").group(1),
is("/b/c"));
+
+ expr = PathExpression.compile("/a(/(b|c|d|)/e)[f/g/@something]");
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").getInputPath(),
is("/a/b/e/f/g/@something"));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").getSelectedNodePath(),
is("/a/b/e"));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").group(0),
is("/a/b/e/f/g/@something"));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").group(1),
is("/b/e"));
+ assertThat(expr.matcher("/a/b/e/f/g/(a)something").group(2),
is("b"));
+
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").getInputPath(),
is("/a/c/e/f/g/@something"));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").getSelectedNodePath(),
is("/a/c/e"));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").group(0),
is("/a/c/e/f/g/@something"));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").group(1),
is("/c/e"));
+ assertThat(expr.matcher("/a/c/e/f/g/(a)something").group(2),
is("c"));
+
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").getInputPath(),
is("/a/d/e/f/g/@something"));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").getSelectedNodePath(),
is("/a/d/e"));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").group(0),
is("/a/d/e/f/g/@something"));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").group(1),
is("/d/e"));
+ assertThat(expr.matcher("/a/d/e/f/g/(a)something").group(2),
is("d"));
+
+ assertThat(expr.matcher("/a/e/f/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").getInputPath(),
is("/a/e/f/g/@something"));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").getSelectedNodePath(),
is("/a/e"));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").group(0),
is("/a/e/f/g/@something"));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").group(1),
is("/e"));
+ assertThat(expr.matcher("/a/e/f/g/(a)something").group(2),
is(nullValue()));
+
+ assertThat(expr.matcher("/a/t/e/f/g/(a)something").matches(),
is(false));
+
+ expr = PathExpression.compile("/a/(b/c)[(d|e)/(f|g)/@something]");
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").getInputPath(),
is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(0),
is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(1),
is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(2),
is("d"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(3),
is("f"));
+
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").getInputPath(),
is("/a/b/c/e/f/@something"));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").group(0),
is("/a/b/c/e/f/@something"));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").group(1),
is("b/c"));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").group(2),
is("e"));
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").group(3),
is("f"));
+
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").getInputPath(),
is("/a/b/c/d/g/@something"));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").group(0),
is("/a/b/c/d/g/@something"));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").group(1),
is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").group(2),
is("d"));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").group(3),
is("g"));
+
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").getInputPath(),
is("/a/b/c/e/g/@something"));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").getSelectedNodePath(),
is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").group(0),
is("/a/b/c/e/g/@something"));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").group(1),
is("b/c"));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").group(2),
is("e"));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").group(3),
is("g"));
+
+ expr = PathExpression.compile("/a/(b/c)/(d|e)/(f|g)/@something");
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").getInputPath(),
is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").getSelectedNodePath(),
is("/a/b/c/d/f"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(0),
is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(1),
is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(2),
is("d"));
+ assertThat(expr.matcher("/a/b/c/d/f/(a)something").group(3),
is("f"));
+
+ assertThat(expr.matcher("/a/b/c/e/f/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/g/(a)something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/g/(a)something").matches(), is(true));
+ }
+
+ @Test
+ public void shouldMatchExpressionWithFilenamePatternAndChildProperty() {
+ expr =
PathExpression.compile("//(*.(jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd))[*]/jcr:content[@jcr:data]");
+
assertThat(expr.matcher("/a/b/caution.png/jcr:content/@jcr:data").matches(),
is(true));
+ }
+}
Property changes on:
trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/PathExpressionTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain