DNA SVN: r356 - trunk/dna-integration-tests.
by dna-commits@lists.jboss.org
Author: jverhaeg(a)redhat.com
Date: 2008-07-14 16:34:33 -0400 (Mon, 14 Jul 2008)
New Revision: 356
Modified:
trunk/dna-integration-tests/pom.xml
Log:
Applied test scopes appropriately
Modified: trunk/dna-integration-tests/pom.xml
===================================================================
--- trunk/dna-integration-tests/pom.xml 2008-07-14 20:33:56 UTC (rev 355)
+++ trunk/dna-integration-tests/pom.xml 2008-07-14 20:34:33 UTC (rev 356)
@@ -49,7 +49,6 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
- <scope>test</scope>
</dependency>
<!--
Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing)
@@ -57,6 +56,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -72,6 +72,7 @@
<dependency>
<groupId>javax.jcr</groupId>
<artifactId>jcr</artifactId>
+ <scope>test</scope>
</dependency>
<!--
Apache Jackrabbit (JCR Implementation)
15 years, 9 months
DNA SVN: r355 - trunk/sequencers/dna-sequencer-cnd.
by dna-commits@lists.jboss.org
Author: jverhaeg(a)redhat.com
Date: 2008-07-14 16:33:56 -0400 (Mon, 14 Jul 2008)
New Revision: 355
Modified:
trunk/sequencers/dna-sequencer-cnd/pom.xml
Log:
Added relative path to see parent pom past parent sequencers folder
Modified: trunk/sequencers/dna-sequencer-cnd/pom.xml
===================================================================
--- trunk/sequencers/dna-sequencer-cnd/pom.xml 2008-07-14 20:33:23 UTC (rev 354)
+++ trunk/sequencers/dna-sequencer-cnd/pom.xml 2008-07-14 20:33:56 UTC (rev 355)
@@ -4,6 +4,7 @@
<artifactId>dna</artifactId>
<groupId>org.jboss.dna</groupId>
<version>0.2-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jboss.dna</groupId>
15 years, 9 months
DNA SVN: r354 - in trunk/sequencers: dna-sequencer-msoffice and 1 other directory.
by dna-commits@lists.jboss.org
Author: jverhaeg(a)redhat.com
Date: 2008-07-14 16:33:23 -0400 (Mon, 14 Jul 2008)
New Revision: 354
Modified:
trunk/sequencers/dna-sequencer-mp3/pom.xml
trunk/sequencers/dna-sequencer-msoffice/pom.xml
Log:
Changed dependency on JUnit to use dependency defined in parent pom
Modified: trunk/sequencers/dna-sequencer-mp3/pom.xml
===================================================================
--- trunk/sequencers/dna-sequencer-mp3/pom.xml 2008-07-14 20:24:57 UTC (rev 353)
+++ trunk/sequencers/dna-sequencer-mp3/pom.xml 2008-07-14 20:33:23 UTC (rev 354)
@@ -28,8 +28,7 @@
</dependency>
<dependency>
<groupId>junit</groupId>
- <artifactId>junit-dep</artifactId>
- <version>4.4</version>
+ <artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
Modified: trunk/sequencers/dna-sequencer-msoffice/pom.xml
===================================================================
--- trunk/sequencers/dna-sequencer-msoffice/pom.xml 2008-07-14 20:24:57 UTC (rev 353)
+++ trunk/sequencers/dna-sequencer-msoffice/pom.xml 2008-07-14 20:33:23 UTC (rev 354)
@@ -40,8 +40,7 @@
</dependency>
<dependency>
<groupId>junit</groupId>
- <artifactId>junit-dep</artifactId>
- <version>4.4</version>
+ <artifactId>junit</artifactId>
</dependency>
<!--
Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing)
15 years, 9 months
DNA SVN: r352 - trunk.
by dna-commits@lists.jboss.org
Author: maeste
Date: 2008-07-12 10:39:09 -0400 (Sat, 12 Jul 2008)
New Revision: 352
Modified:
trunk/pom.xml
Log:
[DNA-177]
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2008-07-11 20:33:53 UTC (rev 351)
+++ trunk/pom.xml 2008-07-12 14:39:09 UTC (rev 352)
@@ -212,6 +212,28 @@
<version>2.2</version>
<configuration>
<aggregate>true</aggregate>
+ <doclet>net.gleamynode.apiviz.APIviz</doclet>
+ <docletArtifact>
+ <groupId>net.gleamynode.apiviz</groupId>
+ <artifactId>apiviz</artifactId>
+ <version>1.1.1</version>
+ </docletArtifact>
+ <additionalparam>
+ -charset UTF-8
+ -docencoding UTF-8
+ -version
+ -author
+ -breakiterator
+ -linksource
+ -sourcetab 4
+ -windowtitle "${project.name} ${project.version} API Reference"
+ -doctitle "${project.name} ${project.version} API Reference"
+ -bottom "Copyright © ${project.inceptionYear}-Present JBoss a division of Red Hat. All Rights Reserved."
+ -link http://java.sun.com/javase/6/docs/api/
+ </additionalparam>
+ <encoding>UTF-8</encoding>
+
+
</configuration>
</plugin>
</plugins>
@@ -455,6 +477,8 @@
<version>10.2.1.6</version>
<scope>test</scope>
</dependency>
+
+
</dependencies>
</dependencyManagement>
<reporting>
@@ -467,6 +491,19 @@
</reporting>
<repositories>
+ <repository>
+ <id>apiviz.release</id>
+ <name>APIviz releases</name>
+ <url>http://apiviz.googlecode.com/svn/site/repo/mvn/release</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+
+
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/maven2/</url>
15 years, 10 months
DNA SVN: r351 - in trunk: dna-common/src/main/java/org/jboss/dna/common/util and 3 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-07-11 16:33:53 -0400 (Fri, 11 Jul 2008)
New Revision: 351
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
trunk/dna-common/src/main/java/org/jboss/dna/common/util/ArgCheck.java
trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
trunk/dna-common/src/test/java/org/jboss/dna/common/util/ArgCheckTest.java
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicEmptyProperty.java
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicMultiValueProperty.java
trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicSingleValueProperty.java
Log:
DNA-163 - Most, if not all, of the assertions in BasicMultiValueProperty are incorrect.
http://jira.jboss.com/jira/browse/DNA-163
The assertions were indeed correct, but they were misplaced and should instead be in the constructors. The BasicEmptyProperty class should be used for properties that have no values; the BasicSingleValueProperty should be used for properties that have just a single value (which may be null); and the BasicMultiValueProperty should be used for properties that contain 2+ values. This is done to minimize the memory footprint: if a single Property implementation were used, it would have to support multiple values, but in reality most properties will be properties with one a single value and therefore the extra collection is simply wasted memory. By having different implementations for the different situations, we're able to hold onto the values with the most efficiency. Also, since the objects are intended to be immutable, there is no need to support changing the values. This description was added to the JavaDoc.
This relies upon a new ArgCheck method, which was added with tests (along with several other similar methods).
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -58,6 +58,10 @@
// Core-related fields
public static I18n argumentMayNotBeLessThan;
public static I18n argumentMayNotBeGreaterThan;
+ public static I18n argumentMustBeGreaterThan;
+ public static I18n argumentMustBeLessThan;
+ public static I18n argumentMustBeGreaterThanOrEqualTo;
+ public static I18n argumentMustBeLessThanOrEqualTo;
public static I18n argumentMayNotBeNegative;
public static I18n argumentMayNotBePositive;
public static I18n argumentMustBeNegative;
@@ -76,6 +80,8 @@
public static I18n argumentDidNotContainObject;
public static I18n argumentDidNotContainKey;
public static I18n argumentMayNotContainNullValue;
+ public static I18n argumentMustBeOfMinimumSize;
+ public static I18n argumentMustBeOfMaximumSize;
public static I18n componentClassnameNotValid;
public static I18n componentNotConfigured;
public static I18n dateParsingFailure;
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/ArgCheck.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/ArgCheck.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/ArgCheck.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -40,7 +40,7 @@
* @param argument The argument
* @param notLessThanValue the value that is to be used to check the value
* @param name The name of the argument
- * @throws IllegalArgumentException If argument is negative (<0)
+ * @throws IllegalArgumentException If argument greater than or equal to the supplied vlaue
*/
public static void isNotLessThan( int argument,
int notLessThanValue,
@@ -56,17 +56,85 @@
* @param argument The argument
* @param notGreaterThanValue the value that is to be used to check the value
* @param name The name of the argument
- * @throws IllegalArgumentException If argument is negative (<0)
+ * @throws IllegalArgumentException If argument is less than or equal to the supplied value
*/
public static void isNotGreaterThan( int argument,
int notGreaterThanValue,
String name ) {
- if (argument < notGreaterThanValue) {
+ if (argument > notGreaterThanValue) {
throw new IllegalArgumentException(CommonI18n.argumentMayNotBeGreaterThan.text(name, argument, notGreaterThanValue));
}
}
/**
+ * Check that the argument is greater than the supplied value
+ *
+ * @param argument The argument
+ * @param greaterThanValue the value that is to be used to check the value
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If argument is not greater than the supplied value
+ */
+ public static void isGreaterThan( int argument,
+ int greaterThanValue,
+ String name ) {
+ if (argument <= greaterThanValue) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeGreaterThan.text(name, argument, greaterThanValue));
+ }
+ }
+
+ /**
+ * Check that the argument is less than the supplied value
+ *
+ * @param argument The argument
+ * @param lessThanValue the value that is to be used to check the value
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If argument is not less than the supplied value
+ */
+ public static void isLessThan( int argument,
+ int lessThanValue,
+ String name ) {
+ if (argument >= lessThanValue) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeLessThan.text(name, argument, lessThanValue));
+ }
+ }
+
+ /**
+ * Check that the argument is greater than or equal to the supplied value
+ *
+ * @param argument The argument
+ * @param greaterThanOrEqualToValue the value that is to be used to check the value
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If argument is not greater than or equal to the supplied value
+ */
+ public static void isGreaterThanOrEqualTo( int argument,
+ int greaterThanOrEqualToValue,
+ String name ) {
+ if (argument < greaterThanOrEqualToValue) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeGreaterThanOrEqualTo.text(name,
+ argument,
+ greaterThanOrEqualToValue));
+ }
+ }
+
+ /**
+ * Check that the argument is less than or equal to the supplied value
+ *
+ * @param argument The argument
+ * @param lessThanOrEqualToValue the value that is to be used to check the value
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If argument is not less than or equal to the supplied value
+ */
+ public static void isLessThanOrEqualTo( int argument,
+ int lessThanOrEqualToValue,
+ String name ) {
+ if (argument > lessThanOrEqualToValue) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeLessThanOrEqualTo.text(name,
+ argument,
+ lessThanOrEqualToValue));
+ }
+ }
+
+ /**
* Check that the argument is non-negative (>=0).
*
* @param argument The argument
@@ -374,8 +442,8 @@
* @param argumentName The name that will be used within the exception message for the argument should an exception be thrown
* @param object The object to assert as the same as <code>argument</code>.
* @param objectName The name that will be used within the exception message for <code>object</code> should an exception be
- * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code>
- * will be used.
+ * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code> will
+ * be used.
* @throws IllegalArgumentException If the specified objects are not the same.
*/
public static <T> void isSame( final T argument,
@@ -396,8 +464,8 @@
* @param argumentName The name that will be used within the exception message for the argument should an exception be thrown
* @param object The object to assert as not the same as <code>argument</code>.
* @param objectName The name that will be used within the exception message for <code>object</code> should an exception be
- * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code>
- * will be used.
+ * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code> will
+ * be used.
* @throws IllegalArgumentException If the specified objects are the same.
*/
public static <T> void isNotSame( final T argument,
@@ -418,8 +486,8 @@
* @param argumentName The name that will be used within the exception message for the argument should an exception be thrown
* @param object The object to assert as equal to <code>argument</code>.
* @param objectName The name that will be used within the exception message for <code>object</code> should an exception be
- * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code>
- * will be used.
+ * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code> will
+ * be used.
* @throws IllegalArgumentException If the specified objects are not equal.
*/
public static <T> void isEquals( final T argument,
@@ -440,8 +508,8 @@
* @param argumentName The name that will be used within the exception message for the argument should an exception be thrown
* @param object The object to assert as equal to <code>argument</code>.
* @param objectName The name that will be used within the exception message for <code>object</code> should an exception be
- * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code>
- * will be used.
+ * thrown; if <code>null</code> and <code>object</code> is not <code>null</code>, <code>object.toString()</code> will
+ * be used.
* @throws IllegalArgumentException If the specified objects are equals.
*/
public static <T> void isNotEquals( final T argument,
@@ -594,6 +662,126 @@
}
}
+ /**
+ * Check that the collection contains at least the supplied number of elements
+ *
+ * @param argument Collection
+ * @param minimumSize the minimum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If collection has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtLeast( Collection<?> argument,
+ int minimumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.size() < minimumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Collection.class.getSimpleName(),
+ argument.size(),
+ minimumSize));
+ }
+ }
+
+ /**
+ * Check that the collection contains no more than the supplied number of elements
+ *
+ * @param argument Collection
+ * @param maximumSize the maximum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If collection has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtMost( Collection<?> argument,
+ int maximumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.size() > maximumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Collection.class.getSimpleName(),
+ argument.size(),
+ maximumSize));
+ }
+ }
+
+ /**
+ * Check that the map contains at least the supplied number of entries
+ *
+ * @param argument the map
+ * @param minimumSize the minimum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If the map has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtLeast( Map<?, ?> argument,
+ int minimumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.size() < minimumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Map.class.getSimpleName(),
+ argument.size(),
+ minimumSize));
+ }
+ }
+
+ /**
+ * Check that the map contains no more than the supplied number of entries
+ *
+ * @param argument the map
+ * @param maximumSize the maximum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If the map has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtMost( Map<?, ?> argument,
+ int maximumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.size() > maximumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Map.class.getSimpleName(),
+ argument.size(),
+ maximumSize));
+ }
+ }
+
+ /**
+ * Check that the array contains at least the supplied number of elements
+ *
+ * @param argument the array
+ * @param minimumSize the minimum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If the array has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtLeast( Object[] argument,
+ int minimumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.length < minimumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Object[].class.getSimpleName(),
+ argument.length,
+ minimumSize));
+ }
+ }
+
+ /**
+ * Check that the array contains no more than the supplied number of elements
+ *
+ * @param argument the array
+ * @param maximumSize the maximum size
+ * @param name The name of the argument
+ * @throws IllegalArgumentException If the array has a size smaller than the supplied value
+ */
+ public static void hasSizeOfAtMost( Object[] argument,
+ int maximumSize,
+ String name ) {
+ isNotNull(argument, name);
+ if (argument.length > maximumSize) {
+ throw new IllegalArgumentException(CommonI18n.argumentMustBeOfMinimumSize.text(name,
+ Object[].class.getSimpleName(),
+ argument.length,
+ maximumSize));
+ }
+ }
+
private ArgCheck() {
// prevent construction
}
Modified: trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
===================================================================
--- trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties 2008-07-11 20:33:53 UTC (rev 351)
@@ -39,6 +39,10 @@
# Core-related fields
argumentMayNotBeGreaterThan = The {0} argument value, {1}, may not be greater than {2}
argumentMayNotBeLessThan = The {0} argument value, {1}, may not be less than {2}
+argumentMustBeGreaterThan = The {0} argument value, {1}, must be greater than {2}
+argumentMustBeLessThan = The {0} argument value, {1}, must be less than {2}
+argumentMustBeGreaterThanOrEqualTo = The {0} argument value, {1}, must be greater than or equal to {2}
+argumentMustBeLessThanOrEqualTo = The {0} argument value, {1}, must be less than or equal to {2}
argumentMayNotBeNegative = The {0} argument value, {1}, may not be negative
argumentMayNotBePositive = The {0} argument value, {1}, may not be positive
argumentMustBeNegative = The {0} argument value, {1}, must be negative
@@ -57,6 +61,8 @@
argumentDidNotContainObject = "The {0} argument did not contain the expected object {1}
argumentDidNotContainKey = "The {0} argument did not contain the expected key {1}
argumentMayNotContainNullValue = The {0} argument may not contain a null value (first null found at position {1})
+argumentMustBeOfMinimumSize = The {0} argument is a {1} with {2} elements but must have at least {3}
+argumentMustBeOfMaximumSize = The {0} argument is a {1} with {2} elements but must have no more than {3}
componentClassnameNotValid = The class name {0} specified for {1} is not a valid Java class name
componentNotConfigured = The component {0} was not configured and will not be used
dateParsingFailure = Unable to parse the date "{0}" using the standard ISO 8601 format
Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/util/ArgCheckTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/util/ArgCheckTest.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/util/ArgCheckTest.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -414,4 +414,185 @@
public void containsNoNullsArrayShouldThrowExceptionIfGivenArrayWithNullEntry() {
ArgCheck.containsNoNulls(new Object[] {"some", null, "thing", null}, "test");
}
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isNotLessThanShouldThrowExceptionIfValueIsLessThanSuppliedValue() {
+ ArgCheck.isNotLessThan(0, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isNotGreaterThanShouldThrowExceptionIfValueIsGreaterThanSuppliedValue() {
+ ArgCheck.isNotGreaterThan(1, 0, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isNotLessThanShouldThrowExceptionIfValueIsEqualToSuppliedValue() {
+ ArgCheck.isNotLessThan(1, 2, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isNotGreaterThanShouldThrowExceptionIfValueIsEqualToSuppliedValue() {
+ ArgCheck.isNotGreaterThan(2, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isLessThanShouldThrowExceptionIfValueIsGreaterThanSuppliedValue() {
+ ArgCheck.isLessThan(1, 0, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isGreaterThanShouldThrowExceptionIfValueIsLessThanSuppliedValue() {
+ ArgCheck.isGreaterThan(0, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isLessThanShouldThrowExceptionIfValueIsEqualToSuppliedValue() {
+ ArgCheck.isLessThan(1, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isGreaterThanShouldThrowExceptionIfValueIsEqualToSuppliedValue() {
+ ArgCheck.isGreaterThan(1, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isLessThanOrEqualToShouldThrowExceptionIfValueIsNotLessThanOrEqualToSuppliedValue() {
+ ArgCheck.isLessThanOrEqualTo(1, 0, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void isGreaterThanOrEqualToShouldThrowExceptionIfValueIsNotGreaterThanOrEqualToSuppliedValue() {
+ ArgCheck.isGreaterThanOrEqualTo(0, 1, "value");
+ }
+
+ @Test
+ public void isNotLessThanShouldNotThrowExceptionIfValueIsNotLessThanSuppliedValue() {
+ ArgCheck.isNotLessThan(1, 1, "value");
+ ArgCheck.isNotLessThan(2, 1, "value");
+ ArgCheck.isNotLessThan(100, 1, "value");
+ }
+
+ @Test
+ public void isNotGreaterThanShouldNotThrowExceptionIfValueIsNotGreaterThanSuppliedValue() {
+ ArgCheck.isNotGreaterThan(1, 1, "value");
+ ArgCheck.isNotGreaterThan(1, 2, "value");
+ ArgCheck.isNotGreaterThan(1, 100, "value");
+ }
+
+ @Test
+ public void isLessThanShouldNotThrowExceptionIfValueIsLessThanSuppliedValue() {
+ ArgCheck.isLessThanOrEqualTo(1, 2, "value");
+ ArgCheck.isLessThanOrEqualTo(1, 100, "value");
+ }
+
+ @Test
+ public void isGreaterThanShouldNotThrowExceptionIfValueIsGreaterThanSuppliedValue() {
+ ArgCheck.isGreaterThan(2, 1, "value");
+ ArgCheck.isGreaterThan(100, 1, "value");
+ }
+
+ @Test
+ public void isLessThanOrEqualToShouldNotThrowExceptionIfValueIsLessThanOrEqualToSuppliedValue() {
+ ArgCheck.isLessThanOrEqualTo(1, 1, "value");
+ ArgCheck.isLessThanOrEqualTo(1, 2, "value");
+ ArgCheck.isLessThanOrEqualTo(1, 100, "value");
+ }
+
+ @Test
+ public void isGreaterThanOrEqualToShouldNotThrowExceptionIfValueIsGreaterThanOrEqualToSuppliedValue() {
+ ArgCheck.isGreaterThanOrEqualTo(1, 1, "value");
+ ArgCheck.isGreaterThanOrEqualTo(2, 1, "value");
+ ArgCheck.isGreaterThanOrEqualTo(100, 1, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtLeastShouldThrowExceptionIfCollectionSizeIsSmallerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonList(" "), 2, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtMostShouldThrowExceptionIfCollectionSizeIsLargerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonList(" "), 0, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfCollectionSizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonList(" "), 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfCollectionSizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonList(" "), 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfCollectionSizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonList(" "), 0, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfCollectionSizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonList(" "), 2, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtLeastShouldThrowExceptionIfMapSizeIsSmallerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonMap("key", "value"), 2, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtMostShouldThrowExceptionIfMapSizeIsLargerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonMap("key", "value"), 0, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfMapSizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonMap("key", "value"), 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfMapSizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonMap("key", "value"), 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfMapSizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(Collections.singletonMap("key", "value"), 0, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfMapSizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(Collections.singletonMap("key", "value"), 2, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtLeastShouldThrowExceptionIfArraySizeIsSmallerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(new Object[] {"key", "value"}, 3, "value");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void hasSizeOfAtMostShouldThrowExceptionIfArraySizeIsLargerThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(new Object[] {"key", "value"}, 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfArraySizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(new Object[] {"key", "value"}, 2, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfArraySizeIsEqualToSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(new Object[] {"key", "value"}, 2, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtLeastShouldNotThrowExceptionIfArraySizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtLeast(new Object[] {"key", "value"}, 1, "value");
+ }
+
+ @Test
+ public void hasSizeOfAtMostShouldNotThrowExceptionIfArraySizeIsGreaterThanSuppliedValue() {
+ ArgCheck.hasSizeOfAtMost(new Object[] {"key", "value"}, 3, "value");
+ }
+
}
Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicEmptyProperty.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicEmptyProperty.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicEmptyProperty.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -27,13 +27,20 @@
import org.jboss.dna.spi.graph.Name;
/**
+ * An immutable version of a property that has no values. This is done for efficiency of the in-memory representation, since many
+ * properties will have just a single value, while others will have multiple values.
+ *
* @author Randall Hauch
*/
@Immutable
public class BasicEmptyProperty extends BasicProperty {
+ private static final Iterator<Object> SHARED_ITERATOR = new EmptyIterator<Object>();
+
/**
- * @param name
+ * Create a property with no values.
+ *
+ * @param name the property name
*/
public BasicEmptyProperty( Name name ) {
super(name);
@@ -71,10 +78,10 @@
* {@inheritDoc}
*/
public Iterator<Object> iterator() {
- return new EmptyIterator<Object>();
+ return SHARED_ITERATOR;
}
- protected class EmptyIterator<T> implements Iterator<T> {
+ protected static class EmptyIterator<T> implements Iterator<T> {
protected EmptyIterator() {
}
Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicMultiValueProperty.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicMultiValueProperty.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicMultiValueProperty.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -21,12 +21,17 @@
*/
package org.jboss.dna.spi.graph.impl;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.ArgCheck;
import org.jboss.dna.spi.graph.Name;
/**
+ * An immutable version of a property that has 2 or more values. This is done for efficiency of the in-memory representation,
+ * since many properties will have just a single value, while others will have multiple values.
+ *
* @author Randall Hauch
*/
@Immutable
@@ -35,20 +40,40 @@
private final List<Object> values;
/**
- * @param name
- * @param values
+ * Create a property with 2 or more values. Note that the supplied list may be modifiable, as this object does not expose any
+ * means for modifying the contents.
+ *
+ * @param name the property name
+ * @param values the property values
+ * @throws IllegalArgumentException if the values is null or does not have at least 2 values
*/
public BasicMultiValueProperty( Name name,
List<Object> values ) {
super(name);
+ ArgCheck.isNotNull(values, "values");
+ ArgCheck.hasSizeOfAtLeast(values, 2, "values");
this.values = values;
}
/**
+ * Create a property with 2 or more values.
+ *
+ * @param name the property name
+ * @param values the property values
+ * @throws IllegalArgumentException if the values is null or does not have at least 2 values
+ */
+ public BasicMultiValueProperty( Name name,
+ Object... values ) {
+ super(name);
+ ArgCheck.isNotNull(values, "values");
+ ArgCheck.hasSizeOfAtLeast(values, 2, "values");
+ this.values = Arrays.asList(values);
+ }
+
+ /**
* {@inheritDoc}
*/
public boolean isEmpty() {
- assert values.isEmpty() == false;
return false;
}
@@ -56,7 +81,6 @@
* {@inheritDoc}
*/
public boolean isMultiple() {
- assert values.size() > 1;
return true;
}
@@ -64,7 +88,6 @@
* {@inheritDoc}
*/
public boolean isSingle() {
- assert values.size() == 1;
return false;
}
Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicSingleValueProperty.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicSingleValueProperty.java 2008-07-09 21:20:54 UTC (rev 350)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicSingleValueProperty.java 2008-07-11 20:33:53 UTC (rev 351)
@@ -27,6 +27,9 @@
import org.jboss.dna.spi.graph.Name;
/**
+ * An immutable version of a property that has exactly 1 value. This is done for efficiency of the in-memory representation, since
+ * many properties will have just a single value, while others will have multiple values.
+ *
* @author Randall Hauch
*/
@Immutable
@@ -35,8 +38,10 @@
protected final Object value;
/**
- * @param name
- * @param value
+ * Create a property with a single value
+ *
+ * @param name the property name
+ * @param value the property value (which may be null)
*/
public BasicSingleValueProperty( Name name,
Object value ) {
15 years, 10 months
DNA SVN: r350 - in trunk/dna-common/src: test/java/org/jboss/dna/common/component and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-07-09 17:20:54 -0400 (Wed, 09 Jul 2008)
New Revision: 350
Modified:
trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentLibraryTest.java
Log:
DNA-176 - Changing class loader factory on ComponentLibrary should update the instances
http://jira.jboss.com/jira/browse/DNA-176
Added method to explicitly refresh the instances, and should automatically do this when setting the class loader factory.
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java 2008-07-09 20:37:01 UTC (rev 349)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java 2008-07-09 21:20:54 UTC (rev 350)
@@ -37,11 +37,11 @@
/**
* Maintains the list of component instances for the system. This class does not actively update the component configurations, but
* is designed to properly maintain the sequencer instances when those configurations are changed by other callers. If the
- * components are subclasses of {@link Component}, then they will be
- * {@link Component#setConfiguration(ComponentConfig) configured} with the appropriate configuration.
+ * components are subclasses of {@link Component}, then they will be {@link Component#setConfiguration(ComponentConfig)
+ * configured} with the appropriate configuration.
* <p>
- * Therefore, this library does guarantee that the {@link #getInstances() instances} at the time they are
- * {@link #getInstances() obtained} are always reflected by the configurations.
+ * Therefore, this library does guarantee that the {@link #getInstances() instances} at the time they are {@link #getInstances()
+ * obtained} are always reflected by the configurations.
* </p>
*
* @author Randall Hauch
@@ -52,9 +52,8 @@
public class ComponentLibrary<ComponentType, ConfigType extends ComponentConfig> {
/**
- * Class loader factory instance that always returns the
- * {@link Thread#getContextClassLoader() current thread's context class loader} (if not null) or component library's class
- * loader.
+ * Class loader factory instance that always returns the {@link Thread#getContextClassLoader() current thread's context class
+ * loader} (if not null) or component library's class loader.
*/
public static final ClassLoaderFactory DEFAULT = new StandardClassLoaderFactory(ComponentLibrary.class.getClassLoader());
@@ -81,9 +80,8 @@
/**
* Get the class loader factory that should be used to load the component classes. Unless changed, the library uses the
- * {@link #DEFAULT default} class loader factory, which uses the
- * {@link Thread#getContextClassLoader() current thread's context class loader} if not null or the class loader that loaded
- * the library class.
+ * {@link #DEFAULT default} class loader factory, which uses the {@link Thread#getContextClassLoader() current thread's
+ * context class loader} if not null or the class loader that loaded the library class.
*
* @return the class loader factory; never null
* @see #setClassLoaderFactory(ClassLoaderFactory)
@@ -94,9 +92,8 @@
/**
* Set the Maven Repository that should be used to load the sequencer classes. Unless changed, the library uses the
- * {@link #DEFAULT default} class loader factory, which uses the
- * {@link Thread#getContextClassLoader() current thread's context class loader} if not null or the class loader that loaded
- * the library class.
+ * {@link #DEFAULT default} class loader factory, which uses the {@link Thread#getContextClassLoader() current thread's
+ * context class loader} if not null or the class loader that loaded the library class.
*
* @param classLoaderFactory the class loader factory reference, or null if the {@link #DEFAULT default class loader factory}
* should be used
@@ -104,11 +101,12 @@
*/
public void setClassLoaderFactory( ClassLoaderFactory classLoaderFactory ) {
this.classLoaderFactory.set(classLoaderFactory != null ? classLoaderFactory : DEFAULT);
+ refreshInstances();
}
/**
- * Add the configuration for a sequencer, or update any existing one that represents the
- * {@link ConfigType#equals(Object) same configuration}
+ * Add the configuration for a sequencer, or update any existing one that represents the {@link ConfigType#equals(Object) same
+ * configuration}
*
* @param config the new configuration
* @return true if the component was added, or false if there already was an existing and
@@ -143,7 +141,8 @@
}
/**
- * Update the configuration for a sequencer, or add it if there is no {@link ConfigType#equals(Object) matching configuration}.
+ * Update the configuration for a sequencer, or add it if there is no {@link ConfigType#equals(Object) matching configuration}
+ * .
*
* @param config the updated (or new) configuration
* @return true if the component was updated, or false if there already was an existing and
@@ -184,6 +183,29 @@
}
/**
+ * Refresh the instances by attempting to re-instantiate each registered configuration.
+ *
+ * @return true if at least one instance was instantiated, or false if none were
+ */
+ public boolean refreshInstances() {
+ try {
+ this.lock.lock();
+ // Loop through and create new instances for each configuration ...
+ boolean found = false;
+ int index = 0;
+ for (ConfigType config : this.configs) {
+ ComponentType instance = newInstance(config);
+ found = found || instance != null;
+ this.instances.set(index, instance);
+ ++index;
+ }
+ return found;
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ /**
* Return the list of sequencers.
*
* @return the unmodifiable list of sequencers; never null
Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentLibraryTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentLibraryTest.java 2008-07-09 20:37:01 UTC (rev 349)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentLibraryTest.java 2008-07-09 21:20:54 UTC (rev 350)
@@ -168,4 +168,66 @@
}
assertThat(firstComponentB.getCounter(), is(10));
}
+
+ @Test
+ public void shouldRefreshInstancesWhenCalledDirectly() {
+ library.add(configA);
+ library.add(configB);
+ List<SampleComponent> components = library.getInstances();
+ assertThat(components.size(), is(2));
+ assertThat(components.get(0), instanceOf(MockComponentA.class));
+ assertThat(components.get(1), instanceOf(MockComponentB.class));
+ MockComponentA firstInstanceA = (MockComponentA)components.get(0);
+ MockComponentB firstInstanceB = (MockComponentB)components.get(1);
+ assertThat(components.get(0), is(sameInstance((SampleComponent)firstInstanceA)));
+ assertThat(components.get(1), is(sameInstance((SampleComponent)firstInstanceB)));
+
+ // Refresh the instances ...
+ assertThat(library.refreshInstances(), is(true));
+
+ // Check that there are instances for each of the components ...
+ components = library.getInstances();
+ assertThat(components.size(), is(2));
+ assertThat(components.get(0), instanceOf(MockComponentA.class));
+ assertThat(components.get(1), instanceOf(MockComponentB.class));
+ MockComponentA secondInstanceA = (MockComponentA)components.get(0);
+ MockComponentB secondInstanceB = (MockComponentB)components.get(1);
+ assertThat(components.get(0), is(sameInstance((SampleComponent)secondInstanceA)));
+ assertThat(components.get(1), is(sameInstance((SampleComponent)secondInstanceB)));
+
+ // And check that the instances have changed ...
+ assertThat(firstInstanceA, is(not(sameInstance((SampleComponent)secondInstanceA))));
+ assertThat(firstInstanceB, is(not(sameInstance((SampleComponent)secondInstanceB))));
+ }
+
+ @Test
+ public void shouldRefreshInstancesWhenSettingClassLoaderFactory() {
+ library.add(configA);
+ library.add(configB);
+ List<SampleComponent> components = library.getInstances();
+ assertThat(components.size(), is(2));
+ assertThat(components.get(0), instanceOf(MockComponentA.class));
+ assertThat(components.get(1), instanceOf(MockComponentB.class));
+ MockComponentA firstInstanceA = (MockComponentA)components.get(0);
+ MockComponentB firstInstanceB = (MockComponentB)components.get(1);
+ assertThat(components.get(0), is(sameInstance((SampleComponent)firstInstanceA)));
+ assertThat(components.get(1), is(sameInstance((SampleComponent)firstInstanceB)));
+
+ // Set the class loader factory to the SAME instance, and this should always refresh the instances ...
+ library.setClassLoaderFactory(library.getClassLoaderFactory());
+
+ // Check that there are instances for each of the components ...
+ components = library.getInstances();
+ assertThat(components.size(), is(2));
+ assertThat(components.get(0), instanceOf(MockComponentA.class));
+ assertThat(components.get(1), instanceOf(MockComponentB.class));
+ MockComponentA secondInstanceA = (MockComponentA)components.get(0);
+ MockComponentB secondInstanceB = (MockComponentB)components.get(1);
+ assertThat(components.get(0), is(sameInstance((SampleComponent)secondInstanceA)));
+ assertThat(components.get(1), is(sameInstance((SampleComponent)secondInstanceB)));
+
+ // And check that the instances have changed ...
+ assertThat(firstInstanceA, is(not(sameInstance((SampleComponent)secondInstanceA))));
+ assertThat(firstInstanceB, is(not(sameInstance((SampleComponent)secondInstanceB))));
+ }
}
15 years, 10 months
DNA SVN: r349 - in trunk: dna-repository/src/test/java/org/jboss/dna/repository/sequencers and 4 other directories.
by dna-commits@lists.jboss.org
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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/E/@something").getInputPath(), is("/a/b/c/d/E/@something"));
+ assertThat(expr.matcher("/a/b/c/d/E/@something").getSelectedNodePath(), is("/a/b/c/d/E"));
+ assertThat(expr.matcher("/a/b/c/d/E/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/@something2").matches(), is(false));
+ assertThat(expr.matcher("/a/b/c/d/ex/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/E/@something2").matches(), is(false));
+ assertThat(expr.matcher("/a/b/c/d/ex/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").matches(), is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithWildcardSelection() {
+ expr = PathExpression.compile("/a/*/c[d/e/@something]");
+ assertThat(expr.matcher("/a/b/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getInputPath(), is("/a/b[2]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getSelectedNodePath(), is("/a/b[2]/c"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").getInputPath(), is("/a/rt/c/d/e/@something"));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").getSelectedNodePath(), is("/a/rt/c"));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/ac/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/b/b2/b3/d/e/@something").matches(), is(false));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithFilenameLikeWildcardSelection() {
+ expr = PathExpression.compile("/a/*.txt[@something]");
+ assertThat(expr.matcher("/a/b.txt/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/@something").getInputPath(), is("/a/b.txt/@something"));
+ assertThat(expr.matcher("/a/b.txt/@something").getSelectedNodePath(), is("/a/b.txt"));
+ assertThat(expr.matcher("/a/b.txt/@something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/@something").matches(), is(false));
+
+ expr = PathExpression.compile("/a/*.txt/c[@something]");
+ assertThat(expr.matcher("/a/b.txt/c/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/c/@something").getInputPath(), is("/a/b.txt/c/@something"));
+ assertThat(expr.matcher("/a/b.txt/c/@something").getSelectedNodePath(), is("/a/b.txt/c"));
+ assertThat(expr.matcher("/a/b.txt/c/@something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/c/@something").matches(), is(false));
+
+ expr = PathExpression.compile("//*.txt[*]/c[@something]");
+ assertThat(expr.matcher("/a/b.txt/c/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b.txt/c/@something").getInputPath(), is("/a/b.txt/c/@something"));
+ assertThat(expr.matcher("/a/b.txt/c/@something").getSelectedNodePath(), is("/a/b.txt/c"));
+ assertThat(expr.matcher("/a/b.txt/c/@something").groupCount(), is(0));
+ assertThat(expr.matcher("/a/b.tx/c/@something").matches(), is(false));
+
+ assertThat(expr.matcher("/z/a/b.txt/c/@something").matches(), is(true));
+ assertThat(expr.matcher("/z/a/b.txt/c/@something").getInputPath(), is("/z/a/b.txt/c/@something"));
+ assertThat(expr.matcher("/z/a/b.txt/c/@something").getSelectedNodePath(), is("/z/a/b.txt/c"));
+ assertThat(expr.matcher("/z/a/b.txt/c/@something").groupCount(), is(0));
+ }
+
+ @Test
+ public void shouldMatchExpressionsWithSegmentWildcardSelection() {
+ expr = PathExpression.compile("/a//c[d/e/@something]");
+ assertThat(expr.matcher("/a/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/c/d/e/@something").getInputPath(), is("/a/c/d/e/@something"));
+ assertThat(expr.matcher("/a/c/d/e/@something").getSelectedNodePath(), is("/a/c"));
+ assertThat(expr.matcher("/a/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getInputPath(), is("/a/b[2]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getSelectedNodePath(), is("/a/b[2]/c"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").getInputPath(), is("/a/rt/c/d/e/@something"));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").getSelectedNodePath(), is("/a/rt/c"));
+ assertThat(expr.matcher("/a/rt/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/@something").getInputPath(), is("/a/r/s/t/c/d/e/@something"));
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/@something").getSelectedNodePath(), is("/a/r/s/t/c"));
+ assertThat(expr.matcher("/a/r/s/t/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/@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/@something").getSelectedNodePath(), is("/a/r[1]/s[2]/t[33]/c"));
+ assertThat(expr.matcher("/a/r[1]/s[2]/t[33]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a[3]/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getInputPath(), is("/a/b[2]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getSelectedNodePath(), is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getInputPath(), is("/a/b[3]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getSelectedNodePath(), is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getInputPath(), is("/a/b[4]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getSelectedNodePath(), is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getInputPath(), is("/a/b[5]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getSelectedNodePath(), is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a[1]/b/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getInputPath(), is("/a/b[2]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getSelectedNodePath(), is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getInputPath(), is("/a/b[3]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getSelectedNodePath(), is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getInputPath(), is("/a/b[4]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getSelectedNodePath(), is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getInputPath(), is("/a/b[5]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getSelectedNodePath(), is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[0]/c/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").matches(), is(false));
+ assertThat(expr.matcher("/a[1]/b/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getInputPath(), is("/a/b[2]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").getSelectedNodePath(), is("/a/b[2]/c/d/e"));
+ assertThat(expr.matcher("/a/b[2]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getInputPath(), is("/a/b[3]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").getSelectedNodePath(), is("/a/b[3]/c/d/e"));
+ assertThat(expr.matcher("/a/b[3]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getInputPath(), is("/a/b[4]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").getSelectedNodePath(), is("/a/b[4]/c/d/e"));
+ assertThat(expr.matcher("/a/b[4]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getInputPath(), is("/a/b[5]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").getSelectedNodePath(), is("/a/b[5]/c/d/e"));
+ assertThat(expr.matcher("/a/b[5]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").getInputPath(), is("/a/b[1]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").getSelectedNodePath(), is("/a/b[1]/c/d/e"));
+ assertThat(expr.matcher("/a/b[1]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[6]/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[6]/c/d/e/@something").getInputPath(), is("/a/b[6]/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b[6]/c/d/e/@something").getSelectedNodePath(), is("/a/b[6]/c/d/e"));
+ assertThat(expr.matcher("/a/b[6]/c/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/@something").getInputPath(), is("/a/b[6]/c[1]/d/e/@something"));
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/@something").getSelectedNodePath(), is("/a/b[6]/c[1]/d/e"));
+ assertThat(expr.matcher("/a/b[6]/c[1]/d/e/@something").groupCount(), is(0));
+
+ assertThat(expr.matcher("/a/b/c/d/e/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c/d/e"));
+ assertThat(expr.matcher("/a/b/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getInputPath(), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").groupCount(), is(1));
+ assertThat(expr.matcher("/a/b/c/d/e/@something").group(0), is("/a/b/c/d/e/@something"));
+ assertThat(expr.matcher("/a/b/c/d/e/@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/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").getInputPath(), is("/a/b/e/f/g/@something"));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").getSelectedNodePath(), is("/a/b/e"));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").group(0), is("/a/b/e/f/g/@something"));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").group(1), is("/b/e"));
+ assertThat(expr.matcher("/a/b/e/f/g/@something").group(2), is("b"));
+
+ assertThat(expr.matcher("/a/c/e/f/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").getInputPath(), is("/a/c/e/f/g/@something"));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").getSelectedNodePath(), is("/a/c/e"));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").group(0), is("/a/c/e/f/g/@something"));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").group(1), is("/c/e"));
+ assertThat(expr.matcher("/a/c/e/f/g/@something").group(2), is("c"));
+
+ assertThat(expr.matcher("/a/d/e/f/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").getInputPath(), is("/a/d/e/f/g/@something"));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").getSelectedNodePath(), is("/a/d/e"));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").group(0), is("/a/d/e/f/g/@something"));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").group(1), is("/d/e"));
+ assertThat(expr.matcher("/a/d/e/f/g/@something").group(2), is("d"));
+
+ assertThat(expr.matcher("/a/e/f/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/e/f/g/@something").getInputPath(), is("/a/e/f/g/@something"));
+ assertThat(expr.matcher("/a/e/f/g/@something").getSelectedNodePath(), is("/a/e"));
+ assertThat(expr.matcher("/a/e/f/g/@something").groupCount(), is(2));
+ assertThat(expr.matcher("/a/e/f/g/@something").group(0), is("/a/e/f/g/@something"));
+ assertThat(expr.matcher("/a/e/f/g/@something").group(1), is("/e"));
+ assertThat(expr.matcher("/a/e/f/g/@something").group(2), is(nullValue()));
+
+ assertThat(expr.matcher("/a/t/e/f/g/@something").matches(), is(false));
+
+ expr = PathExpression.compile("/a/(b/c)[(d|e)/(f|g)/@something]");
+ assertThat(expr.matcher("/a/b/c/d/f/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").getInputPath(), is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(0), is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(1), is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(2), is("d"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(3), is("f"));
+
+ assertThat(expr.matcher("/a/b/c/e/f/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").getInputPath(), is("/a/b/c/e/f/@something"));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").group(0), is("/a/b/c/e/f/@something"));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").group(1), is("b/c"));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").group(2), is("e"));
+ assertThat(expr.matcher("/a/b/c/e/f/@something").group(3), is("f"));
+
+ assertThat(expr.matcher("/a/b/c/d/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").getInputPath(), is("/a/b/c/d/g/@something"));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").group(0), is("/a/b/c/d/g/@something"));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").group(1), is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").group(2), is("d"));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").group(3), is("g"));
+
+ assertThat(expr.matcher("/a/b/c/e/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").getInputPath(), is("/a/b/c/e/g/@something"));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").getSelectedNodePath(), is("/a/b/c"));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").group(0), is("/a/b/c/e/g/@something"));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").group(1), is("b/c"));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").group(2), is("e"));
+ assertThat(expr.matcher("/a/b/c/e/g/@something").group(3), is("g"));
+
+ expr = PathExpression.compile("/a/(b/c)/(d|e)/(f|g)/@something");
+ assertThat(expr.matcher("/a/b/c/d/f/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").getInputPath(), is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").getSelectedNodePath(), is("/a/b/c/d/f"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").groupCount(), is(3));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(0), is("/a/b/c/d/f/@something"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(1), is("b/c"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(2), is("d"));
+ assertThat(expr.matcher("/a/b/c/d/f/@something").group(3), is("f"));
+
+ assertThat(expr.matcher("/a/b/c/e/f/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/d/g/@something").matches(), is(true));
+ assertThat(expr.matcher("/a/b/c/e/g/@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
15 years, 10 months
DNA SVN: r348 - in trunk/dna-repository/src: main/java/org/jboss/dna/repository/federation/merge and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-07-09 11:04:15 -0400 (Wed, 09 Jul 2008)
New Revision: 348
Added:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/AbstractContribution.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/BasicFederatedNode.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/Contribution.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/EmptyContribution.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/FederatedNode.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergePlan.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergeProcessor.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/NodeContribution.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/PropertyContribution.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/BasicFederatedNodeTest.java
Log:
DNA-115 - Create federation service
http://jira.jboss.com/jira/browse/DNA-115
Added MergePlan (including the Cntribution interface and various implementation classes) and MergeProcessor framework.
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/AbstractContribution.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/AbstractContribution.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/AbstractContribution.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,121 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.spi.graph.DateTime;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * @author Randall Hauch
+ */
+public abstract class AbstractContribution implements Contribution {
+
+ /**
+ * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ protected static final List<Segment> EMPTY_CHILDREN = Collections.emptyList();
+ protected static final Map<Name, Property> EMPTY_PROPERTIES = Collections.emptyMap();
+
+ private final String sourceName;
+ private DateTime expirationTimeInUtc;
+
+ /**
+ * Create a contribution for the source with the supplied name.
+ *
+ * @param sourceName the name of the source, which may not be null or blank
+ */
+ protected AbstractContribution( String sourceName ) {
+ assert sourceName != null && sourceName.trim().length() != 0;
+ this.sourceName = sourceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSourceName() {
+ return this.sourceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<Segment> getChildren() {
+ return EMPTY_CHILDREN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<Name, Property> getProperties() {
+ return EMPTY_PROPERTIES;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DateTime getExpirationTimeInUtc() {
+ return this.expirationTimeInUtc;
+ }
+
+ /**
+ * @param expirationTimeInUtc Sets expirationTimeInUtc to the specified value.
+ */
+ public void setExpirationTimeInUtc( DateTime expirationTimeInUtc ) {
+ this.expirationTimeInUtc = expirationTimeInUtc;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation returns the hash code of the {@link #getSourceName() source name}, and is compatible with the
+ * implementation of {@link #equals(Object)}.
+ * </p>
+ */
+ @Override
+ public int hashCode() {
+ return this.sourceName.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation only compares the {@link #getSourceName() source name}.
+ * </p>
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof AbstractContribution) {
+ AbstractContribution that = (AbstractContribution)obj;
+ return this.getSourceName().equals(that.getSourceName());
+ }
+ return false;
+ }
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/AbstractContribution.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/BasicFederatedNode.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/BasicFederatedNode.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/BasicFederatedNode.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,148 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * @author Randall Hauch
+ */
+@ThreadSafe
+public class BasicFederatedNode implements FederatedNode {
+
+ private final UUID uuid;
+
+ // private Name name;
+
+ /**
+ * Create a new federated node instance.
+ *
+ * @param uuid the UUID; may not be null
+ */
+ public BasicFederatedNode( UUID uuid ) {
+ assert uuid != null;
+ this.uuid = uuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Name getName() {
+ return null;
+ }
+
+ /**
+ * @param name Sets name to the specified value; may not be null
+ */
+ public void setName( Name name ) {
+ assert name != null;
+ // this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<String> getContributingSources() {
+ Set<String> names = new HashSet<String>();
+ return names;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Property> getProperties() {
+ // Compute merged properties ...
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Property getProperty( Name propertyName ) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getPropertyCount() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Name> getPropertyNames() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeAllProperties() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setAllProperties( Property... properties ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setAllProperties( Iterable<Property> properties ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setProperties( Property... properties ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setProperties( Iterable<Property> properties ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Property setPropertyIfAbsent( Property property ) {
+ return null;
+ }
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/BasicFederatedNode.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/Contribution.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/Contribution.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/Contribution.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,71 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.spi.graph.DateTime;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * The contribution of a source to the information for a single federated node. Users of this interface should treat contributions
+ * as generally being immutable, since some implementation will be immutable and will return immutable
+ * {@link #getProperties() properties} and {@link #getChildren() children} containers. Thus, rather than make changes to an
+ * existing contribution, a new contribution is created to replace the previous contribution.
+ *
+ * @author Randall Hauch
+ */
+@NotThreadSafe
+public interface Contribution extends Serializable {
+
+ /**
+ * Get the name of the source that made this contribution.
+ *
+ * @return the name of the contributing source
+ */
+ public String getSourceName();
+
+ /**
+ * Get the expiration time, already in UTC.
+ *
+ * @return the expiration time in UTC
+ */
+ public DateTime getExpirationTimeInUtc();
+
+ /**
+ * Get the properties that make up this contribution. This map is immutable.
+ *
+ * @return the map of properties; never null
+ */
+ public Map<Name, Property> getProperties();
+
+ /**
+ * Get the children that make up this contribution. This list is immutable.
+ *
+ * @return the list of children; never null
+ */
+ public List<Segment> getChildren();
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/Contribution.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/EmptyContribution.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/EmptyContribution.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/EmptyContribution.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,50 @@
+/*
+ * 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.repository.federation.merge;
+
+/**
+ * A source contribution that is empty. In other words, the source has no contribution to make.
+ * <p>
+ * Note that this is different than an unknown contribution, which may occur when a source is added to a federated repository
+ * after the contributions have already been determined for nodes. In this case, the new source's contribution for a node is not
+ * known and must be determined.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+public class EmptyContribution extends AbstractContribution {
+
+ /**
+ * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a contribution for the source with the supplied name.
+ *
+ * @param sourceName the name of the source, which may not be null or blank
+ */
+ public EmptyContribution( String sourceName ) {
+ super(sourceName);
+ }
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/EmptyContribution.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Copied: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/FederatedNode.java (from rev 321, trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedNode.java)
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/FederatedNode.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/FederatedNode.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,140 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * @author Randall Hauch
+ */
+@ThreadSafe
+public interface FederatedNode {
+ /**
+ * Get the unique identifier for this federated node.
+ *
+ * @return the UUID; never null
+ */
+ UUID getUuid();
+
+ /**
+ * Get the name of this node.
+ *
+ * @return the node name; never null
+ */
+ Name getName();
+
+ /**
+ * Get the property with the given name.
+ *
+ * @param propertyName the property name
+ * @return the property, or null if the property does not exist
+ * @throws IllegalArgumentException if the name is null
+ */
+ Property getProperty( Name propertyName );
+
+ /**
+ * Get the number of properties on this node. This value is technically an estimate, as it may not exactly match the number of
+ * properties returned by {@link #getProperties()} or {@link #getPropertyNames()}.
+ *
+ * @return the approximate number of properties.
+ */
+ int getPropertyCount();
+
+ /**
+ * Get the properties. This method returns a consistent set of properties at the moment this method is called, and is not
+ * affected by additions or change to the properties. In other words, it is safe for concurrent operations and is not a
+ * fail-fast iterator.
+ *
+ * @return the properties on this node via an immutable iterator
+ */
+ Iterator<Property> getProperties();
+
+ /**
+ * Get the names of the properties for this node. This method returns a consistent set of names at the moment this method is
+ * called, and is not affected by additions or change to the properties. In other words, it is safe for concurrent operations
+ * and is not a fail-fast iterator.
+ *
+ * @return the property names via an immutable iterator
+ */
+ Iterator<Name> getPropertyNames();
+
+ /**
+ * Set the property if it is not already set.
+ *
+ * @param property the property
+ * @return the existing property, or null if there was no property and the supplied property was set
+ * @throws IllegalArgumentException if the property is null
+ */
+ Property setPropertyIfAbsent( Property property );
+
+ /**
+ * Set the supplied properties. This method will overwrite any existing properties with the new properties if they have the
+ * same {@link Property#getName() property name}. This method ignores any null property references, and does nothing if there
+ * are no properties supplied.
+ *
+ * @param properties the properties that should be set
+ */
+ void setProperties( Property... properties );
+
+ /**
+ * Set the supplied properties. This method will overwrite any existing properties with the new properties if they have the
+ * same {@link Property#getName() property name}. This method ignores any null property references, and does nothing if there
+ * are no properties supplied.
+ *
+ * @param properties the properties that should be set
+ */
+ void setProperties( Iterable<Property> properties );
+
+ /**
+ * Replace all existing properties with the supplied properties. This method ignores any null property references, and does
+ * nothing if there are no properties supplied.
+ *
+ * @param properties the properties that should be set
+ */
+ void setAllProperties( Property... properties );
+
+ /**
+ * Replace all existing properties with the supplied properties. This method ignores any null property references, and does
+ * nothing if there are no properties supplied.
+ *
+ * @param properties the properties that should replace the existing properties
+ */
+ void setAllProperties( Iterable<Property> properties );
+
+ /**
+ * Remove all properties, except for the {@link #getName() name} and {@link #getUuid() identifier}.
+ */
+ void removeAllProperties();
+
+ /**
+ * Get the sources that have contributed information to this node.
+ *
+ * @return the names of the sources that have contributed content to this node.
+ */
+ Set<String> getContributingSources();
+
+}
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergePlan.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergePlan.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergePlan.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,168 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.io.InvalidClassException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.spi.graph.DateTime;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * This class represents the details about how information from different sources are merged into a single federated node.
+ * <p>
+ * A merge plan basically consists of the individual contribution from each source and the information about how these
+ * contributions were merged into the single federated node.
+ * </p>
+ * <p>
+ * Merge plans are designed to be {@link Serializable serializable}, as they are persisted on the federated node and deserialized
+ * to assist in the management of the federated node.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@NotThreadSafe
+public class MergePlan implements Serializable {
+
+ /**
+ * Define the earliest version of this class that is supported. The Java runtime, upon deserialization, compares the
+ * serialized object's version to this, and if less than this version will throw a {@link InvalidClassException}. If, however,
+ * the serialized object's version is compatible with this class, it will be deserialized successfully.
+ * <p>
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html...">Sun's documentation</a> describes
+ * the following changes can be made without negatively affecting the deserialization of older versions:
+ * <ul>
+ * <li>Adding fields - When the class being reconstituted has a field that does not occur in the stream, that field in the
+ * object will be initialized to the default value for its type. If class-specific initialization is needed, the class may
+ * provide a readObject method that can initialize the field to nondefault values.</i>
+ * <li>Adding classes - The stream will contain the type hierarchy of each object in the stream. Comparing this hierarchy in
+ * the stream with the current class can detect additional classes. Since there is no information in the stream from which to
+ * initialize the object, the class's fields will be initialized to the default values.</i>
+ * <li>Removing classes - Comparing the class hierarchy in the stream with that of the current class can detect that a class
+ * has been deleted. In this case, the fields and objects corresponding to that class are read from the stream. Primitive
+ * fields are discarded, but the objects referenced by the deleted class are created, since they may be referred to later in
+ * the stream. They will be garbage-collected when the stream is garbage-collected or reset.</i>
+ * <li>Adding writeObject/readObject methods - If the version reading the stream has these methods then readObject is
+ * expected, as usual, to read the required data written to the stream by the default serialization. It should call
+ * defaultReadObject first before reading any optional data. The writeObject method is expected as usual to call
+ * defaultWriteObject to write the required data and then may write optional data.</i>
+ * <li>Removing writeObject/readObject methods - If the class reading the stream does not have these methods, the required
+ * data will be read by default serialization, and the optional data will be discarded.</i>
+ * <li>Adding java.io.Serializable - This is equivalent to adding types. There will be no values in the stream for this class
+ * so its fields will be initialized to default values. The support for subclassing nonserializable classes requires that the
+ * class's supertype have a no-arg constructor and the class itself will be initialized to default values. If the no-arg
+ * constructor is not available, the InvalidClassException is thrown.</i>
+ * <li>Changing the access to a field - The access modifiers public, package, protected, and private have no effect on the
+ * ability of serialization to assign values to the fields.</i>
+ * <li>Changing a field from static to nonstatic or transient to nontransient - When relying on default serialization to
+ * compute the serializable fields, this change is equivalent to adding a field to the class. The new field will be written to
+ * the stream but earlier classes will ignore the value since serialization will not assign values to static or transient
+ * fields.</i>
+ * </ul>
+ * All other kinds of modifications should be avoided.
+ * </p>
+ */
+ private static final long serialVersionUID = 1L;
+
+ private final Map<String, Contribution> contributions = new HashMap<String, Contribution>();
+ private Map<Name, Property> annotations = null;
+ private DateTime expirationTimeInUtc;
+
+ /**
+ * Create this version
+ */
+ public MergePlan() {
+ }
+
+ /**
+ * Get the expiration time, already in UTC.
+ *
+ * @return the expiration time in UTC, or null if there is no known expiration time
+ */
+ public DateTime getExpirationTimeInUtc() {
+ return expirationTimeInUtc;
+ }
+
+ /**
+ * Get the contribution from the source with the supplied name. Note that contributions always include sources that contribute
+ * information and sources that contribute no information. If a source is not included in this list, its contributions are
+ * <i>unknown</i>; that is, it is unknown whether that source does or does not contribute to the node.
+ *
+ * @param sourceName the name of the source
+ * @return the contribution, or null if the contribution of the source is unknown
+ */
+ public Contribution getContributionFrom( String sourceName ) {
+ return contributions.get(sourceName);
+ }
+
+ /**
+ * Add the supplied contribution, replacing and returning any previous contribution for the same source.
+ *
+ * @param contribution the new contribution
+ * @return the previous contribution for the source, or null if there was no previous contribution.
+ */
+ public Contribution addContribution( Contribution contribution ) {
+ assert contribution != null;
+ Contribution previous = contributions.put(contribution.getSourceName(), contribution);
+ DateTime contributionExpirationInUtc = contribution.getExpirationTimeInUtc();
+ if (expirationTimeInUtc == null || contributionExpirationInUtc.isBefore(expirationTimeInUtc)) {
+ expirationTimeInUtc = contributionExpirationInUtc;
+ }
+ return previous;
+ }
+
+ /**
+ * Get the plan annotation property with the given name. Plan annotations are custom properties that may be set by
+ * {@link MergeProcessor} implementations to store custom properties on the plan. This method does nothing if the supplied
+ * name is null
+ *
+ * @param name the name of the annotation
+ * @return the existing annotation, or null if there is no annotation with the supplied name
+ * @see #setAnnotation(Property)
+ */
+ public Property getAnnotation( Name name ) {
+ if (name == null) return null;
+ if (this.annotations == null) return null;
+ return this.annotations.get(name);
+ }
+
+ /**
+ * Set the plan annotation property. This method replaces and returns any existing annotation property with the same name.
+ * This method also returns immediately if the supplied annotation is null.
+ *
+ * @param annotation the new annotation
+ * @return the previous annotation property with the same name, or null if there was no previous annotation property for the
+ * name
+ * @see #getAnnotation(Name)
+ */
+ public Property setAnnotation( Property annotation ) {
+ if (annotation == null) return null;
+ if (this.annotations == null) {
+ this.annotations = new HashMap<Name, Property>();
+ }
+ return this.annotations.put(annotation.getName(), annotation);
+ }
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergePlan.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergeProcessor.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergeProcessor.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergeProcessor.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,47 @@
+/*
+ * 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.repository.federation.merge;
+
+import org.jboss.dna.repository.util.ExecutionContext;
+
+/**
+ * @author Randall Hauch
+ */
+public interface MergeProcessor {
+
+ /**
+ * Merge the supplied contributions into a single federated node instance.
+ *
+ * @param context the context in which this processor is running; never null
+ * @param federatedNode the federated node into which should be place all of the merged properties and child references; never
+ * null
+ * @param previousMergePlan the merge plan generated from the most recent previous merge; may be null if the node has not yet
+ * been merged
+ * @param newMergePlan the new merge plan that contains the contributions from each of the sources and which may be consulted
+ * (and annotated) to should be filled out by this method implementation; never null
+ */
+ void merge( ExecutionContext context,
+ FederatedNode federatedNode,
+ MergePlan previousMergePlan,
+ MergePlan newMergePlan );
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/MergeProcessor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/NodeContribution.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/NodeContribution.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/NodeContribution.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,74 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * The contribution of a source to the information for a single federated node.
+ * <p>
+ * This class does return a mutable list of {@link #getChildren() children} and mutable map of {@link #getProperties() properties}.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@NotThreadSafe
+public class NodeContribution extends PropertyContribution {
+
+ /**
+ * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ private List<Segment> children;
+
+ /**
+ * Create a contribution of node properties and children from the source with the supplied name.
+ *
+ * @param sourceName the name of the source, which may not be null or blank
+ * @param properties the properties from the source; may not be null
+ * @param children the children from the source; may not be null or empty
+ */
+ public NodeContribution( String sourceName,
+ Iterable<Property> properties,
+ Iterable<Segment> children ) {
+ super(sourceName, properties);
+ this.children = new ArrayList<Segment>(1);
+ for (Segment child : children) {
+ this.children.add(child);
+ }
+ }
+
+ /**
+ * Get the children that make up this contribution. This list is immutable.
+ *
+ * @return the list of children; never null
+ */
+ @Override
+ public List<Segment> getChildren() {
+ return this.children;
+ }
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/NodeContribution.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/PropertyContribution.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/PropertyContribution.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/PropertyContribution.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,69 @@
+/*
+ * 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.repository.federation.merge;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * <p>
+ * This class does return a mutable mutable map of {@link #getProperties() properties}.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+public class PropertyContribution extends AbstractContribution {
+
+ /**
+ * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ private final Map<Name, Property> properties;
+
+ /**
+ * Create a contribution of node properties from the source with the supplied name.
+ *
+ * @param sourceName the name of the source, which may not be null or blank
+ * @param properties the properties from the source; may not be null
+ */
+ public PropertyContribution( String sourceName,
+ Iterable<Property> properties ) {
+ super(sourceName);
+ this.properties = new HashMap<Name, Property>();
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map<Name, Property> getProperties() {
+ return this.properties;
+ }
+
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/merge/PropertyContribution.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/BasicFederatedNodeTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/BasicFederatedNodeTest.java (rev 0)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/BasicFederatedNodeTest.java 2008-07-09 15:04:15 UTC (rev 348)
@@ -0,0 +1,41 @@
+/*
+ * 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.repository.federation.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class BasicFederatedNodeTest {
+
+ @Before
+ public void beforeEach() {
+ }
+
+ @Test
+ public void shouldDoSomething() {
+
+ }
+
+}
Property changes on: trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/impl/BasicFederatedNodeTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
15 years, 10 months
DNA SVN: r347 - in trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation: impl and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-07-09 11:01:11 -0400 (Wed, 09 Jul 2008)
New Revision: 347
Added:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/FederatingCommandExecutor.java
Log:
DNA-115 - Create federation service
http://jira.jboss.com/jira/browse/DNA-115
Added FederatingCommandExecutor implementation, which is used by the FederatedRepository to execute commands.
Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/FederatingCommandExecutor.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/FederatingCommandExecutor.java (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/FederatingCommandExecutor.java 2008-07-09 15:01:11 UTC (rev 347)
@@ -0,0 +1,179 @@
+/*
+ * 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.repository.federation.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.repository.federation.FederatedRegion;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.PathNotFoundException;
+import org.jboss.dna.spi.graph.commands.GetPropertiesCommand;
+import org.jboss.dna.spi.graph.commands.executor.AbstractCommandExecutor;
+import org.jboss.dna.spi.graph.commands.impl.BasicGetPropertiesCommand;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+import org.jboss.dna.spi.graph.connection.RepositoryConnection;
+import org.jboss.dna.spi.graph.connection.RepositoryConnectionFactories;
+import org.jboss.dna.spi.graph.connection.RepositoryConnectionFactory;
+import org.jboss.dna.spi.graph.connection.RepositorySource;
+import org.jboss.dna.spi.graph.connection.RepositorySourceException;
+
+/**
+ * @author Randall Hauch
+ */
+@NotThreadSafe
+public class FederatingCommandExecutor extends AbstractCommandExecutor {
+
+ private final Name mergePlanPropertyName;
+ private final FederatedRegion cacheRegion;
+ private final List<FederatedRegion> regions;
+ private final RepositoryConnectionFactories connectionFactories;
+ /** The set of all connections, including the cache connection */
+ private final Map<String, RepositoryConnection> connectionsBySourceName;
+ /** A direct reference to the cache connection */
+ private RepositoryConnection cacheConnection;
+
+ /**
+ * @param env the execution environment in which the executor will be run; may not be null
+ * @param sourceName the name of the {@link RepositorySource} that is making use of this executor; may not be null or empty
+ * @param cacheRegion the region used for the cached information; may not be null
+ * @param regions the federated regions; may not be null
+ * @param connectionFactories the factory for connection factory instances
+ */
+ public FederatingCommandExecutor( ExecutionEnvironment env,
+ String sourceName,
+ FederatedRegion cacheRegion,
+ List<FederatedRegion> regions,
+ RepositoryConnectionFactories connectionFactories ) {
+ super(env, sourceName);
+ assert regions != null;
+ assert connectionFactories != null;
+ assert cacheRegion != null;
+ this.cacheRegion = cacheRegion;
+ this.regions = regions;
+ this.connectionFactories = connectionFactories;
+ this.connectionsBySourceName = new HashMap<String, RepositoryConnection>();
+ this.mergePlanPropertyName = env.getValueFactories().getNameFactory().create("dna:mergePlan");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.commands.executor.AbstractCommandExecutor#close()
+ */
+ @Override
+ public void close() throws InterruptedException {
+ try {
+ super.close();
+ } finally {
+ // Make sure to close ALL open connections ...
+ for (RepositoryConnection connection : connectionsBySourceName.values()) {
+ if (connection == null) continue;
+ try {
+ connection.close();
+ } catch (Throwable t) {
+ Logger.getLogger(getClass()).debug("Error while closing connection to {0}", connection.getSourceName());
+ }
+ }
+ connectionsBySourceName.clear();
+ }
+ }
+
+ protected RepositoryConnection getConnectionToCache() throws RepositorySourceException, InterruptedException {
+ if (this.cacheConnection == null) {
+ this.cacheConnection = getConnection(this.cacheRegion);
+ }
+ assert this.cacheConnection != null;
+ return this.cacheConnection;
+ }
+
+ protected RepositoryConnection getConnection( FederatedRegion region ) throws RepositorySourceException, InterruptedException {
+ String sourceName = region.getSourceName();
+ RepositoryConnection connection = connectionsBySourceName.get(sourceName);
+ if (connection == null) {
+ RepositoryConnectionFactory connectionFactory = connectionFactories.getConnectionFactory(sourceName);
+ if (connectionFactory != null) {
+ connection = connectionFactory.getConnection();
+ }
+ connectionsBySourceName.put(sourceName, connection);
+ }
+ return connection;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.commands.executor.AbstractCommandExecutor#execute(org.jboss.dna.spi.graph.commands.GetPropertiesCommand)
+ */
+ @Override
+ public void execute( GetPropertiesCommand command ) throws RepositorySourceException, InterruptedException {
+
+ // Check the cache first ...
+ RepositoryConnection cacheConnection = getConnectionToCache();
+ BasicGetPropertiesCommand fromCache = new BasicGetPropertiesCommand(command.getPath());
+ cacheConnection.execute(getEnvironment(), fromCache);
+ if (fromCache.getError() instanceof PathNotFoundException) {
+ } else {
+ // Get the existing merge plan from the cached properties ...
+ // MergePlan mergePlan = getMergePlan(fromCache);
+ // DateTime
+ // if ( mergePlan.getExpirationTimeInUtc() )
+ }
+ }
+
+ // protected MergePlan getMergePlan( BasicGetPropertiesCommand command ) {
+ // Property mergePlanProperty = command.getProperties().get(mergePlanPropertyName);
+ // if (mergePlanProperty == null || mergePlanProperty.isEmpty()) {
+ // return null;
+ // }
+ // ValueFactory<Binary> binaryFactory = getEnvironment().getValueFactories().getBinaryFactory();
+ // Binary binaryValue = binaryFactory.create(mergePlanProperty.getValues().next());
+ // binaryValue.acquire();
+ // ObjectInputStream stream = null;
+ // MergePlan mergePlan = null;
+ // RepositorySourceException error = null;
+ // try {
+ // stream = new ObjectInputStream(binaryValue.getStream());
+ // mergePlan = (MergePlan)stream.readObject();
+ // } catch (Throwable err) {
+ // I18n msg = RepositoryI18n.errorReadingMergePlan;
+ // error = new RepositorySourceException(getSourceName(), msg.text(command.getPath()), err);
+ // throw error;
+ // } finally {
+ // try {
+ // if (stream != null) stream.close();
+ // } catch (Throwable err) {
+ // if (error == null) {
+ // I18n msg = RepositoryI18n.errorReadingMergePlan;
+ // error = new RepositorySourceException(getSourceName(), msg.text(command.getPath()), err);
+ // throw error;
+ // }
+ // } finally {
+ // binaryValue.release();
+ // }
+ // }
+ // return mergePlan;
+ // }
+ //
+}
Property changes on: trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/impl/FederatingCommandExecutor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
15 years, 10 months