From dna-commits at lists.jboss.org  Mon Nov  9 14:29:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 9 Nov 2009 14:29:51 -0500
Subject: [dna-commits] DNA SVN: r1317 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph and 1 other directory.
Message-ID: <200911091929.nA9JTp22025837@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-09 14:29:50 -0500 (Mon, 09 Nov 2009)
New Revision: 1317

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
Log:
DNA-550 Added methods to Graph to allow reading of one or more properties on multiple nodes, all in a single operation

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-30 21:34:28 UTC (rev 1316)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-11-09 19:29:50 UTC (rev 1317)
@@ -2062,8 +2062,9 @@
      * @param name the name of the property that is to be read
      * @return the object that is used to specified the node whose property is to be read, and which will return the property
      */
-    public On<Property> getProperty( final Name name ) {
-        return new On<Property>() {
+    public OnMultiple<Property> getProperty( final Name name ) {
+        CheckArg.isNotNull(name, "name");
+        return new OnMultiple<Property>() {
             public Property on( String path ) {
                 return on(Location.create(createPath(path)));
             }
@@ -2092,10 +2093,237 @@
             public Property on( Location at ) {
                 return requests.readProperty(at, getCurrentWorkspaceName(), name).getProperty();
             }
+
+            public Map<Location, Property> on( Collection<Location> locations ) {
+                CheckArg.isNotNull(locations, "locations");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                for (Location location : locations) {
+                    requests.add(new ReadPropertyRequest(location, workspace, name));
+                }
+                return execute(requests);
+            }
+
+            public Map<Location, Property> on( Location first,
+                                               Location... additional ) {
+                CheckArg.isNotNull(first, "first");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                requests.add(new ReadPropertyRequest(first, workspace, name));
+                for (Location location : additional) {
+                    requests.add(new ReadPropertyRequest(location, workspace, name));
+                }
+                return execute(requests);
+            }
+
+            public Map<Location, Property> on( String first,
+                                               String... additional ) {
+                CheckArg.isNotNull(first, "first");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                requests.add(new ReadPropertyRequest(Location.create(createPath(first)), workspace, name));
+                for (String path : additional) {
+                    requests.add(new ReadPropertyRequest(Location.create(createPath(path)), workspace, name));
+                }
+                return execute(requests);
+            }
+
+            public Map<Location, Property> on( Path first,
+                                               Path... additional ) {
+                CheckArg.isNotNull(first, "first");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                requests.add(new ReadPropertyRequest(Location.create(first), workspace, name));
+                for (Path path : additional) {
+                    requests.add(new ReadPropertyRequest(Location.create(path), workspace, name));
+                }
+                return execute(requests);
+            }
+
+            public Map<Location, Property> on( UUID first,
+                                               UUID... additional ) {
+                CheckArg.isNotNull(first, "first");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                requests.add(new ReadPropertyRequest(Location.create(first), workspace, name));
+                for (UUID uuid : additional) {
+                    requests.add(new ReadPropertyRequest(Location.create(uuid), workspace, name));
+                }
+                return execute(requests);
+            }
+
+            protected Map<Location, Property> execute( List<ReadPropertyRequest> requests ) {
+                // Create a composite request ...
+                Request composite = CompositeRequest.with(requests);
+                Graph.this.execute(composite);
+                Map<Location, Property> results = new HashMap<Location, Property>();
+                for (ReadPropertyRequest request : requests) {
+                    Property property = request.getProperty();
+                    Location location = request.getActualLocationOfNode();
+                    results.put(location, property);
+                }
+                return results;
+            }
         };
     }
 
     /**
+     * Request that the properties with the given names be read on the node defined via the <code>on(...)</code> method on the
+     * returned {@link On} object. Once the location is specified, the {@link Property property} is read and then returned.
+     * 
+     * @param names the name of the property that are to be read
+     * @return the object that is used to specified the node whose properties are to be read, and which will return the map of
+     *         properties keyed by their name; never null
+     */
+    public OnMultiple<Map<Name, Property>> getProperties( final Name... names ) {
+        return new OnMultiple<Map<Name, Property>>() {
+            public Map<Name, Property> on( String path ) {
+                return on(Location.create(createPath(path)));
+            }
+
+            public Map<Name, Property> on( Path path ) {
+                return on(Location.create(path));
+            }
+
+            public Map<Name, Property> on( Property idProperty ) {
+                return on(Location.create(idProperty));
+            }
+
+            public Map<Name, Property> on( Property firstIdProperty,
+                                           Property... additionalIdProperties ) {
+                return on(Location.create(firstIdProperty, additionalIdProperties));
+            }
+
+            public Map<Name, Property> on( Iterable<Property> idProperties ) {
+                return on(Location.create(idProperties));
+            }
+
+            public Map<Name, Property> on( UUID uuid ) {
+                return on(Location.create(uuid));
+            }
+
+            public Map<Name, Property> on( Location at ) {
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                for (Name propertyName : names) {
+                    requests.add(new ReadPropertyRequest(at, workspace, propertyName));
+                }
+                // Create a composite request ...
+                Request composite = CompositeRequest.with(requests);
+                Graph.this.execute(composite);
+                Map<Name, Property> results = new HashMap<Name, Property>();
+                for (ReadPropertyRequest request : requests) {
+                    Property property = request.getProperty();
+                    results.put(property.getName(), property);
+                }
+                return results;
+            }
+
+            public Map<Location, Map<Name, Property>> on( Collection<Location> locations ) {
+                CheckArg.isNotNull(locations, "locations");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                for (Location location : locations) {
+                    if (location == null) continue;
+                    for (Name propertyName : names) {
+                        if (propertyName == null) continue;
+                        requests.add(new ReadPropertyRequest(location, workspace, propertyName));
+                    }
+                }
+                return execute(requests);
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.Graph.OnMultiple#on(org.jboss.dna.graph.Location, org.jboss.dna.graph.Location[])
+             */
+            public Map<Location, Map<Name, Property>> on( Location first,
+                                                          Location... additional ) {
+                CheckArg.isNotNull(first, "first");
+                final List<ReadPropertyRequest> requests = new LinkedList<ReadPropertyRequest>();
+                String workspace = getCurrentWorkspaceName();
+                for (Location location : additional) {
+                    if (location == null) continue;
+                    for (Name propertyName : names) {
+                        if (propertyName == null) continue;
+                        requests.add(new ReadPropertyRequest(first, workspace, propertyName));
+                        requests.add(new ReadPropertyRequest(location, workspace, propertyName));
+                    }
+                }
+                return execute(requests);
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.Graph.OnMultiple#on(org.jboss.dna.graph.property.Path,
+             *      org.jboss.dna.graph.property.Path[])
+             */
+            public Map<Location, Map<Name, Property>> on( Path first,
+                                                          Path... additional ) {
+                CheckArg.isNotNull(first, "first");
+                List<Location> locations = new LinkedList<Location>();
+                locations.add(Location.create(first));
+                for (Path path : additional) {
+                    if (path != null) locations.add(Location.create(path));
+                }
+                return on(locations);
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.Graph.OnMultiple#on(java.lang.String, java.lang.String[])
+             */
+            public Map<Location, Map<Name, Property>> on( String first,
+                                                          String... additional ) {
+                CheckArg.isNotNull(first, "first");
+                List<Location> locations = new LinkedList<Location>();
+                locations.add(Location.create(createPath(first)));
+                for (String path : additional) {
+                    if (path != null) locations.add(Location.create(createPath(path)));
+                }
+                return on(locations);
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.Graph.OnMultiple#on(java.util.UUID, java.util.UUID[])
+             */
+            public Map<Location, Map<Name, Property>> on( UUID first,
+                                                          UUID... additional ) {
+                CheckArg.isNotNull(first, "first");
+                List<Location> locations = new LinkedList<Location>();
+                locations.add(Location.create(first));
+                for (UUID uuid : additional) {
+                    if (uuid != null) locations.add(Location.create(uuid));
+                }
+                return on(locations);
+            }
+
+            protected Map<Location, Map<Name, Property>> execute( List<ReadPropertyRequest> requests ) {
+                // Create a composite request ...
+                Request composite = CompositeRequest.with(requests);
+                Graph.this.execute(composite);
+                Map<Location, Map<Name, Property>> results = new HashMap<Location, Map<Name, Property>>();
+                for (ReadPropertyRequest request : requests) {
+                    Property property = request.getProperty();
+                    Location location = request.getActualLocationOfNode();
+                    Map<Name, Property> properties = results.get(location);
+                    if (properties == null) {
+                        properties = new HashMap<Name, Property>();
+                        results.put(location, properties);
+                    }
+                    properties.put(property.getName(), property);
+                }
+                return results;
+            }
+        };
+    }
+
+    /**
      * Request to read the node with the supplied UUID.
      * 
      * @param uuid the UUID of the node that is to be read
@@ -2395,8 +2623,6 @@
      * Interface for creating multiple requests to perform various operations. Note that all the requests are accumulated until
      * the {@link #execute()} method is called. The results of all the operations are then available in the {@link Results} object
      * returned by the {@link #execute()}.
-     * 
-     * @author Randall Hauch
      */
     @Immutable
     public final class Batch implements Executable<Node> {
@@ -4259,7 +4485,6 @@
     /**
      * A interface used to execute the accumulated {@link Batch requests}.
      * 
-     * @author Randall Hauch
      * @param <NodeType> the type of node that is returned
      */
     public interface Executable<NodeType extends Node> {
@@ -4281,7 +4506,6 @@
      * A interface that can be used to finish the current request and start another.
      * 
      * @param <Next> the interface that will be used to start another request
-     * @author Randall Hauch
      */
     public interface Conjunction<Next> {
         /**
@@ -4296,7 +4520,6 @@
      * A component that defines the location into which a node should be copied or moved.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface Into<Next> {
         /**
@@ -4376,7 +4599,6 @@
      * or copied node as the last child of the new parent.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface Before<Next> {
         /**
@@ -4480,7 +4702,6 @@
      * A component that defines the location to which a node should be copied or moved.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface To<Next> {
         /**
@@ -4555,7 +4776,6 @@
      * A component that defines a new name for a node.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface AsName<Next> {
         /**
@@ -4579,7 +4799,6 @@
      * A interface that is used to add more locations that are to be copied/moved.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface And<Next> {
         /**
@@ -4647,7 +4866,6 @@
      * The interface for defining additional nodes to be moved and the parent into which the node(s) are to be moved.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface Move<Next> extends AsName<Into<Next>>, Into<Next>, Before<Next>, And<Move<Next>> {
     }
@@ -4659,7 +4877,6 @@
      * is to be placed, which will assume the new copy will have the same name as the original.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface Copy<Next> extends FromWorkspace<CopyTarget<Next>>, CopyTarget<Next>, And<Copy<Next>> {
     }
@@ -4780,7 +4997,6 @@
      * The interface for defining additional properties on a new node.
      * 
      * @param <Next> The interface that is to be returned when this create request is completed
-     * @author Randall Hauch
      */
     public interface Create<Next> extends Conjunction<Next> {
         /**
@@ -4929,7 +5145,6 @@
      * The interface for defining additional properties on a new node.
      * 
      * @param <Next> The interface that is to be returned when this create request is completed
-     * @author Randall Hauch
      */
     public interface CreateAt<Next> extends Conjunction<Next> {
         /**
@@ -5060,7 +5275,6 @@
      * The interface for defining the node upon which a request operates.
      * 
      * @param <Next> The interface that is to be returned when the request is completed
-     * @author Randall Hauch
      */
     public interface On<Next> {
         /**
@@ -5125,10 +5339,64 @@
     }
 
     /**
+     * The interface for defining the node upon which a request operates, including a method that accepts multiple locations.
+     * 
+     * @param <Next> The interface that is to be returned when the request is completed
+     */
+    public interface OnMultiple<Next> extends On<Next> {
+        /**
+         * Specify the location of each node upon which the requests are to operate.
+         * 
+         * @param to the locations
+         * @return the interface for additional requests or actions
+         */
+        Map<Location, Next> on( Collection<Location> to );
+
+        /**
+         * Specify the location of each node upon which the requests are to operate.
+         * 
+         * @param firstTo the first location
+         * @param additional the additional location
+         * @return the interface for additional requests or actions
+         */
+        Map<Location, Next> on( Location firstTo,
+                                Location... additional );
+
+        /**
+         * Specify the path of each node upon which the requests are to operate.
+         * 
+         * @param firstPath the first path
+         * @param additional the additional path
+         * @return the interface for additional requests or actions
+         */
+        Map<Location, Next> on( String firstPath,
+                                String... additional );
+
+        /**
+         * Specify the path of each node upon which the requests are to operate.
+         * 
+         * @param firstPath the first path
+         * @param additional the additional path
+         * @return the interface for additional requests or actions
+         */
+        Map<Location, Next> on( Path firstPath,
+                                Path... additional );
+
+        /**
+         * Specify the UUID of each node upon which the requests are to operate.
+         * 
+         * @param firstPath the first UUID of the node
+         * @param additional the additional UUIDs
+         * @return the interface for additional requests or actions
+         */
+        Map<Location, Next> on( UUID firstPath,
+                                UUID... additional );
+    }
+
+    /**
      * The interface for defining the node upon which a request operates.
      * 
      * @param <Next> The interface that is to be returned when the request is completed
-     * @author Randall Hauch
      */
     public interface Of<Next> {
         /**
@@ -5196,7 +5464,6 @@
      * The interface for defining the node upon which which a request operates.
      * 
      * @param <Next> The interface that is to be returned when the request is completed
-     * @author Randall Hauch
      */
     public interface At<Next> {
         /**
@@ -5267,7 +5534,6 @@
      * the {@link BlockOfChildren block size and parent}.
      * 
      * @param <Next>
-     * @author Randall Hauch
      */
     public interface Children<Next> extends Of<Next> {
         /**
@@ -5284,7 +5550,6 @@
      * {@link #startingAfter(Location) after a previous sibling}.
      * 
      * @param <Next>
-     * @author Randall Hauch
      */
     public interface BlockOfChildren<Next> {
         /**
@@ -5364,7 +5629,6 @@
      * The interface for defining the node under which which a request operates.
      * 
      * @param <Next> The interface that is to be returned when the request is completed
-     * @author Randall Hauch
      */
     public interface Under<Next> {
         /**
@@ -5587,7 +5851,6 @@
      * A component used to set the values on a property.
      * 
      * @param <Next> the next command
-     * @author Randall Hauch
      */
     public interface SetValues<Next> extends On<SetValuesTo<Next>>, SetValuesTo<On<Next>> {
     }
@@ -5596,7 +5859,6 @@
      * A component used to set the values on a property.
      * 
      * @param <Next>
-     * @author Randall Hauch
      */
     public interface SetValuesTo<Next> {
 
@@ -5842,7 +6104,6 @@
      * A component that defines a node that is to be created.
      * 
      * @param <Next> The interface that is to be returned to complete the create request
-     * @author Randall Hauch
      */
     public interface CreateNode<Next> {
         /**
@@ -5880,7 +6141,6 @@
      * A component that defines a node that is to be created.
      * 
      * @param <Next> The interface that is to be returned to complete the create request
-     * @author Randall Hauch
      */
     public interface CreateNodeNamed<Next> {
         /**
@@ -5904,7 +6164,6 @@
      * A component that defines the location into which a node should be copied or moved.
      * 
      * @param <Next> The interface that is to be returned when this request is completed
-     * @author Randall Hauch
      */
     public interface ImportInto<Next> {
         /**
@@ -7183,7 +7442,6 @@
      * A set of nodes returned from a {@link Graph graph}, with methods to access the properties and children of the nodes in the
      * result. The {@link #iterator()} method can be used to iterate all over the nodes in the result.
      * 
-     * @author Randall Hauch
      * @param <NodeType> the type of node that tis results deals with
      */
     @Immutable

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-10-30 21:34:28 UTC (rev 1316)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-11-09 19:29:50 UTC (rev 1317)
@@ -32,6 +32,7 @@
 import static org.junit.matchers.JUnitMatchers.hasItems;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
@@ -1112,6 +1113,64 @@
     }
 
     // ----------------------------------------------------------------------------------------------------------------
+    // Read set number of properties on multiple nodes ...
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldReadOnePropertyOnMultipleNodes() {
+        List<Location> locations = new ArrayList<Location>();
+        locations.add(Location.create(createPath("/x/y/a")));
+        locations.add(Location.create(createPath("/x/y/b")));
+        locations.add(Location.create(createPath("/x/y/c")));
+        for (Location location : locations) {
+            Property prop1 = context.getPropertyFactory().create(validName, "1");
+            Property prop2 = context.getPropertyFactory().create(createName("otherName"), "2");
+            setPropertiesToReadOn(location, prop1, prop2);
+        }
+
+        Map<Location, Property> propertiesByLocation = graph.getProperty(validName).on(locations);
+        assertThat(numberOfExecutions, is(1));
+        extractRequestsFromComposite();
+        for (Location location : locations) {
+            Property prop = this.properties.get(location).iterator().next();
+            assertNextRequestReadProperty(location, prop);
+            assertThat(propertiesByLocation.get(location), is(prop));
+        }
+        assertNoMoreRequests();
+    }
+
+    @Test
+    public void shouldReadMultiplePropertiesOnMultipleNodes() {
+        List<Location> locations = new ArrayList<Location>();
+        locations.add(Location.create(createPath("/x/y/a")));
+        locations.add(Location.create(createPath("/x/y/b")));
+        locations.add(Location.create(createPath("/x/y/c")));
+        Name name1 = createName("name1");
+        Name name2 = createName("name2");
+        for (Location location : locations) {
+            Property prop1 = context.getPropertyFactory().create(name1, "1");
+            Property prop2 = context.getPropertyFactory().create(name2, "2");
+            setPropertiesToReadOn(location, prop1, prop2);
+        }
+        Map<Location, Map<Name, Property>> propertiesByLocation = graph.getProperties(name1, name2).on(locations);
+        assertThat(numberOfExecutions, is(1));
+        extractRequestsFromComposite();
+        for (Location location : locations) {
+            Map<Name, Property> expectedProps = new HashMap<Name, Property>();
+            for (Property prop : this.properties.get(location)) {
+                expectedProps.put(prop.getName(), prop);
+            }
+            Property prop1 = expectedProps.get(name1);
+            Property prop2 = expectedProps.get(name2);
+            assertNextRequestReadProperty(location, prop1);
+            assertNextRequestReadProperty(location, prop2);
+            assertThat(propertiesByLocation.get(location).get(name1), is(prop1));
+            assertThat(propertiesByLocation.get(location).get(name2), is(prop2));
+        }
+        assertNoMoreRequests();
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
     // Implementation of RepositoryConnection and RequestProcessor for tests
     // ----------------------------------------------------------------------------------------------------------------
 
@@ -1148,7 +1207,7 @@
             if (request.into().hasPath()) {
                 Name childName = request.desiredName();
                 if (childName == null) childName = request.desiredSegment().getName();
-                
+
                 Path childPath = context.getValueFactories().getPathFactory().create(request.into().getPath(), childName);
                 Location newChild = actualLocationOf(Location.create(childPath));
                 // Just update the actual location


From dna-commits at lists.jboss.org  Mon Nov 16 18:23:02 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Nov 2009 18:23:02 -0500
Subject: [dna-commits] DNA SVN: r1318 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/validate
	and 2 other directories.
Message-ID: <200911162323.nAGNN2JI021565@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-16 18:23:02 -0500 (Mon, 16 Nov 2009)
New Revision: 1318

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
Log:
DNA-467 Changed how the Queryable and QueryEngine use Schemata. Before, the schemata instance was passed into the engine, and used for all queries. But this meant that the schemata instance could change the Table objects it returns.  This would be more difficult to implement than if a different Schemata were passed with each query, since that Schemata instance can be a reflection of the tables at the time the query is implemented.  And, Schemata can then be made to be immutable, not only simplifying the implementation but also ensuring that the schema information doesn't change during the processing of a query.

So, the Queryable and QueryEngine were changed to always pass a Schemata in with each QueryCommand.  This rippled down to the SearchEngine, but this also cleaned things up a bit there, too.

It also allows different Schemata instances to be used for different languages (if that would ever make sense).

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -54,10 +54,20 @@
 @ThreadSafe
 public class QueryEngine implements Queryable {
 
+    /**
+     * A {@link Schemata} implementation that always returns null, meaning the table does not exist.
+     */
+    private static final Schemata DEFAULT_SCHEMATA = new Schemata() {
+        public Table getTable( SelectorName name ) {
+            // This won't allow the query engine to do anything (or much of anything),
+            // but it is legal and will result in meaningful problems
+            return null;
+        }
+    };
+
     private final Planner planner;
     private final Optimizer optimizer;
     private final Processor processor;
-    private final Schemata schemata;
 
     /**
      * Create a new query engine given the {@link Planner planner}, {@link Optimizer optimizer}, {@link Processor processor}, and
@@ -68,49 +78,43 @@
      * @param optimizer the optimizer that should be used to optimize the canonical query plan; may be null if the
      *        {@link RuleBasedOptimizer} should be used
      * @param processor the processor implementation that should be used to process the planned query and return the results
-     * @param schemata the schemata implementation, or null if an empty schema should be used (resulting in errors when named
-     *        tables are queried)
      * @throws IllegalArgumentException if the processor reference is null
      */
     public QueryEngine( Planner planner,
                         Optimizer optimizer,
-                        Processor processor,
-                        Schemata schemata ) {
+                        Processor processor ) {
         CheckArg.isNotNull(processor, "processor");
         this.planner = planner != null ? planner : new CanonicalPlanner();
         this.optimizer = optimizer != null ? optimizer : new RuleBasedOptimizer();
         this.processor = processor;
-        this.schemata = schemata != null ? schemata : new Schemata() {
-            public Table getTable( SelectorName name ) {
-                // This won't allow the query engine to do anything (or much of anything),
-                // but it is legal and will result in meaningful problems
-                return null;
-            }
-        };
     }
 
     /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand)
+     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata)
      */
     public QueryResults execute( ExecutionContext context,
-                                 QueryCommand query ) {
-        return execute(context, query, new PlanHints());
+                                 QueryCommand query,
+                                 Schemata schemata ) {
+        return execute(context, query, schemata, new PlanHints());
     }
 
     /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.plan.PlanHints)
+     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata,
+     *      org.jboss.dna.graph.query.plan.PlanHints)
      */
     public QueryResults execute( ExecutionContext context,
                                  QueryCommand query,
+                                 Schemata schemata,
                                  PlanHints hints ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(query, "query");
+        if (schemata == null) schemata = DEFAULT_SCHEMATA;
         QueryContext queryContext = new QueryContext(context, hints, schemata);
 
         // Create the canonical plan ...

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -26,6 +26,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
  * An interface defining the ability to submit a query and obtain results.
@@ -37,22 +38,26 @@
      * 
      * @param context the context in which the query should be executed
      * @param query the query that is to be executed
+     * @param schemata the schemata that should be used to validate the query
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
     QueryResults execute( ExecutionContext context,
-                          QueryCommand query );
+                          QueryCommand query,
+                          Schemata schemata );
 
     /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
      * @param context the context in which the query should be executed
      * @param query the query that is to be executed
+     * @param schemata the schemata that should be used to validate the query
      * @param hints the hints for the execution; may be null if there are no hints
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
     QueryResults execute( ExecutionContext context,
                           QueryCommand query,
+                          Schemata schemata,
                           PlanHints hints );
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
@@ -34,13 +35,13 @@
 /**
  * The interface used to access the structure being queried and validate a query.
  */
+ at Immutable
 public interface Schemata {
 
     /**
      * Get the information for the table or view with the supplied name within this schema.
      * <p>
-     * The resulting definition is immutable, though subsequent calls to this method with the same argument may result in a
-     * different definition.
+     * The resulting definition is immutable.
      * </p>
      * 
      * @param name the table or view name; may not be null

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -40,7 +40,7 @@
  * A set of index readers and writers.
  */
 @NotThreadSafe
-final class IndexContext {
+public final class IndexContext {
 
     private final ExecutionContext context;
     private final Directory pathsIndexDirectory;

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -34,12 +34,13 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.request.ChangeRequest;
 
 /**
  * Interface defining the behaviors associated with indexing graph content.
  */
-interface IndexingStrategy {
+public interface IndexingStrategy {
 
     int getChangeCountForAutomaticOptimization();
 
@@ -108,14 +109,18 @@
                        List<Location> results ) throws IOException, ParseException;
 
     /**
-     * Perform a query of the content.
+     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
+     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
+     * writers) that should be used.
      * 
      * @param query the query; never null
+     * @param schemata the definition of the tables used in the query; never null
      * @param indexes the set of index readers and writers; never null
      * @return the results of the query
      * @throws IOException if there is a problem indexing or using the writers
      * @throws ParseException if there is a problem parsing the query
      */
     QueryResults performQuery( QueryCommand query,
+                               Schemata schemata,
                                IndexContext indexes ) throws IOException, ParseException;
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -49,14 +49,15 @@
 
     private QueryEngine engine;
 
-    public LuceneQueryEngine( Schemata schemata ) {
-        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata);
+    public LuceneQueryEngine() {
+        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor());
     }
 
     /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
      * @param query the query that is to be executed
+     * @param schemata the schemata that defines the tables used in the query
      * @param indexes the indexes that should be used to execute the query; never null
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
@@ -64,8 +65,9 @@
      * @throws ParseException if there is a problem parsing the query
      */
     public QueryResults execute( QueryCommand query,
+                                 Schemata schemata,
                                  IndexContext indexes ) throws IOException, ParseException {
-        return engine.execute(indexes.context(), query, new PlanHints());
+        return engine.execute(indexes.context(), query, schemata, new PlanHints());
     }
 
     /**

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -45,6 +45,7 @@
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.request.ChangeRequest;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
 
@@ -293,13 +294,17 @@
      * 
      * @param workspaceName the name of the workspace
      * @param query the query that is to be executed, in the form of the Abstract Query Model
+     * @param schemata the definition of the tables and views that can be used in the query; may not be null
      * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
+     * @throws IllegalArgumentException if the context, query, or schemata references are null
      */
     public QueryResults execute( String workspaceName,
-                                 QueryCommand query ) {
+                                 QueryCommand query,
+                                 Schemata schemata ) {
         CheckArg.isNotNull(workspaceName, "workspaceName");
-        return getWorkspaceEngine(workspaceName).execute(query);
+        CheckArg.isNotNull(query, "query");
+        CheckArg.isNotNull(schemata, "schemata");
+        return getWorkspaceEngine(workspaceName).execute(query, schemata);
     }
 
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -124,22 +124,19 @@
      * Create a new indexing strategy instance that does not support queries.
      */
     public StoreLittleIndexingStrategy() {
-        this(null, null);
+        this(null);
     }
 
     /**
      * Create a new indexing strategy instance.
      * 
-     * @param schemata the schemata that defines the structure that can be queried; may be null if queries are not going to be
-     *        used
      * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
      *        rules} are to be used
      */
-    public StoreLittleIndexingStrategy( Schemata schemata,
-                                        IndexingRules rules ) {
+    public StoreLittleIndexingStrategy( IndexingRules rules ) {
         this.rules = rules != null ? rules : DEFAULT_RULES;
         this.logger = Logger.getLogger(getClass());
-        this.queryEngine = new LuceneQueryEngine(schemata);
+        this.queryEngine = new LuceneQueryEngine();
     }
 
     /**
@@ -351,11 +348,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.search.IndexingStrategy#performQuery(QueryCommand, IndexContext)
+     * @see org.jboss.dna.search.IndexingStrategy#performQuery(org.jboss.dna.graph.query.model.QueryCommand,
+     *      org.jboss.dna.graph.query.validate.Schemata, org.jboss.dna.search.IndexContext)
      */
     public QueryResults performQuery( QueryCommand query,
+                                      Schemata schemata,
                                       IndexContext indexes ) throws IOException, ParseException {
-        return this.queryEngine.execute(query, indexes);
+        return this.queryEngine.execute(query, schemata, indexes);
     }
 
     /**

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -48,6 +48,7 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.request.ChangeRequest;
 
 /**
@@ -252,11 +253,13 @@
      * representation of the query.
      * 
      * @param query the query that is to be executed, in the form of the Abstract Query Model
+     * @param schemata the definition of the tables available for the query; may not be null
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
-    public QueryResults execute( QueryCommand query ) {
-        return execute(false, queryContent(query)).getResults();
+    public QueryResults execute( QueryCommand query,
+                                 Schemata schemata ) {
+        return execute(false, queryContent(query, schemata)).getResults();
     }
 
     /**
@@ -540,14 +543,16 @@
      * Create an activity that will perform a query against the index.
      * 
      * @param query the query to be performed; may not be null
+     * @param schemata the definition of the tables being used in the query; may not be null
      * @return the activity that will perform the work
      */
-    protected Query queryContent( final QueryCommand query ) {
+    protected Query queryContent( final QueryCommand query,
+                                  final Schemata schemata ) {
         return new Query() {
             private QueryResults results = null;
 
             public void execute( IndexContext indexes ) throws IOException, ParseException {
-                results = strategy().performQuery(query, indexes);
+                results = strategy().performQuery(query, schemata, indexes);
             }
 
             public String messageFor( Throwable error ) {

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -27,7 +27,6 @@
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -35,7 +34,6 @@
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +49,6 @@
     private RepositoryConnectionFactory connectionFactory;
     private DirectoryConfiguration directoryFactory;
     private IndexingStrategy indexingStrategy;
-    private Schemata schemata;
     private Graph content;
 
     @Before
@@ -84,14 +81,11 @@
             }
         };
 
-        // Set up the schemata for the queries ...
-        schemata = mock(Schemata.class);
-
         // Set up the indexing strategy ...
         IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
                                            .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
                                            .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(schemata, rules);
+        indexingStrategy = new StoreLittleIndexingStrategy(rules);
 
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-09 19:29:50 UTC (rev 1317)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
@@ -26,7 +26,6 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
 import java.util.List;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -36,7 +35,6 @@
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -51,7 +49,6 @@
     private DirectoryConfiguration directoryFactory;
     private IndexingStrategy indexingStrategy;
     private Graph content;
-    private Schemata schemata;
 
     @Before
     public void beforeEach() throws Exception {
@@ -76,14 +73,11 @@
             }
         };
 
-        // Set up the schemata for the queries ...
-        schemata = mock(Schemata.class);
-
         // Set up the indexing strategy ...
         IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
                                            .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
                                            .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(schemata, rules);
+        indexingStrategy = new StoreLittleIndexingStrategy(rules);
 
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();


From dna-commits at lists.jboss.org  Mon Nov 16 18:24:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Nov 2009 18:24:06 -0500
Subject: [dna-commits] DNA SVN: r1319 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/process and
	6 other directories.
Message-ID: <200911162324.nAGNO6Ov021694@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-16 18:24:06 -0500 (Mon, 16 Nov 2009)
New Revision: 1319

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
Log:
DNA-467 Additional refactoring to move toward a working search engine.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -26,6 +26,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.common.collection.SimpleProblems;
 import org.jboss.dna.common.util.CheckArg;
@@ -35,64 +36,120 @@
 import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
- * 
+ * An immutable context in which queries are to be executed. Each query context defines the information that is available during
+ * query execution.
  */
-public final class QueryContext {
+ at Immutable
+public class QueryContext {
     private final ExecutionContext context;
     private final PlanHints hints;
     private final Schemata schemata;
     private final Problems problems;
     private final Map<String, Object> variables;
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @param variables the mapping of variables and values, or null if there are no such variables
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
+                         Schemata schemata,
                          PlanHints hints,
-                         Schemata schemata,
                          Problems problems,
                          Map<String, Object> variables ) {
         CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(schemata, "schemata");
         this.context = context;
         this.hints = hints != null ? hints : new PlanHints();
         this.schemata = schemata;
         this.problems = problems != null ? problems : new SimpleProblems();
         this.variables = variables != null ? Collections.<String, Object>unmodifiableMap(new HashMap<String, Object>(variables)) : Collections.<String, Object>emptyMap();
+        assert this.context != null;
+        assert this.hints != null;
+        assert this.schemata != null;
+        assert this.problems != null;
+        assert this.variables != null;
     }
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
+                         Schemata schemata,
                          PlanHints hints,
-                         Schemata schemata,
                          Problems problems ) {
-        this(context, hints, schemata, problems, null);
+        this(context, schemata, hints, problems, null);
     }
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
-                         PlanHints hints,
+                         Schemata schemata,
+                         PlanHints hints ) {
+        this(context, schemata, hints, null, null);
+    }
+
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
+    public QueryContext( ExecutionContext context,
                          Schemata schemata ) {
-        this(context, hints, schemata, null, null);
+        this(context, schemata, null, null, null);
     }
 
     /**
-     * @return context
+     * Get the execution context available to this query context.
+     * 
+     * @return the execution context; never null
      */
     public final ExecutionContext getExecutionContext() {
         return context;
     }
 
     /**
-     * @return hints
+     * Get the plan hints.
+     * 
+     * @return the plan hints; never null
      */
     public final PlanHints getHints() {
         return hints;
     }
 
     /**
-     * @return problems
+     * Get the problem container used by this query context. Any problems that have been encountered will be accumlated in this
+     * container.
+     * 
+     * @return the problem container; never null
      */
     public final Problems getProblems() {
         return problems;
     }
 
     /**
-     * @return schemata
+     * Get the definition of the tables available within this query context.
+     * 
+     * @return the schemata; never null
      */
     public Schemata getSchemata() {
         return schemata;
@@ -101,10 +158,66 @@
     /**
      * Get the variables that are to be substituted into the {@link BindVariableName} used in the query.
      * 
-     * @return immutable map of variable values keyed by their name; never null
+     * @return immutable map of variable values keyed by their name; never null but possibly empty
      */
     public Map<String, Object> getVariables() {
         return variables;
     }
 
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied execution context.
+     * 
+     * @param context the execution context that should be used in the new query context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the execution context reference is null
+     */
+    public QueryContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied schemata.
+     * 
+     * @param schemata the schemata that should be used in the new context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the schemata reference is null
+     */
+    public QueryContext with( Schemata schemata ) {
+        CheckArg.isNotNull(schemata, "schemata");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied hints.
+     * 
+     * @param hints the hints that should be used in the new context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the hints reference is null
+     */
+    public QueryContext with( PlanHints hints ) {
+        CheckArg.isNotNull(hints, "hints");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied problem container.
+     * 
+     * @param problems the problems that should be used in the new context; may be null if a new problem container should be used
+     * @return the new context; never null
+     */
+    public QueryContext with( Problems problems ) {
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied variables.
+     * 
+     * @param variables the variables that should be used in the new context; may be null if there are no such variables
+     * @return the new context; never null
+     */
+    public QueryContext with( Map<String, Object> variables ) {
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -27,18 +27,15 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
 import org.jboss.dna.graph.query.model.FullTextSearch;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.Planner;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
@@ -54,17 +51,6 @@
 @ThreadSafe
 public class QueryEngine implements Queryable {
 
-    /**
-     * A {@link Schemata} implementation that always returns null, meaning the table does not exist.
-     */
-    private static final Schemata DEFAULT_SCHEMATA = new Schemata() {
-        public Table getTable( SelectorName name ) {
-            // This won't allow the query engine to do anything (or much of anything),
-            // but it is legal and will result in meaningful problems
-            return null;
-        }
-    };
-
     private final Planner planner;
     private final Optimizer optimizer;
     private final Processor processor;
@@ -92,42 +78,25 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata)
+     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand)
      */
-    public QueryResults execute( ExecutionContext context,
-                                 QueryCommand query,
-                                 Schemata schemata ) {
-        return execute(context, query, schemata, new PlanHints());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata,
-     *      org.jboss.dna.graph.query.plan.PlanHints)
-     */
-    public QueryResults execute( ExecutionContext context,
-                                 QueryCommand query,
-                                 Schemata schemata,
-                                 PlanHints hints ) {
+    public QueryResults execute( QueryContext context,
+                                 QueryCommand query ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(query, "query");
-        if (schemata == null) schemata = DEFAULT_SCHEMATA;
-        QueryContext queryContext = new QueryContext(context, hints, schemata);
 
         // Create the canonical plan ...
         long start = System.nanoTime();
-        PlanNode plan = planner.createPlan(queryContext, query);
+        PlanNode plan = planner.createPlan(context, query);
         long duration = System.nanoTime() - start;
         Statistics stats = new Statistics(duration);
 
         QueryResultColumns resultColumns = QueryResultColumns.empty();
-        if (!queryContext.getProblems().hasErrors()) {
+        if (!context.getProblems().hasErrors()) {
             // Optimize the plan ...
             start = System.nanoTime();
-            PlanNode optimizedPlan = optimizer.optimize(queryContext, plan);
+            PlanNode optimizedPlan = optimizer.optimize(context, plan);
             duration = System.nanoTime() - start;
             stats = stats.withOptimizationTime(duration);
 
@@ -137,11 +106,11 @@
             duration = System.nanoTime() - start;
             stats = stats.withOptimizationTime(duration);
 
-            if (!queryContext.getProblems().hasErrors()) {
+            if (!context.getProblems().hasErrors()) {
                 // Execute the plan ...
                 try {
                     start = System.nanoTime();
-                    return processor.execute(queryContext, query, stats, optimizedPlan);
+                    return processor.execute(context, query, stats, optimizedPlan);
                 } finally {
                     duration = System.nanoTime() - start;
                     stats = stats.withOptimizationTime(duration);
@@ -149,7 +118,7 @@
             }
         }
         // There were problems somewhere ...
-        return new org.jboss.dna.graph.query.process.QueryResults(queryContext, query, resultColumns, stats);
+        return new org.jboss.dna.graph.query.process.QueryResults(context, query, resultColumns, stats);
     }
 
     protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -23,10 +23,7 @@
  */
 package org.jboss.dna.graph.query;
 
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
  * An interface defining the ability to submit a query and obtain results.
@@ -38,26 +35,9 @@
      * 
      * @param context the context in which the query should be executed
      * @param query the query that is to be executed
-     * @param schemata the schemata that should be used to validate the query
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
-    QueryResults execute( ExecutionContext context,
-                          QueryCommand query,
-                          Schemata schemata );
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param context the context in which the query should be executed
-     * @param query the query that is to be executed
-     * @param schemata the schemata that should be used to validate the query
-     * @param hints the hints for the execution; may be null if there are no hints
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     */
-    QueryResults execute( ExecutionContext context,
-                          QueryCommand query,
-                          Schemata schemata,
-                          PlanHints hints );
+    QueryResults execute( QueryContext context,
+                          QueryCommand query );
 }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,120 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.process;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.AllNodes;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.Limit;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * A reusable base class for {@link ProcessingComponent} implementations that does everything except obtain the correct
+ * {@link Location} objects for the query results.
+ */
+public abstract class AbstractAccessComponent extends ProcessingComponent {
+
+    protected final PlanNode accessNode;
+    protected final SelectorName sourceName;
+    protected final List<Column> projectedColumns;
+    protected final Constraint constraint;
+    protected final Limit limit;
+
+    protected AbstractAccessComponent( QueryContext context,
+                                       Columns columns,
+                                       PlanNode accessNode ) {
+        super(context, columns);
+        this.accessNode = accessNode;
+
+        // Find the table name; should be
+        PlanNode source = accessNode.findAtOrBelow(Type.SOURCE);
+        if (source != null) {
+            this.sourceName = source.getProperty(Property.SOURCE_NAME, SelectorName.class);
+            if (!AllNodes.ALL_NODES_NAME.equals(this.sourceName)) {
+                throw new IllegalArgumentException();
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        // Find the project ...
+        PlanNode project = accessNode.findAtOrBelow(Type.PROJECT);
+        if (project != null) {
+            List<Column> projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+            if (projectedColumns != null) {
+                this.projectedColumns = projectedColumns;
+            } else {
+                // Get the columns from the source columns ...
+                List<Schemata.Column> schemataColumns = source.getPropertyAsList(Property.SOURCE_COLUMNS, Schemata.Column.class);
+                this.projectedColumns = new ArrayList<Column>(schemataColumns.size());
+                NameFactory nameFactory = context.getExecutionContext().getValueFactories().getNameFactory();
+                for (Schemata.Column schemataColumn : schemataColumns) {
+                    String columnName = schemataColumn.getName();
+                    // PropertyType type = schemataColumn.getPropertyType();
+                    Name propertyName = nameFactory.create(columnName);
+                    Column column = new Column(sourceName, propertyName, columnName);
+                    this.projectedColumns.add(column);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        // Add the criteria ...
+        Constraint constraint = null;
+        for (PlanNode select : accessNode.findAllAtOrBelow(Type.SELECT)) {
+            Constraint selectConstraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+            if (constraint != null) {
+                constraint = new And(constraint, selectConstraint);
+            } else {
+                constraint = selectConstraint;
+            }
+        }
+        this.constraint = constraint;
+
+        // Find the limit ...
+        Limit limit = Limit.NONE;
+        PlanNode limitNode = accessNode.findAtOrBelow(Type.LIMIT);
+        if (limitNode != null) {
+            Integer count = limitNode.getProperty(Property.LIMIT_COUNT, Integer.class);
+            if (count != null) limit = limit.withRowLimit(count.intValue());
+            Integer offset = limitNode.getProperty(Property.LIMIT_OFFSET, Integer.class);
+            if (offset != null) limit = limit.withOffset(offset.intValue());
+        }
+        this.limit = limit;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,44 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.graph.query.process;
-
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.query.QueryResults;
-
-/**
- * Interface representing a cache of property values used in a {@link QueryResults}.
- */
-public interface ValueCache {
-
-    /**
-     * Get the value of the named property on the node at the supplied location.
-     * 
-     * @param location the location of the node; may not be null
-     * @param name the property name
-     * @return the value of the property, or null if there is no such value
-     */
-    Object getProperty( Location location,
-                        Name name );
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -45,7 +45,6 @@
 import org.jboss.dna.graph.query.parse.InvalidQueryException;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -394,7 +393,7 @@
                 for (SelectorName name : viewNames) {
                     QueryCommand command = definitions.get(name);
                     // Create the canonical plan for the definition ...
-                    QueryContext queryContext = new QueryContext(context, new PlanHints(), schemata);
+                    QueryContext queryContext = new QueryContext(context, schemata);
                     CanonicalPlanner planner = new CanonicalPlanner();
                     PlanNode plan = planner.createPlan(queryContext, command);
                     if (queryContext.getProblems().hasErrors()) continue;

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.optimize.AddAccessNodes;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = AddAccessNodes.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -35,10 +35,7 @@
 import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
 import org.jboss.dna.graph.query.model.JoinCondition;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.ChooseJoinAlgorithm;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
 import org.jboss.dna.graph.query.plan.JoinAlgorithm;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -57,7 +54,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         bestRule = ChooseJoinAlgorithm.USE_BEST_JOIN_ALGORITHM;
         nestedRule = ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM;
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,9 +32,6 @@
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.PushSelectCriteria;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -52,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = PushSelectCriteria.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -38,7 +38,6 @@
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -67,7 +66,7 @@
         builder.addView("v1", "SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS LONG)");
         builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
         schemata = builder.build();
-        context = new QueryContext(execContext, new PlanHints(), schemata);
+        context = new QueryContext(execContext, schemata);
     }
 
     /**

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,9 +32,6 @@
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RightOuterToLeftOuterJoins;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -52,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = RightOuterToLeftOuterJoins.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -86,7 +86,7 @@
         builder.addView("type2",
                         "SELECT all.a3, all.a4 FROM all WHERE all.primaryType IN ('t2','t0') AND all.mixins IN ('t4','t5')");
         Schemata schemata = builder.build();
-        context = new QueryContext(execContext, new PlanHints(), schemata);
+        context = new QueryContext(execContext, schemata);
 
         node = new PlanNode(Type.ACCESS);
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -112,7 +112,7 @@
     public void shouldProducePlanForSelectStarFromTable() {
         schemata = schemataBuilder.addTable("__ALLNODES__", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.isEmpty(), is(true));
         assertProjectNode(plan, "column1", "column2", "column3");
@@ -128,7 +128,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -137,7 +137,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantColumnOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.select("column1", "column4").from("someTable").query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -146,7 +146,7 @@
     public void shouldProducePlanWhenSelectingAllColumnsOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().from("someTable").query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         System.out.println(plan);
         assertThat(problems.hasErrors(), is(false));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.DistinctComponent;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -37,9 +37,6 @@
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.QueryResultColumns;
-import org.jboss.dna.graph.query.process.QueryResults;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -66,7 +63,7 @@
     @Before
     public void beforeEach() {
         MockitoAnnotations.initMocks(this);
-        context = new QueryContext(executionContext, new PlanHints(), schemata);
+        context = new QueryContext(executionContext, schemata);
         columnList = new ArrayList<Column>();
         columnList.add(new Column(selector("table1"), name("colA"), "colA"));
         columnList.add(new Column(selector("table1"), name("colB"), "colB"));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.SortLocationsComponent;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,7 +32,6 @@
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Ordering;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -56,7 +55,7 @@
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
         schemata = schemataFor(columns, PropertyType.STRING, PropertyType.LONG, PropertyType.STRING);
         // Define the context ...
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), schemata);
+        context = new QueryContext(new ExecutionContext(), schemata);
         inputTuples = new ArrayList<Object[]>();
         // And define the delegating component ...
         delegate = new ProcessingComponent(context, columns) {

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,399 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.util.Version;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.search.IndexRules.Rule;
+
+/**
+ * A simple {@link IndexStrategy} implementation that relies upon two separate indexes: one for the node content and a second
+ * one for paths and UUIDs.
+ */
+ at ThreadSafe
+abstract class DualIndexStrategy implements IndexStrategy {
+
+    static class PathIndex {
+        public static final String PATH = "path";
+        public static final String UUID = "uuid";
+    }
+
+    static class ContentIndex {
+        public static final String UUID = PathIndex.UUID;
+        public static final String FULL_TEXT = "fts";
+    }
+
+    /**
+     * The number of results that should be returned when performing queries while deleting entire branches of content. The
+     * current value is {@value} .
+     */
+    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
+
+    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+        }
+    };
+
+    /**
+     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
+     */
+    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
+        private static final long serialVersionUID = 1L;
+
+        public FieldSelectorResult accept( String fieldName ) {
+            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+        }
+    };
+
+    private final IndexRules rules;
+    private final Logger logger;
+    private final QueryEngine queryEngine;
+
+    /**
+     * Create a new indexing strategy instance.
+     * 
+     * @param rules the indexing rules that govern how properties are to be index; may not be null
+     */
+    protected DualIndexStrategy( IndexRules rules ) {
+        assert rules != null;
+        this.rules = rules;
+        this.logger = Logger.getLogger(getClass());
+        // Create the query engine ...
+        Planner planner = new CanonicalPlanner();
+        Optimizer optimizer = new RuleBasedOptimizer() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+             *      org.jboss.dna.graph.query.plan.PlanHints)
+             */
+            @Override
+            protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+                                              PlanHints hints ) {
+                super.populateRuleStack(ruleStack, hints);
+                // Add any custom rules here, either at the front of the stack or at the end
+            }
+        };
+        QueryProcessor processor = new QueryProcessor() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
+             *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
+             *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+             */
+            @Override
+            protected ProcessingComponent createAccessComponent( QueryContext context,
+                                                                 PlanNode accessNode,
+                                                                 Columns resultColumns,
+                                                                 org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+                return DualIndexStrategy.this.createAccessComponent((SearchContext)context, accessNode, resultColumns, analyzer);
+            }
+        };
+
+        this.queryEngine = new QueryEngine(planner, optimizer, processor);
+    }
+
+    protected abstract ProcessingComponent createAccessComponent( SearchContext context,
+                                                                  PlanNode accessNode,
+                                                                  Columns resultColumns,
+                                                                  org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer );
+
+    /**
+     * Utility method to obtain a {@link DateFormat} instance that can be used safely within a single thread.
+     * 
+     * @return the date formatter; never null
+     */
+    protected final DateFormat dateFormatter() {
+        return dateFormatter.get();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#getNamespaceEncoder()
+     */
+    public TextEncoder getNamespaceEncoder() {
+        return new NoOpEncoder();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#getChangeCountForAutomaticOptimization()
+     */
+    public int getChangeCountForAutomaticOptimization() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#createAnalyzer()
+     */
+    public Analyzer createAnalyzer() {
+        return new StandardAnalyzer(Version.LUCENE_CURRENT);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
+     * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
+     * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
+     * documents in the content index that represent those same nodes.
+     * </p>
+     * <p>
+     * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
+     * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
+     * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
+     * query.
+     * </p>
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#deleteBelow(Path, IndexContext)
+     */
+    public int deleteBelow( Path path,
+                            IndexContext indexes ) throws IOException {
+        // Perform a query using the reader to find those nodes at/below the path ...
+        try {
+            IndexReader pathReader = indexes.getPathsReader();
+            IndexSearcher pathSearcher = new IndexSearcher(pathReader);
+            String pathStr = indexes.stringFactory().create(path) + "/";
+            PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
+            int numberDeleted = 0;
+            while (true) {
+                // Execute the query and get the results ...
+                TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
+                int numResultsInBatch = results.scoreDocs.length;
+                // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
+                IndexReader contentReader = indexes.getContentReader();
+                for (ScoreDoc result : results.scoreDocs) {
+                    int docId = result.doc;
+                    // Find the UUID of the node ...
+                    Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
+                    String uuid = doc.get(PathIndex.UUID);
+                    // Delete the document from the paths index ...
+                    pathReader.deleteDocument(docId);
+                    // Delete the corresponding document from the content index ...
+                    contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
+                }
+                numberDeleted += numResultsInBatch;
+                if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
+            }
+            indexes.commit();
+            return numberDeleted;
+        } catch (FileNotFoundException e) {
+            // There are no index files yet, so nothing to delete ...
+            return 0;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#index(Node, IndexContext)
+     */
+    public void index( Node node,
+                       IndexContext indexes ) throws IOException {
+        ValueFactory<String> strings = indexes.stringFactory();
+        Location location = node.getLocation();
+        UUID uuid = location.getUuid();
+        if (uuid == null) uuid = UUID.randomUUID();
+        Path path = location.getPath();
+        String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
+        String uuidStr = uuid.toString();
+
+        if (logger.isTraceEnabled()) {
+            logger.trace("indexing {0}", pathStr);
+        }
+
+        // Create a separate document for the path, which makes it easier to handle moves since the path can
+        // be changed without changing any other content fields ...
+        Document doc = new Document();
+        doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        indexes.getPathsWriter().addDocument(doc);
+
+        // Create the document for the content (properties) ...
+        doc = new Document();
+        doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        String stringValue = null;
+        StringBuilder fullTextSearchValue = null;
+        for (Property property : node.getProperties()) {
+            Name name = property.getName();
+            Rule rule = rules.getRule(name);
+            if (rule.isSkipped()) continue;
+            String nameString = strings.create(name);
+            if (rule.isDate()) {
+                DateTimeFactory dateFactory = indexes.dateFactory();
+                for (Object value : property) {
+                    if (value == null) continue;
+                    DateTime dateValue = dateFactory.create(value);
+                    stringValue = dateFormatter().format(dateValue.toDate());
+                    // Add a separate field for each property value ...
+                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+                    // Dates are not added to the full-text search field (since this wouldn't make sense)
+                }
+                continue;
+            }
+            for (Object value : property) {
+                if (value == null) continue;
+                if (value instanceof Binary) {
+                    // don't include binary values as individual fields but do include them in the full-text search ...
+                    // TODO : add to full-text search ...
+                    continue;
+                }
+                stringValue = strings.create(value);
+                // Add a separate field for each property value ...
+                doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+                // And add to the full-text field ...
+                if (rule.isFullText()) {
+                    if (fullTextSearchValue == null) {
+                        fullTextSearchValue = new StringBuilder();
+                    } else {
+                        fullTextSearchValue.append(' ');
+                    }
+                    fullTextSearchValue.append(stringValue);
+                }
+            }
+        }
+        // Add the full-text-search field ...
+        if (fullTextSearchValue != null) {
+            doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
+        }
+        indexes.getContentWriter().addDocument(doc);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#search(String, int, int, IndexContext, List)
+     */
+    public void search( String fullTextString,
+                        int maxResults,
+                        int offset,
+                        IndexContext indexes,
+                        List<Location> results ) throws IOException, ParseException {
+        assert fullTextString != null;
+        assert fullTextString.length() > 0;
+        assert offset >= 0;
+        assert maxResults > 0;
+        assert indexes != null;
+        assert results != null;
+
+        // Parse the full-text search and search against the 'fts' field ...
+        QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+        Query query = parser.parse(fullTextString);
+        TopDocs docs = indexes.getContentSearcher().search(query, maxResults + offset);
+
+        // Collect the results ...
+        IndexReader contentReader = indexes.getContentReader();
+        IndexReader pathReader = indexes.getPathsReader();
+        IndexSearcher pathSearcher = indexes.getPathsSearcher();
+        ScoreDoc[] scoreDocs = docs.scoreDocs;
+        int numberOfResults = scoreDocs.length;
+        if (numberOfResults > offset) {
+            // There are enough results to satisfy the offset ...
+            for (int i = offset, num = scoreDocs.length; i != num; ++i) {
+                ScoreDoc result = scoreDocs[i];
+                int docId = result.doc;
+                // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
+                Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
+                String uuid = doc.get(ContentIndex.UUID);
+                // Find the path for this node (is there a better way to do this than one search per UUID?) ...
+                TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
+                if (pathDocs.scoreDocs.length < 1) {
+                    // No path record found ...
+                    continue;
+                }
+                Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+                Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
+                // Now add the location ...
+                results.add(Location.create(path, UUID.fromString(uuid)));
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#query(org.jboss.dna.search.SearchContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand)
+     */
+    public QueryResults query( SearchContext context,
+                               QueryCommand query ) {
+        return this.queryEngine.execute(context, query);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,16 +31,17 @@
 import org.apache.lucene.index.IndexWriter.MaxFieldLength;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.store.Directory;
+import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.DateTimeFactory;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactory;
 
 /**
- * A set of index readers and writers.
+ * A context for working with the index readers and writers.
  */
 @NotThreadSafe
-public final class IndexContext {
+final class IndexContext {
 
     private final ExecutionContext context;
     private final Directory pathsIndexDirectory;
@@ -278,4 +279,16 @@
         if (runtimeError != null) throw runtimeError;
     }
 
+    /**
+     * Create a copy of this index context, except that it uses the supplied execution context.
+     * 
+     * @param context the new execution context that should be used in the copy
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the context is null
+     */
+    public IndexContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new IndexContext(context, pathsIndexDirectory, contentIndexDirectory, analyzer, overwrite, readOnly);
+    }
+
 }

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,628 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * The set of rules that dictate how properties should be indexed.
+ */
+ at Immutable
+class IndexRules {
+
+    public static final int INDEX = 2 << 0;
+    public static final int ANALYZE = 2 << 1;
+    public static final int STORE = 2 << 2;
+    public static final int STORE_COMPRESSED = 2 << 3;
+    public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
+    public static final int FULL_TEXT = 2 << 5;
+    public static final int TREAT_AS_DATE = 2 << 6;
+
+    /**
+     * A single rule that dictates how a single property should be indexed.
+     * 
+     * @see IndexRules#getRule(Name)
+     */
+    @Immutable
+    public static interface Rule {
+        boolean isIncluded();
+
+        boolean isSkipped();
+
+        boolean isAnalyzed();
+
+        boolean isAnalyzedWithoutNorms();
+
+        boolean isStored();
+
+        boolean isStoredCompressed();
+
+        boolean isFullText();
+
+        boolean isDate();
+
+        int getMask();
+
+        Field.Store getStoreOption();
+
+        Field.Index getIndexOption();
+    }
+
+    public static final Rule SKIP = new SkipRule();
+
+    @Immutable
+    protected static class SkipRule implements Rule {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
+         */
+        public int getMask() {
+            return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         */
+        public boolean isAnalyzed() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         */
+        public boolean isAnalyzedWithoutNorms() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
+         */
+        public boolean isFullText() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
+         */
+        public boolean isIncluded() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
+         */
+        public boolean isSkipped() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
+         */
+        public boolean isStored() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
+         */
+        public boolean isStoredCompressed() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
+         */
+        public boolean isDate() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
+         */
+        public Index getIndexOption() {
+            return Field.Index.NO;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
+         */
+        public Store getStoreOption() {
+            return Field.Store.NO;
+        }
+    }
+
+    @Immutable
+    public static final class GeneralRule implements Rule {
+        private final int value;
+        private final Field.Store store;
+        private final Field.Index index;
+
+        protected GeneralRule( int value ) {
+            this.value = value;
+            this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
+            this.store = isStored() ? Field.Store.YES : Field.Store.NO;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
+         */
+        public int getMask() {
+            return value;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         */
+        public boolean isAnalyzed() {
+            return (value & ANALYZE) == ANALYZE;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         */
+        public boolean isAnalyzedWithoutNorms() {
+            return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
+         */
+        public boolean isFullText() {
+            return (value & FULL_TEXT) == FULL_TEXT;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
+         */
+        public boolean isIncluded() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
+         */
+        public boolean isSkipped() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
+         */
+        public boolean isStored() {
+            return (value & STORE) == STORE;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
+         */
+        public boolean isStoredCompressed() {
+            return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
+         */
+        public boolean isDate() {
+            return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
+        }
+
+        protected Rule with( int options ) {
+            return createRule(value | options);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
+         */
+        public Index getIndexOption() {
+            return index;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
+         */
+        public Store getStoreOption() {
+            return store;
+        }
+    }
+
+    private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
+
+    protected static Rule createRule( int value ) {
+        if (value <= 0) {
+            return SKIP;
+        }
+        Integer key = new Integer(value);
+        Rule rule = CACHE.get(key);
+        if (rule == null) {
+            Rule newRule = new GeneralRule(value);
+            rule = CACHE.putIfAbsent(value, newRule);
+            if (rule == null) rule = newRule;
+        }
+        return rule;
+    }
+
+    private final Map<Name, Rule> rulesByName;
+    private final Rule defaultRule;
+
+    protected IndexRules( Map<Name, Rule> rulesByName,
+                          Rule defaultRule ) {
+        this.rulesByName = rulesByName;
+        this.defaultRule = defaultRule != null ? defaultRule : SKIP;
+        assert this.defaultRule != null;
+    }
+
+    /**
+     * Get the rule associated with the given property name.
+     * 
+     * @param name the property name, or null if the default rule is to be returned
+     * @return the rule; never null
+     */
+    public Rule getRule( Name name ) {
+        Rule result = rulesByName.get(name);
+        return result != null ? result : this.defaultRule;
+    }
+
+    /**
+     * Return a new builder that can be used to create {@link IndexRules} objects.
+     * 
+     * @return a builder; never null
+     */
+    public static Builder createBuilder() {
+        return new Builder(new HashMap<Name, Rule>());
+    }
+
+    /**
+     * Return a new builder that can be used to create {@link IndexRules} objects.
+     * 
+     * @param initialRules the rules that the builder should start with
+     * @return a builder; never null
+     * @throws IllegalArgumentException if the initial rules reference is null
+     */
+    public static Builder createBuilder( IndexRules initialRules ) {
+        CheckArg.isNotNull(initialRules, "initialRules");
+        return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
+    }
+
+    /**
+     * A builder of immutable {@link IndexRules} objects.
+     */
+    @NotThreadSafe
+    public static class Builder {
+        private final Map<Name, Rule> rulesByName;
+        private Rule defaultRule;
+
+        Builder( Map<Name, Rule> rulesByName ) {
+            assert rulesByName != null;
+            this.rulesByName = rulesByName;
+        }
+
+        /**
+         * Set the default rules.
+         * 
+         * @param rule the default rule to use
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder defaultTo( Rule rule ) {
+            CheckArg.isNotNull(rule, "rule");
+            defaultRule = rule;
+            return this;
+        }
+
+        /**
+         * Set the default rules.
+         * 
+         * @param ruleMask the bitmask of rule to use
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder defaultTo( int ruleMask ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (ruleMask == 0) {
+                defaultRule = SKIP;
+            } else {
+                // Make sure the index flag is set ...
+                ruleMask |= INDEX;
+                defaultRule = createRule(ruleMask);
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be skipped from indexing.
+         * 
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder skip( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    rulesByName.put(name, SKIP);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Set the properties with the supplied names to use the supplied rules.
+         * 
+         * @param ruleMask the bitmask of rules to use
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder set( int ruleMask,
+                            Name... namesToIndex ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (namesToIndex != null) {
+                if (ruleMask > 0) {
+                    skip(namesToIndex);
+                } else {
+                    // Make sure the index flag is set ...
+                    ruleMask |= INDEX;
+                    Rule rule = createRule(ruleMask);
+                    for (Name name : namesToIndex) {
+                        rulesByName.put(name, rule);
+                    }
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
+         * these properties.
+         * 
+         * @param ruleMask the bitmask of rules to add
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder add( int ruleMask,
+                            Name... namesToIndex ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, ruleMask);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder index( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
+         * rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be analyzed
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyze( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, ANALYZE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
+         * for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be stored
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder store( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, STORE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder fullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, FULL_TEXT | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
+         * other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder treatAsDates( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, TREAT_AS_DATE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
+         * for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndStore( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | STORE);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
+         * does not remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
+         *        searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder storeAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | STORE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        protected void add( Name name,
+                            int option ) {
+            Rule rule = rulesByName.get(name);
+            if (rule != null) {
+                option |= rule.getMask();
+            }
+            rulesByName.put(name, createRule(option));
+        }
+
+        /**
+         * Build the indexing rules.
+         * 
+         * @return the immutable indexing rules.
+         */
+        public IndexRules build() {
+            return new IndexRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,133 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * Interface defining the behaviors associated with indexing graph content.
+ */
+interface IndexStrategy {
+
+    /**
+     * Get the number of changes that are allowed before optimization is automatically run.
+     * 
+     * @return a positive number denoting the minimum number of changes between automatic optimization operations, or a
+     *         non-positive number if automatic optimization should never be run
+     */
+    int getChangeCountForAutomaticOptimization();
+
+    /**
+     * Get the {@link TextEncoder} that should be used to encode the namespace URIs.
+     * 
+     * @return the encoder; may not be null
+     */
+    TextEncoder getNamespaceEncoder();
+
+    /**
+     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
+     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
+     * {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param node the node to be indexed; never null
+     * @param indexes the set of index readers and writers; never null
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    void index( Node node,
+                IndexContext indexes ) throws IOException;
+
+    /**
+     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
+     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
+     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param changes the set of changes to the content
+     * @param indexes the set of index readers and writers; never null
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int apply( Iterable<ChangeRequest> changes,
+               IndexContext indexes ) throws IOException;
+
+    /**
+     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
+     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
+     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param path the path identifying the graph content that is to be removed; never null
+     * @param indexes the set of index readers and writers; never null
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int deleteBelow( Path path,
+                     IndexContext indexes ) throws IOException;
+
+    /**
+     * Create the analyzer that is used for reading and updating the indexes.
+     * 
+     * @return the analyzer; may not be null
+     */
+    Analyzer createAnalyzer();
+
+    /**
+     * Perform a full-text search given the supplied query.
+     * 
+     * @param fullTextString the full-text query; never null or blank
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @param indexes the set of index readers and writers; never null
+     * @param results the list where the results should be accumulated; never null
+     * @throws IOException if there is a problem indexing or using the writers
+     * @throws ParseException if there is a problem parsing the query
+     */
+    void search( String fullTextString,
+                 int maxResults,
+                 int offset,
+                 IndexContext indexes,
+                 List<Location> results ) throws IOException, ParseException;
+
+    /**
+     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
+     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
+     * writers) that should be used.
+     * 
+     * @param context the context in which the query should be executed; never null
+     * @param query the query; never null
+     * @return the results of the query
+     */
+    QueryResults query( SearchContext context,
+                        QueryCommand query );
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,628 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import net.jcip.annotations.Immutable;
-import net.jcip.annotations.NotThreadSafe;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.Field.Index;
-import org.apache.lucene.document.Field.Store;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.property.Name;
-
-/**
- * The set of rules that dictate how properties should be indexed.
- */
- at Immutable
-public class IndexingRules {
-
-    public static final int INDEX = 2 << 0;
-    public static final int ANALYZE = 2 << 1;
-    public static final int STORE = 2 << 2;
-    public static final int STORE_COMPRESSED = 2 << 3;
-    public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
-    public static final int FULL_TEXT = 2 << 5;
-    public static final int TREAT_AS_DATE = 2 << 6;
-
-    /**
-     * A single rule that dictates how a single property should be indexed.
-     * 
-     * @see IndexingRules#getRule(Name)
-     */
-    @Immutable
-    public static interface Rule {
-        boolean isIncluded();
-
-        boolean isSkipped();
-
-        boolean isAnalyzed();
-
-        boolean isAnalyzedWithoutNorms();
-
-        boolean isStored();
-
-        boolean isStoredCompressed();
-
-        boolean isFullText();
-
-        boolean isDate();
-
-        int getMask();
-
-        Field.Store getStoreOption();
-
-        Field.Index getIndexOption();
-    }
-
-    public static final Rule SKIP = new SkipRule();
-
-    @Immutable
-    protected static class SkipRule implements Rule {
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
-         */
-        public int getMask() {
-            return 0;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
-         */
-        public boolean isAnalyzed() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
-         */
-        public boolean isAnalyzedWithoutNorms() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
-         */
-        public boolean isFullText() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
-         */
-        public boolean isIncluded() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
-         */
-        public boolean isDate() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
-         */
-        public Index getIndexOption() {
-            return Field.Index.NO;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
-         */
-        public Store getStoreOption() {
-            return Field.Store.NO;
-        }
-    }
-
-    @Immutable
-    public static final class GeneralRule implements Rule {
-        private final int value;
-        private final Field.Store store;
-        private final Field.Index index;
-
-        protected GeneralRule( int value ) {
-            this.value = value;
-            this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
-            this.store = isStored() ? Field.Store.YES : Field.Store.NO;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
-         */
-        public int getMask() {
-            return value;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
-         */
-        public boolean isAnalyzed() {
-            return (value & ANALYZE) == ANALYZE;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
-         */
-        public boolean isAnalyzedWithoutNorms() {
-            return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
-         */
-        public boolean isFullText() {
-            return (value & FULL_TEXT) == FULL_TEXT;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
-         */
-        public boolean isIncluded() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return (value & STORE) == STORE;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
-         */
-        public boolean isDate() {
-            return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
-        }
-
-        protected Rule with( int options ) {
-            return createRule(value | options);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
-         */
-        public Index getIndexOption() {
-            return index;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
-         */
-        public Store getStoreOption() {
-            return store;
-        }
-    }
-
-    private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
-
-    protected static Rule createRule( int value ) {
-        if (value <= 0) {
-            return SKIP;
-        }
-        Integer key = new Integer(value);
-        Rule rule = CACHE.get(key);
-        if (rule == null) {
-            Rule newRule = new GeneralRule(value);
-            rule = CACHE.putIfAbsent(value, newRule);
-            if (rule == null) rule = newRule;
-        }
-        return rule;
-    }
-
-    private final Map<Name, Rule> rulesByName;
-    private final Rule defaultRule;
-
-    protected IndexingRules( Map<Name, Rule> rulesByName,
-                             Rule defaultRule ) {
-        this.rulesByName = rulesByName;
-        this.defaultRule = defaultRule != null ? defaultRule : SKIP;
-        assert this.defaultRule != null;
-    }
-
-    /**
-     * Get the rule associated with the given property name.
-     * 
-     * @param name the property name, or null if the default rule is to be returned
-     * @return the rule; never null
-     */
-    public Rule getRule( Name name ) {
-        Rule result = rulesByName.get(name);
-        return result != null ? result : this.defaultRule;
-    }
-
-    /**
-     * Return a new builder that can be used to create {@link IndexingRules} objects.
-     * 
-     * @return a builder; never null
-     */
-    public static Builder createBuilder() {
-        return new Builder(new HashMap<Name, Rule>());
-    }
-
-    /**
-     * Return a new builder that can be used to create {@link IndexingRules} objects.
-     * 
-     * @param initialRules the rules that the builder should start with
-     * @return a builder; never null
-     * @throws IllegalArgumentException if the initial rules reference is null
-     */
-    public static Builder createBuilder( IndexingRules initialRules ) {
-        CheckArg.isNotNull(initialRules, "initialRules");
-        return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
-    }
-
-    /**
-     * A builder of immutable {@link IndexingRules} objects.
-     */
-    @NotThreadSafe
-    public static class Builder {
-        private final Map<Name, Rule> rulesByName;
-        private Rule defaultRule;
-
-        Builder( Map<Name, Rule> rulesByName ) {
-            assert rulesByName != null;
-            this.rulesByName = rulesByName;
-        }
-
-        /**
-         * Set the default rules.
-         * 
-         * @param rule the default rule to use
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder defaultTo( Rule rule ) {
-            CheckArg.isNotNull(rule, "rule");
-            defaultRule = rule;
-            return this;
-        }
-
-        /**
-         * Set the default rules.
-         * 
-         * @param ruleMask the bitmask of rule to use
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder defaultTo( int ruleMask ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (ruleMask == 0) {
-                defaultRule = SKIP;
-            } else {
-                // Make sure the index flag is set ...
-                ruleMask |= INDEX;
-                defaultRule = createRule(ruleMask);
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be skipped from indexing.
-         * 
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder skip( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    rulesByName.put(name, SKIP);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Set the properties with the supplied names to use the supplied rules.
-         * 
-         * @param ruleMask the bitmask of rules to use
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder set( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                if (ruleMask > 0) {
-                    skip(namesToIndex);
-                } else {
-                    // Make sure the index flag is set ...
-                    ruleMask |= INDEX;
-                    Rule rule = createRule(ruleMask);
-                    for (Name name : namesToIndex) {
-                        rulesByName.put(name, rule);
-                    }
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
-         * these properties.
-         * 
-         * @param ruleMask the bitmask of rules to add
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder add( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ruleMask);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder index( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
-         * rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be analyzed
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyze( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ANALYZE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
-         * for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be stored
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder store( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, STORE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder fullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, FULL_TEXT | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
-         * other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder treatAsDates( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, TREAT_AS_DATE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
-         * for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndStore( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
-         * does not remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
-         *        searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder storeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | STORE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        protected void add( Name name,
-                            int option ) {
-            Rule rule = rulesByName.get(name);
-            if (rule != null) {
-                option |= rule.getMask();
-            }
-            rulesByName.put(name, createRule(option));
-        }
-
-        /**
-         * Build the indexing rules.
-         * 
-         * @return the immutable indexing rules.
-         */
-        public IndexingRules build() {
-            return new IndexingRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
-        }
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,126 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.List;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * Interface defining the behaviors associated with indexing graph content.
- */
-public interface IndexingStrategy {
-
-    int getChangeCountForAutomaticOptimization();
-
-    TextEncoder getNamespaceEncoder();
-
-    /**
-     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
-     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
-     * {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param node the node to be indexed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    void index( Node node,
-                IndexContext indexes ) throws IOException;
-
-    /**
-     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
-     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
-     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param changes the set of changes to the content
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int apply( Iterable<ChangeRequest> changes,
-               IndexContext indexes ) throws IOException;
-
-    /**
-     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
-     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
-     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param path the path identifying the graph content that is to be removed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int deleteBelow( Path path,
-                     IndexContext indexes ) throws IOException;
-
-    /**
-     * Create the analyzer that is used for reading and updating the indexes.
-     * 
-     * @return the analyzer; may not be null
-     */
-    Analyzer createAnalyzer();
-
-    /**
-     * Perform a full-text search given the supplied query.
-     * 
-     * @param fullTextString the full-text query; never null or blank
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @param indexes the set of index readers and writers; never null
-     * @param results the list where the results should be accumulated; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    void performQuery( String fullTextString,
-                       int maxResults,
-                       int offset,
-                       IndexContext indexes,
-                       List<Location> results ) throws IOException, ParseException;
-
-    /**
-     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
-     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
-     * writers) that should be used.
-     * 
-     * @param query the query; never null
-     * @param schemata the definition of the tables used in the query; never null
-     * @param indexes the set of index readers and writers; never null
-     * @return the results of the query
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    QueryResults performQuery( QueryCommand query,
-                               Schemata schemata,
-                               IndexContext indexes ) throws IOException, ParseException;
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.util.List;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.AbstractAccessComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * An {@link IndexStrategy} implementation that stores all content within a set of two indexes: one for the node content and a
+ * second one for paths and UUIDs.
+ */
+ at ThreadSafe
+class KitchenSinkIndexStrategy extends DualIndexStrategy {
+
+    /**
+     * The default set of {@link IndexRules} used by {@link KitchenSinkIndexStrategy} instances when no rules are provided.
+     */
+    public static final IndexRules DEFAULT_RULES;
+
+    static {
+        IndexRules.Builder builder = IndexRules.createBuilder();
+        // Configure the default behavior ...
+        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
+        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
+        // Configure the properties that we'll treat as dates ...
+        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+        DEFAULT_RULES = builder.build();
+    }
+
+    /**
+     * Create a new indexing strategy instance uses the {@link #DEFAULT_RULES default indexing rules}.
+     */
+    public KitchenSinkIndexStrategy() {
+        this(null);
+    }
+
+    /**
+     * Create a new indexing strategy instance.
+     * 
+     * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
+     *        rules} are to be used
+     */
+    public KitchenSinkIndexStrategy( IndexRules rules ) {
+        super(rules != null ? rules : DEFAULT_RULES);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.DualIndexStrategy#createAccessComponent(org.jboss.dna.search.SearchContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
+     *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+     */
+    @Override
+    protected ProcessingComponent createAccessComponent( final SearchContext context,
+                                                         PlanNode accessNode,
+                                                         Columns resultColumns,
+                                                         Analyzer analyzer ) {
+        // Create a processing component for this access query ...
+        return new AbstractAccessComponent(context, resultColumns, accessNode) {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+             */
+            @Override
+            public List<Object[]> execute() {
+                return null;
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#apply(Iterable, IndexContext)
+     */
+    public int apply( Iterable<ChangeRequest> changes,
+                      IndexContext indexes ) /*throws IOException*/{
+        for (ChangeRequest change : changes) {
+            if (change != null) continue;
+        }
+        return 0;
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,57 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.util.List;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-
-/**
- * A {@link ProcessingComponent} implementation that is used by the {@link LuceneQueryEngine.LuceneProcessor} to perform atomic
- * queries against the Lucene indexes.
- */
-class LuceneQueryComponent extends ProcessingComponent {
-
-    private final PlanNode accessNode;
-
-    LuceneQueryComponent( QueryContext context,
-                          Columns columns,
-                          PlanNode accessNode ) {
-        super(context, columns);
-        this.accessNode = accessNode;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
-     */
-    @Override
-    public List<Object[]> execute() {
-        if (accessNode != null) return null;
-        return null;
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,114 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryEngine;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.optimize.Optimizer;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.QueryProcessor;
-import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
-import org.jboss.dna.graph.query.validate.Schemata;
-
-/**
- * 
- */
-class LuceneQueryEngine {
-
-    private QueryEngine engine;
-
-    public LuceneQueryEngine() {
-        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor());
-    }
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param query the query that is to be executed
-     * @param schemata the schemata that defines the tables used in the query
-     * @param indexes the indexes that should be used to execute the query; never null
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    public QueryResults execute( QueryCommand query,
-                                 Schemata schemata,
-                                 IndexContext indexes ) throws IOException, ParseException {
-        return engine.execute(indexes.context(), query, schemata, new PlanHints());
-    }
-
-    /**
-     * An {@link Optimizer} implementation that specializes the {@link RuleBasedOptimizer} by using custom rules.
-     */
-    protected static class LuceneOptimizer extends RuleBasedOptimizer {
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
-         *      org.jboss.dna.graph.query.plan.PlanHints)
-         */
-        @Override
-        protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
-                                          PlanHints hints ) {
-            super.populateRuleStack(ruleStack, hints);
-            // Add any custom rules here, either at the front of the stack or at the end
-        }
-    }
-
-    /**
-     * A query processor that operates against Lucene indexes. All functionality is inherited from the {@link QueryProcessor},
-     * except for the creation of the {@link ProcessingComponent} that does the low-level atomic queries (against the Lucene
-     * indexes).
-     */
-    protected static class LuceneProcessor extends QueryProcessor {
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
-         *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
-         *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-         */
-        @Override
-        protected ProcessingComponent createAccessComponent( QueryContext context,
-                                                             PlanNode accessNode,
-                                                             Columns resultColumns,
-                                                             Analyzer analyzer ) {
-            return new LuceneQueryComponent(context, resultColumns, accessNode);
-        }
-    }
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,161 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.util.Map;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * 
+ */
+class SearchContext extends QueryContext {
+
+    private final IndexContext indexes;
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param indexes the indexes that should be used
+     * @param schemata the definition of the tables available to this query
+     */
+    public SearchContext( IndexContext indexes,
+                          Schemata schemata ) {
+        super(indexes.context(), schemata);
+        this.indexes = indexes;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param queryContext
+     * @param indexes
+     */
+    public SearchContext( QueryContext queryContext,
+                          IndexContext indexes ) {
+        super(queryContext.getExecutionContext(), queryContext.getSchemata(), queryContext.getHints(),
+              queryContext.getProblems(), queryContext.getVariables());
+        this.indexes = indexes;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @param variables the mapping of variables and values, or null if there are no such variables
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
+    public SearchContext( IndexContext context,
+                          Schemata schemata,
+                          PlanHints hints,
+                          Problems problems,
+                          Map<String, Object> variables ) {
+        super(context.context(), schemata, hints, problems, variables);
+        this.indexes = context;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Get the {@link IndexContext} for this query context.
+     * 
+     * @return the index context; never null
+     */
+    public IndexContext getIndexes() {
+        return indexes;
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied index context.
+     * 
+     * @param context the index context that should be used in the new query context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the index context reference is null
+     */
+    public SearchContext with( IndexContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new SearchContext(context, getSchemata(), getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.ExecutionContext)
+     */
+    @Override
+    public SearchContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new SearchContext(indexes.with(context), getSchemata(), getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.validate.Schemata)
+     */
+    @Override
+    public SearchContext with( Schemata schemata ) {
+        CheckArg.isNotNull(schemata, "schemata");
+        return new SearchContext(indexes, schemata, getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.plan.PlanHints)
+     */
+    @Override
+    public SearchContext with( PlanHints hints ) {
+        CheckArg.isNotNull(hints, "hints");
+        return new SearchContext(indexes, getSchemata(), hints, getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.common.collection.Problems)
+     */
+    @Override
+    public SearchContext with( Problems problems ) {
+        return new SearchContext(indexes, getSchemata(), getHints(), problems, getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(java.util.Map)
+     */
+    @Override
+    public SearchContext with( Map<String, Object> variables ) {
+        return new SearchContext(indexes, getSchemata(), getHints(), getProblems(), variables);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -61,7 +61,7 @@
     private final String sourceName;
     private final RepositoryConnectionFactory connectionFactory;
     private final DirectoryConfiguration directoryFactory;
-    private final IndexingStrategy indexingStrategy;
+    private final IndexStrategy indexStrategy;
     private final PathFactory pathFactory;
     @GuardedBy( "workspaceEngineLock" )
     private final Map<String, WorkspaceSearchEngine> workspaceEnginesByName;
@@ -76,15 +76,12 @@
      * @param sourceName the name of the {@link RepositorySource}
      * @param connectionFactory the connection factory
      * @param directoryFactory the factory for Lucene {@link Directory directories}
-     * @param indexingStrategy the indexing strategy that governs how properties are to be indexed; or null if the default
-     *        strategy should be used
      * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
      */
     public SearchEngine( ExecutionContext context,
                          String sourceName,
                          RepositoryConnectionFactory connectionFactory,
-                         DirectoryConfiguration directoryFactory,
-                         IndexingStrategy indexingStrategy ) {
+                         DirectoryConfiguration directoryFactory ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(sourceName, "sourceName");
         CheckArg.isNotNull(connectionFactory, "connectionFactory");
@@ -95,7 +92,7 @@
         this.context = context;
         this.pathFactory = context.getValueFactories().getPathFactory();
         this.workspaceEnginesByName = new HashMap<String, WorkspaceSearchEngine>();
-        this.indexingStrategy = indexingStrategy != null ? indexingStrategy : new StoreLittleIndexingStrategy();
+        this.indexStrategy = new KitchenSinkIndexStrategy();
     }
 
     /**
@@ -153,7 +150,7 @@
                 engine = workspaceEnginesByName.get(workspaceName);
                 if (engine == null) {
                     // Create the engine and register it ...
-                    engine = new WorkspaceSearchEngine(context, directoryFactory, indexingStrategy, sourceName, workspaceName,
+                    engine = new WorkspaceSearchEngine(context, directoryFactory, indexStrategy, sourceName, workspaceName,
                                                        connectionFactory);
                     workspaceEnginesByName.put(workspaceName, engine);
                 }

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,372 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.List;
-import java.util.UUID;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.PrefixQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.util.Version;
-import org.jboss.dna.common.text.NoOpEncoder;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.util.Logger;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.ValueFactory;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-import org.jboss.dna.search.IndexingRules.Rule;
-
-/**
- * A simple {@link IndexingStrategy} implementation that relies upon very few fields to be stored in the indexes.
- */
- at ThreadSafe
-class StoreLittleIndexingStrategy implements IndexingStrategy {
-
-    static class PathIndex {
-        public static final String PATH = "path";
-        public static final String UUID = "uuid";
-    }
-
-    static class ContentIndex {
-        public static final String UUID = PathIndex.UUID;
-        public static final String FULL_TEXT = "fts";
-    }
-
-    public static final int SIZE_OF_DELETE_BATCHES = 100;
-
-    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
-        }
-    };
-
-    private static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
-        private static final long serialVersionUID = 1L;
-
-        public FieldSelectorResult accept( String fieldName ) {
-            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
-        }
-    };
-
-    /**
-     * The default set of {@link IndexingRules} used by {@link StoreLittleIndexingStrategy} instances when no rules are provided.
-     */
-    public static final IndexingRules DEFAULT_RULES;
-
-    static {
-        IndexingRules.Builder builder = IndexingRules.createBuilder();
-        // Configure the default behavior ...
-        builder.defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE);
-        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
-        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
-        // Configure the properties that we'll treat as dates ...
-        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
-        DEFAULT_RULES = builder.build();
-    }
-
-    private final IndexingRules rules;
-    private final Logger logger;
-    private final LuceneQueryEngine queryEngine;
-
-    /**
-     * Create a new indexing strategy instance that does not support queries.
-     */
-    public StoreLittleIndexingStrategy() {
-        this(null);
-    }
-
-    /**
-     * Create a new indexing strategy instance.
-     * 
-     * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
-     *        rules} are to be used
-     */
-    public StoreLittleIndexingStrategy( IndexingRules rules ) {
-        this.rules = rules != null ? rules : DEFAULT_RULES;
-        this.logger = Logger.getLogger(getClass());
-        this.queryEngine = new LuceneQueryEngine();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#getNamespaceEncoder()
-     */
-    public TextEncoder getNamespaceEncoder() {
-        return new NoOpEncoder();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#getChangeCountForAutomaticOptimization()
-     */
-    public int getChangeCountForAutomaticOptimization() {
-        return 0;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#createAnalyzer()
-     */
-    public Analyzer createAnalyzer() {
-        return new StandardAnalyzer(Version.LUCENE_CURRENT);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
-     * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
-     * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
-     * documents in the content index that represent those same nodes.
-     * </p>
-     * <p>
-     * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
-     * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
-     * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
-     * query.
-     * </p>
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#deleteBelow(Path, IndexContext)
-     */
-    public int deleteBelow( Path path,
-                            IndexContext indexes ) throws IOException {
-        // Perform a query using the reader to find those nodes at/below the path ...
-        try {
-            IndexReader pathReader = indexes.getPathsReader();
-            IndexSearcher pathSearcher = new IndexSearcher(pathReader);
-            String pathStr = indexes.stringFactory().create(path) + "/";
-            PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
-            int numberDeleted = 0;
-            while (true) {
-                // Execute the query and get the results ...
-                TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
-                int numResultsInBatch = results.scoreDocs.length;
-                // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
-                IndexReader contentReader = indexes.getContentReader();
-                for (ScoreDoc result : results.scoreDocs) {
-                    int docId = result.doc;
-                    // Find the UUID of the node ...
-                    Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
-                    String uuid = doc.get(PathIndex.UUID);
-                    // Delete the document from the paths index ...
-                    pathReader.deleteDocument(docId);
-                    // Delete the corresponding document from the content index ...
-                    contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
-                }
-                numberDeleted += numResultsInBatch;
-                if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
-            }
-            indexes.commit();
-            return numberDeleted;
-        } catch (FileNotFoundException e) {
-            // There are no index files yet, so nothing to delete ...
-            return 0;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#index(Node, IndexContext)
-     */
-    public void index( Node node,
-                       IndexContext indexes ) throws IOException {
-        ValueFactory<String> strings = indexes.stringFactory();
-        Location location = node.getLocation();
-        UUID uuid = location.getUuid();
-        if (uuid == null) uuid = UUID.randomUUID();
-        Path path = location.getPath();
-        String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
-        String uuidStr = uuid.toString();
-
-        if (logger.isTraceEnabled()) {
-            logger.trace("indexing {0}", pathStr);
-        }
-
-        // Create a separate document for the path, which makes it easier to handle moves since the path can
-        // be changed without changing any other content fields ...
-        Document doc = new Document();
-        doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        indexes.getPathsWriter().addDocument(doc);
-
-        // Create the document for the content (properties) ...
-        doc = new Document();
-        doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        String stringValue = null;
-        StringBuilder fullTextSearchValue = null;
-        for (Property property : node.getProperties()) {
-            Name name = property.getName();
-            Rule rule = rules.getRule(name);
-            if (rule.isSkipped()) continue;
-            String nameString = strings.create(name);
-            if (rule.isDate()) {
-                DateTimeFactory dateFactory = indexes.dateFactory();
-                for (Object value : property) {
-                    if (value == null) continue;
-                    DateTime dateValue = dateFactory.create(value);
-                    stringValue = dateFormatter.get().format(dateValue.toDate());
-                    // Add a separate field for each property value ...
-                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                    // Dates are not added to the full-text search field (since this wouldn't make sense)
-                }
-                continue;
-            }
-            for (Object value : property) {
-                if (value == null) continue;
-                if (value instanceof Binary) {
-                    // don't include binary values as individual fields but do include them in the full-text search ...
-                    // TODO : add to full-text search ...
-                    continue;
-                }
-                stringValue = strings.create(value);
-                // Add a separate field for each property value ...
-                doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                // And add to the full-text field ...
-                if (rule.isFullText()) {
-                    if (fullTextSearchValue == null) {
-                        fullTextSearchValue = new StringBuilder();
-                    } else {
-                        fullTextSearchValue.append(' ');
-                    }
-                    fullTextSearchValue.append(stringValue);
-                }
-            }
-        }
-        // Add the full-text-search field ...
-        if (fullTextSearchValue != null) {
-            doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
-        }
-        indexes.getContentWriter().addDocument(doc);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#performQuery(String, int, int, IndexContext, List)
-     */
-    public void performQuery( String fullTextString,
-                              int maxResults,
-                              int offset,
-                              IndexContext indexes,
-                              List<Location> results ) throws IOException, ParseException {
-        assert fullTextString != null;
-        assert fullTextString.length() > 0;
-        assert offset >= 0;
-        assert maxResults > 0;
-        assert indexes != null;
-        assert results != null;
-
-        // Parse the full-text search and search against the 'fts' field ...
-        QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
-        Query query = parser.parse(fullTextString);
-        TopDocs docs = indexes.getContentSearcher().search(query, maxResults + offset);
-
-        // Collect the results ...
-        IndexReader contentReader = indexes.getContentReader();
-        IndexReader pathReader = indexes.getPathsReader();
-        IndexSearcher pathSearcher = indexes.getPathsSearcher();
-        ScoreDoc[] scoreDocs = docs.scoreDocs;
-        int numberOfResults = scoreDocs.length;
-        if (numberOfResults > offset) {
-            // There are enough results to satisfy the offset ...
-            for (int i = offset, num = scoreDocs.length; i != num; ++i) {
-                ScoreDoc result = scoreDocs[i];
-                int docId = result.doc;
-                // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
-                Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
-                String uuid = doc.get(ContentIndex.UUID);
-                // Find the path for this node (is there a better way to do this than one search per UUID?) ...
-                TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
-                if (pathDocs.scoreDocs.length < 1) {
-                    // No path record found ...
-                    continue;
-                }
-                Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
-                Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
-                // Now add the location ...
-                results.add(Location.create(path, UUID.fromString(uuid)));
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#performQuery(org.jboss.dna.graph.query.model.QueryCommand,
-     *      org.jboss.dna.graph.query.validate.Schemata, org.jboss.dna.search.IndexContext)
-     */
-    public QueryResults performQuery( QueryCommand query,
-                                      Schemata schemata,
-                                      IndexContext indexes ) throws IOException, ParseException {
-        return this.queryEngine.execute(query, schemata, indexes);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#apply(Iterable, IndexContext)
-     */
-    public int apply( Iterable<ChangeRequest> changes,
-                      IndexContext indexes ) /*throws IOException*/{
-        for (ChangeRequest change : changes) {
-            if (change != null) continue;
-        }
-        return 0;
-    }
-}

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -55,7 +55,7 @@
  * A search engine dedicated to a single workspace.
  */
 @ThreadSafe
-public class WorkspaceSearchEngine {
+class WorkspaceSearchEngine {
 
     protected static final String PATHS_INDEX_NAME = "paths";
     protected static final String CONTENT_INDEX_NAME = "content";
@@ -67,7 +67,7 @@
     private final String sourceName;
     private final String workspaceName;
     private final RepositoryConnectionFactory connectionFactory;
-    private final IndexingStrategy indexingStrategy;
+    private final IndexStrategy indexingStrategy;
     protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
 
     /**
@@ -86,7 +86,7 @@
      */
     protected WorkspaceSearchEngine( ExecutionContext context,
                                      DirectoryConfiguration directoryFactory,
-                                     IndexingStrategy indexingStrategy,
+                                     IndexStrategy indexingStrategy,
                                      String sourceName,
                                      String workspaceName,
                                      RepositoryConnectionFactory connectionFactory ) throws SearchEngineException {
@@ -168,7 +168,7 @@
         return context.getValueFactories().getStringFactory().create(path);
     }
 
-    final IndexingStrategy strategy() {
+    final IndexStrategy strategy() {
         return indexingStrategy;
     }
 
@@ -523,7 +523,7 @@
         final List<Location> results = new ArrayList<Location>(maxResults);
         return new Search() {
             public void execute( IndexContext indexes ) throws IOException, ParseException {
-                strategy().performQuery(fullTextSearch, maxResults, offset, indexes, results);
+                strategy().search(fullTextSearch, maxResults, offset, indexes, results);
             }
 
             public String messageFor( Throwable error ) {
@@ -550,9 +550,11 @@
                                   final Schemata schemata ) {
         return new Query() {
             private QueryResults results = null;
+            private SearchContext context = null;
 
-            public void execute( IndexContext indexes ) throws IOException, ParseException {
-                results = strategy().performQuery(query, schemata, indexes);
+            public void execute( IndexContext indexes ) {
+                context = new SearchContext(indexes, schemata);
+                results = strategy().query(context, query);
             }
 
             public String messageFor( Throwable error ) {

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -25,7 +25,7 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
-import org.jboss.dna.search.IndexingRules.Builder;
+import org.jboss.dna.search.IndexRules.Builder;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -35,23 +35,23 @@
 public class IndexingRulesTest {
 
     private Builder builder;
-    private IndexingRules rules;
+    private IndexRules rules;
 
     @Before
     public void beforeEach() {
-        builder = IndexingRules.createBuilder();
+        builder = IndexRules.createBuilder();
         rules = builder.build();
     }
 
     @Test
     public void shouldBuildValidRulesFromBuilderThatIsNotInvoked() {
-        builder = IndexingRules.createBuilder();
+        builder = IndexRules.createBuilder();
         rules = builder.build();
     }
 
     @Test
     public void shouldBuildValidRulesFromBuilderAfterJustSettingDefaultRules() {
-        builder.defaultTo(IndexingRules.FULL_TEXT);
+        builder.defaultTo(IndexRules.FULL_TEXT);
         rules = builder.build();
         assertThat(rules.getRule(null).isFullText(), is(true));
     }

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -48,7 +48,6 @@
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
     private DirectoryConfiguration directoryFactory;
-    private IndexingStrategy indexingStrategy;
     private Graph content;
 
     @Before
@@ -81,15 +80,9 @@
             }
         };
 
-        // Set up the indexing strategy ...
-        IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
-                                           .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
-                                           .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(rules);
-
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();
-        engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory, indexingStrategy);
+        engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory);
     }
 
     protected Path path( String string ) {

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -47,7 +47,7 @@
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
     private DirectoryConfiguration directoryFactory;
-    private IndexingStrategy indexingStrategy;
+    private IndexStrategy indexingStrategy;
     private Graph content;
 
     @Before
@@ -74,10 +74,10 @@
         };
 
         // Set up the indexing strategy ...
-        IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
-                                           .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
+        IndexRules rules = IndexRules.createBuilder(KitchenSinkIndexStrategy.DEFAULT_RULES)
+                                           .defaultTo(IndexRules.INDEX | IndexRules.ANALYZE | IndexRules.FULL_TEXT)
                                            .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(rules);
+        indexingStrategy = new KitchenSinkIndexStrategy(rules);
 
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();


From dna-commits at lists.jboss.org  Mon Nov 16 18:25:37 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Nov 2009 18:25:37 -0500
Subject: [dna-commits] DNA SVN: r1320 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property and 17
	other directories.
Message-ID: <200911162325.nAGNPb2k022147@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-16 18:25:36 -0500 (Mon, 16 Nov 2009)
New Revision: 1320

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/filters/
   trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareNameQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/ComparePathQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/MatchNoneQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/NotQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/ScoreQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/query/
   trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java
Removed:
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueComparators.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
   trunk/dna-search/pom.xml
   trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
   trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Log:
DNA-467 Continued implementation of the dna-search components.  Current status is that the SearchEngine is nearly complete (feature-wise), but has had little testing.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -133,6 +133,8 @@
     public static I18n tableDoesNotExist;
     public static I18n columnDoesNotExistOnTable;
     public static I18n columnDoesNotExistInQuery;
+    public static I18n columnIsNotFullTextSearchable;
+    public static I18n tableIsNotFullTextSearchable;
     public static I18n selectorDoesNotExistInQuery;
     public static I18n propertyOnSelectorIsNotUsedInQuery;
     public static I18n errorResolvingNodesFromLocationsUsingSourceAndWorkspace;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueComparators.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueComparators.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueComparators.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -261,6 +261,19 @@
         }
     };
     /**
+     * A comparator of path segment values.
+     */
+    public static final Comparator<Path.Segment> PATH_SEGMENT_COMPARATOR = new Comparator<Path.Segment>() {
+
+        public int compare( Path.Segment o1,
+                            Path.Segment o2 ) {
+            if (o1 == o2) return 0;
+            if (o1 == null) return -1;
+            if (o2 == null) return 1;
+            return o1.compareTo(o2);
+        }
+    };
+    /**
      * A comparator of URI values.
      */
     public static final Comparator<URI> URI_COMPARATOR = new Comparator<URI>() {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -165,6 +165,24 @@
     }
 
     /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof QueryContext) {
+            QueryContext that = (QueryContext)obj;
+            if (!this.context.equals(that.getExecutionContext())) return false;
+            if (!this.schemata.equals(that.getSchemata())) return false;
+            if (!this.variables.equals(that.getVariables())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Obtain a copy of this context, except that the copy uses the supplied execution context.
      * 
      * @param context the execution context that should be used in the new query context

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -179,6 +179,7 @@
      * {@link QueryResults#getTuples() tuples} in the results, and which can be used to access the individual values in each of
      * the tuples.
      */
+    @Immutable
     public interface Columns {
         /**
          * Get the columns.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -190,25 +190,77 @@
     }
 
     /**
+     * A {@link Term} that represents a search term that requires another term to not appear.
+     */
+    public static class NegationTerm implements Term {
+        private final Term negated;
+
+        public NegationTerm( Term negatedTerm ) {
+            assert negatedTerm != null;
+            this.negated = negatedTerm;
+        }
+
+        /**
+         * Get the term that is negated.
+         * 
+         * @return the negated term; never null
+         */
+        public Term getNegatedTerm() {
+            return negated;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return negated.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof NegationTerm) {
+                NegationTerm that = (NegationTerm)obj;
+                return this.getNegatedTerm().equals(that.getNegatedTerm());
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "-" + negated.toString();
+        }
+    }
+
+    /**
      * A {@link Term} that represents a single search term. The term may be comprised of multiple words.
      */
     public static class SimpleTerm implements Term {
         private final String value;
-        private final boolean excluded;
         private final boolean quoted;
 
         /**
          * Create a simple term with the value and whether the term is excluded or included.
          * 
          * @param value the value that makes up the term
-         * @param excluded true if the term should not appear, or false if the term is required
          */
-        public SimpleTerm( String value,
-                           boolean excluded ) {
+        public SimpleTerm( String value ) {
             assert value != null;
             assert value.trim().length() > 0;
             this.value = value;
-            this.excluded = excluded;
             this.quoted = this.value.indexOf(' ') != -1;
         }
 
@@ -223,12 +275,12 @@
         }
 
         /**
-         * Get whether or not this term is expected to appear in the results.
+         * Get the values of this term if the term is quoted.
          * 
-         * @return true if the term is expected to not appear, or false if the term is expected to appear
+         * @return the array of terms; never null
          */
-        public boolean isExcluded() {
-            return excluded;
+        public String[] getValues() {
+            return value.split("/w");
         }
 
         /**
@@ -260,7 +312,6 @@
             if (obj == this) return true;
             if (obj instanceof SimpleTerm) {
                 SimpleTerm that = (SimpleTerm)obj;
-                if (this.isExcluded() != that.isExcluded()) return false;
                 return this.getValue().equals(that.getValue());
             }
             return false;
@@ -273,8 +324,7 @@
          */
         @Override
         public String toString() {
-            String value = quoted ? "\"" + this.value + "\"" : this.value;
-            return excluded ? "-" + value : value;
+            return quoted ? "\"" + this.value + "\"" : this.value;
         }
     }
 
@@ -352,7 +402,7 @@
     }
 
     /**
-     * A set of {@link Term}s that are ANDed together.
+     * A set of {@link Term}s that are ORed together.
      */
     public static class Disjunction extends CompoundTerm {
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -511,18 +511,23 @@
         }
 
         protected final void enqueue( Visitable objectToBeVisited ) {
-            itemQueue.add(objectToBeVisited);
+            if (objectToBeVisited != null) {
+                itemQueue.add(objectToBeVisited);
+            }
         }
 
         protected final void enqueue( Iterable<? extends Visitable> objectsToBeVisited ) {
             for (Visitable objectToBeVisited : objectsToBeVisited) {
-                itemQueue.add(objectToBeVisited);
+                if (objectToBeVisited != null) {
+                    itemQueue.add(objectToBeVisited);
+                }
             }
         }
 
         protected final void visitNext() {
             if (!itemQueue.isEmpty()) {
                 Visitable first = (Visitable)itemQueue.removeFirst();
+                assert first != null;
                 first.accept(this);
             }
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -31,6 +31,7 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
 import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
 import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
 import org.jboss.dna.graph.query.model.FullTextSearch.Term;
 
@@ -88,10 +89,9 @@
     }
 
     protected Term parseTerm( TokenStream tokens ) {
-        if (tokens.canConsume('-')) {
-            return new SimpleTerm(removeQuotes(tokens.consume()), true);
-        }
-        return new SimpleTerm(removeQuotes(tokens.consume()), false);
+        boolean negated = tokens.canConsume('-');
+        Term result = new SimpleTerm(removeQuotes(tokens.consume()));
+        return negated ? new NegationTerm(result) : result;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -53,6 +53,7 @@
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.query.validate.Validator;
 import org.jboss.dna.graph.query.validate.Schemata.Table;
 import org.jboss.dna.graph.query.validate.Schemata.View;
 
@@ -143,10 +144,28 @@
         // Process the orderings and limits ...
         plan = attachSorting(context, plan, query.getOrderings());
         plan = attachLimits(context, plan, query.getLimits());
+
+        // Validate that all the parts of the query are resolvable ...
+        validate(context, query, usedSources);
+
         return plan;
     }
 
     /**
+     * Validate the supplied query.
+     * 
+     * @param context the context in which the query is being planned
+     * @param query the set query to be planned
+     * @param usedSelectors the map of {@link SelectorName}s (aliases or names) used in the query.
+     */
+    protected void validate( QueryContext context,
+                             QueryCommand query,
+                             Map<SelectorName, Table> usedSelectors ) {
+        // Resolve everything ...
+        Visitors.visitAll(query, new Validator(context, usedSelectors));
+    }
+
+    /**
      * Create a canonical query plan for the given set query.
      * 
      * @param context the context in which the query is being planned

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -24,6 +24,7 @@
 package org.jboss.dna.graph.query.process;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
@@ -31,7 +32,6 @@
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.AllNodes;
-import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
 import org.jboss.dna.graph.query.model.Limit;
@@ -50,7 +50,7 @@
     protected final PlanNode accessNode;
     protected final SelectorName sourceName;
     protected final List<Column> projectedColumns;
-    protected final Constraint constraint;
+    protected final List<Constraint> andedConstraints;
     protected final Limit limit;
 
     protected AbstractAccessComponent( QueryContext context,
@@ -94,16 +94,13 @@
         }
 
         // Add the criteria ...
-        Constraint constraint = null;
+        List<Constraint> andedConstraints = null;
         for (PlanNode select : accessNode.findAllAtOrBelow(Type.SELECT)) {
             Constraint selectConstraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-            if (constraint != null) {
-                constraint = new And(constraint, selectConstraint);
-            } else {
-                constraint = selectConstraint;
-            }
+            if (andedConstraints == null) andedConstraints = new ArrayList<Constraint>();
+            andedConstraints.add(selectConstraint);
         }
-        this.constraint = constraint;
+        this.andedConstraints = andedConstraints != null ? andedConstraints : Collections.<Constraint>emptyList();
 
         // Find the limit ...
         Limit limit = Limit.NONE;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -51,8 +51,8 @@
 /**
  * An abstract {@link Processor} implementation that builds a tree of {@link ProcessingComponent} objects to perform the different
  * parts of the query processing logic. Subclasses are required to only implement one method: the
- * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer)} should create a ProcessorComponent object that will
- * perform the (low-level access) query described by the {@link PlanNode plan} given as a parameter.
+ * {@link #createAccessComponent(QueryCommand,QueryContext, PlanNode, Columns, Analyzer)} should create a ProcessorComponent
+ * object that will perform the (low-level access) query described by the {@link PlanNode plan} given as a parameter.
  */
 public abstract class QueryProcessor implements Processor {
 
@@ -81,18 +81,24 @@
 
             // Go through the plan and create the corresponding ProcessingComponents ...
             Analyzer analyzer = createAnalyzer(context);
-            ProcessingComponent component = createComponent(context, plan, columns, analyzer);
+            ProcessingComponent component = createComponent(command, context, plan, columns, analyzer);
             long nanos2 = System.nanoTime();
             statistics = statistics.withResultsFormulationTime(nanos2 - nanos);
-
-            // Now execute the component ...
             nanos = nanos2;
-            tuples = component.execute();
 
+            if (component != null) {
+                // Now execute the component ...
+                tuples = component.execute();
+            } else {
+                // There must have been an error ...
+                assert context.getProblems().hasErrors();
+                tuples = Collections.emptyList();
+            }
+
         } finally {
             statistics = statistics.withExecutionTime(System.nanoTime() - nanos);
         }
-        if (tuples == null) tuples = Collections.emptyList();
+        assert tuples != null;
         return new org.jboss.dna.graph.query.process.QueryResults(context, command, columns, statistics, tuples);
     }
 
@@ -100,7 +106,7 @@
      * Create an {@link Analyzer} implementation that should be used by the non-access {@link ProcessingComponent}s that evaluate
      * criteria. By default, this method returns null, which means that any criteria evaluation will likely be pushed down under
      * an {@link Type#ACCESS ACCESS} node (and thus handled by an
-     * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer) access component}.
+     * {@link #createAccessComponent(QueryCommand,QueryContext, PlanNode, Columns, Analyzer) access component}.
      * <p>
      * However, for more simple access components that are not capable of handling joins and other non-trivial criteria, simply
      * return an Analyzer implementation that implements the methods using the source.
@@ -116,13 +122,15 @@
     /**
      * Create the {@link ProcessingComponent} that processes a single {@link Type#ACCESS} branch of a query plan.
      * 
+     * @param originalQuery the original query that is being executed; never null
      * @param context the context in which query is being evaluated; never null
      * @param accessNode the node in the query plan that represents the {@link Type#ACCESS} plan; never null
      * @param resultColumns the columns that are to be returned; never null
      * @param analyzer the criteria analyzer; never null
      * @return the processing component; may not be null
      */
-    protected abstract ProcessingComponent createAccessComponent( QueryContext context,
+    protected abstract ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                  QueryContext context,
                                                                   PlanNode accessNode,
                                                                   Columns resultColumns,
                                                                   Analyzer analyzer );
@@ -133,17 +141,20 @@
      * {@link PlanNode} objects in the optimized query plan, and the method is actually recursive (since the optimized query plan
      * forms a tree). However, whenever this call structure reaches the {@link Type#ACCESS ACCESS} nodes in the query plan (which
      * each represents a separate atomic low-level query to the underlying system), the
-     * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer)} method is called. Subclasses should create an
-     * appropriate ProcessingComponent implementation that performs this atomic low-level query.
+     * {@link #createAccessComponent(QueryCommand,QueryContext, PlanNode, Columns, Analyzer)} method is called. Subclasses should
+     * create an appropriate ProcessingComponent implementation that performs this atomic low-level query.
      * 
+     * @param originalQuery the original query that is being executed; never null
      * @param context the context in which query is being evaluated
      * @param node the plan node for which the ProcessingComponent is to be created
      * @param columns the definition of the result columns for this portion of the query
      * @param analyzer the analyzer (returned from {@link #createAnalyzer(QueryContext)}) that should be used on the components
      *        that evaluate criteria; may be null if a best-effort should be made for the evaluation
-     * @return the processing component for this plan node; never null
+     * @return the processing component for this plan node; or null if there was an error recorded in the
+     *         {@link QueryContext#getProblems() problems}
      */
-    protected ProcessingComponent createComponent( QueryContext context,
+    protected ProcessingComponent createComponent( QueryCommand originalQuery,
+                                                   QueryContext context,
                                                    PlanNode node,
                                                    Columns columns,
                                                    Analyzer analyzer ) {
@@ -152,14 +163,18 @@
             case ACCESS:
                 // Create the component to handle the ACCESS node ...
                 assert node.getChildCount() == 1;
-                component = createAccessComponent(context, node, columns, analyzer);
+                component = createAccessComponent(originalQuery, context, node, columns, analyzer);
                 // // Don't do anything special with an access node at the moment ...
                 // component = createComponent(context, node.getFirstChild(), columns, analyzer);
                 break;
             case DUP_REMOVE:
                 // Create the component under the DUP_REMOVE ...
                 assert node.getChildCount() == 1;
-                ProcessingComponent distinctDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent distinctDelegate = createComponent(originalQuery,
+                                                                       context,
+                                                                       node.getFirstChild(),
+                                                                       columns,
+                                                                       analyzer);
                 component = new DistinctComponent(distinctDelegate);
                 break;
             case GROUP:
@@ -167,8 +182,8 @@
             case JOIN:
                 // Create the components under the JOIN ...
                 assert node.getChildCount() == 2;
-                ProcessingComponent left = createComponent(context, node.getFirstChild(), columns, analyzer);
-                ProcessingComponent right = createComponent(context, node.getLastChild(), columns, analyzer);
+                ProcessingComponent left = createComponent(originalQuery, context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent right = createComponent(originalQuery, context, node.getLastChild(), columns, analyzer);
                 // Create the join component ...
                 JoinAlgorithm algorithm = node.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class);
                 JoinType joinType = node.getProperty(Property.JOIN_TYPE, JoinType.class);
@@ -202,7 +217,11 @@
             case LIMIT:
                 // Create the component under the LIMIT ...
                 assert node.getChildCount() == 1;
-                ProcessingComponent limitDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent limitDelegate = createComponent(originalQuery,
+                                                                    context,
+                                                                    node.getFirstChild(),
+                                                                    columns,
+                                                                    analyzer);
                 // Then create the limit component ...
                 Integer rowLimit = node.getProperty(Property.LIMIT_COUNT, Integer.class);
                 Integer offset = node.getProperty(Property.LIMIT_OFFSET, Integer.class);
@@ -217,7 +236,11 @@
             case PROJECT:
                 // Create the component under the PROJECT ...
                 assert node.getChildCount() == 1;
-                ProcessingComponent projectDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent projectDelegate = createComponent(originalQuery,
+                                                                      context,
+                                                                      node.getFirstChild(),
+                                                                      columns,
+                                                                      analyzer);
                 // Then create the project component ...
                 List<Column> projectedColumns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
                 component = new ProjectComponent(projectDelegate, projectedColumns);
@@ -225,7 +248,11 @@
             case SELECT:
                 // Create the component under the SELECT ...
                 assert node.getChildCount() == 1;
-                ProcessingComponent selectDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent selectDelegate = createComponent(originalQuery,
+                                                                     context,
+                                                                     node.getFirstChild(),
+                                                                     columns,
+                                                                     analyzer);
                 // Then create the select component ...
                 Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
                 component = new SelectComponent(selectDelegate, constraint, context.getVariables(), analyzer);
@@ -234,7 +261,7 @@
                 // Create the components under the SET_OPERATION ...
                 List<ProcessingComponent> setDelegates = new LinkedList<ProcessingComponent>();
                 for (PlanNode child : node) {
-                    setDelegates.add(createComponent(context, child, columns, analyzer));
+                    setDelegates.add(createComponent(originalQuery, context, child, columns, analyzer));
                 }
                 // Then create the select component ...
                 Operation operation = node.getProperty(Property.SET_OPERATION, Operation.class);
@@ -255,7 +282,11 @@
             case SORT:
                 // Create the component under the SORT ...
                 assert node.getChildCount() == 1;
-                ProcessingComponent sortDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+                ProcessingComponent sortDelegate = createComponent(originalQuery,
+                                                                   context,
+                                                                   node.getFirstChild(),
+                                                                   columns,
+                                                                   analyzer);
                 // Then create the sort component ...
                 List<Object> orderBys = node.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
                 if (orderBys.isEmpty()) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -29,13 +29,24 @@
 
 @Immutable
 class ImmutableColumn implements Column {
+
+    public static final boolean DEFAULT_FULL_TEXT_SEARCHABLE = false;
+
+    private final boolean fullTextSearchable;
     private final String name;
     private final PropertyType type;
 
     protected ImmutableColumn( String name,
                                PropertyType type ) {
+        this(name, type, DEFAULT_FULL_TEXT_SEARCHABLE);
+    }
+
+    protected ImmutableColumn( String name,
+                               PropertyType type,
+                               boolean fullTextSearchable ) {
         this.name = name;
         this.type = type != null ? type : PropertyType.STRING;
+        this.fullTextSearchable = fullTextSearchable;
     }
 
     /**
@@ -59,6 +70,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.query.validate.Schemata.Column#isFullTextSearchable()
+     */
+    public boolean isFullTextSearchable() {
+        return fullTextSearchable;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#toString()
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -328,12 +328,34 @@
             CheckArg.isNotEmpty(tableName, "tableName");
             CheckArg.isNotEmpty(columnName, "columnName");
             CheckArg.isNotNull(type, "type");
+            return addColumn(tableName, columnName, type, ImmutableColumn.DEFAULT_FULL_TEXT_SEARCHABLE);
+        }
+
+        /**
+         * Add a column with the supplied name and type to the named table. Any existing column with that name will be replaced
+         * with the new column. If the table does not yet exist, it will be added.
+         * 
+         * @param tableName the name of the new table
+         * @param columnName the names of the column
+         * @param type the type for the column
+         * @param fullTextSearchable true if the column should be full-text searchable, or false if not
+         * @return this builder, for convenience in method chaining; never null
+         * @throws IllegalArgumentException if the table name is null or empty, the column name is null or empty, or if the
+         *         property type is null
+         */
+        public Builder addColumn( String tableName,
+                                  String columnName,
+                                  PropertyType type,
+                                  boolean fullTextSearchable ) {
+            CheckArg.isNotEmpty(tableName, "tableName");
+            CheckArg.isNotEmpty(columnName, "columnName");
+            CheckArg.isNotNull(type, "type");
             SelectorName selector = new SelectorName(tableName);
             ImmutableTable existing = tables.get(selector);
             ImmutableTable table = null;
             if (existing == null) {
                 List<Column> columns = new ArrayList<Column>();
-                columns.add(new ImmutableColumn(columnName, type));
+                columns.add(new ImmutableColumn(columnName, type, fullTextSearchable));
                 table = new ImmutableTable(selector, columns);
             } else {
                 table = existing.withColumn(columnName, type);
@@ -343,6 +365,37 @@
         }
 
         /**
+         * Make sure the column on the named table is searchable.
+         * 
+         * @param tableName the name of the new table
+         * @param columnName the names of the column
+         * @return this builder, for convenience in method chaining; never null
+         * @throws IllegalArgumentException if the table name is null or empty or if the column name is null or empty
+         */
+        public Builder makeSearchable( String tableName,
+                                       String columnName ) {
+            CheckArg.isNotEmpty(tableName, "tableName");
+            CheckArg.isNotEmpty(columnName, "columnName");
+            SelectorName selector = new SelectorName(tableName);
+            ImmutableTable existing = tables.get(selector);
+            ImmutableTable table = null;
+            if (existing == null) {
+                List<Column> columns = new ArrayList<Column>();
+                columns.add(new ImmutableColumn(columnName, PropertyType.STRING, true));
+                table = new ImmutableTable(selector, columns);
+            } else {
+                Column column = existing.getColumn(columnName);
+                PropertyType type = PropertyType.STRING;
+                if (column != null) {
+                    type = column.getPropertyType();
+                }
+                table = existing.withColumn(columnName, type, true);
+            }
+            tables.put(table.getName(), table);
+            return this;
+        }
+
+        /**
          * Add to the specified table a key that references the existing named columns.
          * 
          * @param tableName the name of the new table
@@ -419,7 +472,8 @@
                                                             "The view references a non-existant column '"
                                                             + column.getColumnName() + "' in '" + source.getName() + "'");
                         }
-                        viewColumns.add(new ImmutableColumn(viewColumnName, sourceColumn.getPropertyType()));
+                        viewColumns.add(new ImmutableColumn(viewColumnName, sourceColumn.getPropertyType(),
+                                                            sourceColumn.isFullTextSearchable()));
                     }
                     if (viewColumns.size() != columns.size()) {
                         // We weren't able to resolve all of the columns,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -187,10 +187,18 @@
         return new ImmutableTable(getName(), newColumns);
     }
 
+    public ImmutableTable withColumn( String name,
+                                      PropertyType type,
+                                      boolean fullTextSearchable ) {
+        List<Column> newColumns = new LinkedList<Column>(columns);
+        newColumns.add(new ImmutableColumn(name, type, fullTextSearchable));
+        return new ImmutableTable(getName(), newColumns);
+    }
+
     public ImmutableTable withColumns( Iterable<Column> columns ) {
         List<Column> newColumns = new LinkedList<Column>(this.getColumns());
         for (Column column : columns) {
-            newColumns.add(new ImmutableColumn(column.getName(), column.getPropertyType()));
+            newColumns.add(new ImmutableColumn(column.getName(), column.getPropertyType(), column.isFullTextSearchable()));
         }
         return new ImmutableTable(getName(), newColumns);
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -160,6 +160,13 @@
          * @return the property type; never null
          */
         PropertyType getPropertyType();
+
+        /**
+         * Get whether the column can be used in a full-text search.
+         * 
+         * @return true if the column is full-text searchable, or false otherwise
+         */
+        boolean isFullTextSearchable();
     }
 
     /**

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,339 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.validate;
+
+import java.util.Map;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.AllNodes;
+import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NamedSelector;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitor;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+
+/**
+ * A {@link Visitor} implementation that validates a query's used of a {@link Schemata} and records any problems as errors.
+ */
+public class Validator extends AbstractVisitor {
+
+    private final QueryContext context;
+    private final Problems problems;
+    private final Map<SelectorName, Table> selectorsByName;
+
+    /**
+     * @param context the query context
+     * @param selectorsByName the {@link Table tables} by their name or alias, as defined by the selectors
+     */
+    public Validator( QueryContext context,
+                      Map<SelectorName, Table> selectorsByName ) {
+        this.context = context;
+        this.problems = this.context.getProblems();
+        this.selectorsByName = selectorsByName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.AllNodes)
+     */
+    @Override
+    public void visit( AllNodes obj ) {
+        // this table doesn't have to be in the list of selected tables
+        verifyTable(obj.getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.ChildNode)
+     */
+    @Override
+    public void visit( ChildNode obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.ChildNodeJoinCondition)
+     */
+    @Override
+    public void visit( ChildNodeJoinCondition obj ) {
+        verify(obj.getParentSelectorName());
+        verify(obj.getChildSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.Column)
+     */
+    @Override
+    public void visit( Column obj ) {
+        verify(obj.getSelectorName(), obj.getPropertyName()); // don't care about the alias
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.DescendantNode)
+     */
+    @Override
+    public void visit( DescendantNode obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.DescendantNodeJoinCondition)
+     */
+    @Override
+    public void visit( DescendantNodeJoinCondition obj ) {
+        verify(obj.getAncestorSelectorName());
+        verify(obj.getDescendantSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+     */
+    @Override
+    public void visit( EquiJoinCondition obj ) {
+        verify(obj.getSelector1Name(), obj.getProperty1Name());
+        verify(obj.getSelector2Name(), obj.getProperty2Name());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.FullTextSearch)
+     */
+    @Override
+    public void visit( FullTextSearch obj ) {
+        SelectorName selectorName = obj.getSelectorName();
+        if (obj.getPropertyName() != null) {
+            Schemata.Column column = verify(selectorName, obj.getPropertyName());
+            if (column != null) {
+                // Make sure the column is full-text searchable ...
+                if (!column.isFullTextSearchable()) {
+                    problems.addError(GraphI18n.columnIsNotFullTextSearchable, column.getName(), selectorName);
+                }
+            }
+        } else {
+            Table table = verify(selectorName);
+            if (table != null) {
+                // Make sure there is at least one column on the table that is full-text searchable ...
+                boolean searchable = false;
+                for (Schemata.Column column : table.getColumns()) {
+                    if (column.isFullTextSearchable()) {
+                        searchable = true;
+                        break;
+                    }
+                }
+                if (!searchable) {
+                    problems.addError(GraphI18n.tableIsNotFullTextSearchable, selectorName);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.FullTextSearchScore)
+     */
+    @Override
+    public void visit( FullTextSearchScore obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.Length)
+     */
+    @Override
+    public void visit( Length obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.LowerCase)
+     */
+    @Override
+    public void visit( LowerCase obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.NamedSelector)
+     */
+    @Override
+    public void visit( NamedSelector obj ) {
+        verify(obj.getAliasOrName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.NodeDepth)
+     */
+    @Override
+    public void visit( NodeDepth obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.NodeLocalName)
+     */
+    @Override
+    public void visit( NodeLocalName obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.NodeName)
+     */
+    @Override
+    public void visit( NodeName obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.NodePath)
+     */
+    @Override
+    public void visit( NodePath obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+     */
+    @Override
+    public void visit( PropertyExistence obj ) {
+        verify(obj.getSelectorName(), obj.getPropertyName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+     */
+    @Override
+    public void visit( PropertyValue obj ) {
+        verify(obj.getSelectorName(), obj.getPropertyName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.SameNode)
+     */
+    @Override
+    public void visit( SameNode obj ) {
+        verify(obj.getSelectorName());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.SameNodeJoinCondition)
+     */
+    @Override
+    public void visit( SameNodeJoinCondition obj ) {
+        verify(obj.getSelector1Name());
+        verify(obj.getSelector2Name());
+    }
+
+    protected String string( Name name ) {
+        return context.getExecutionContext().getValueFactories().getStringFactory().create(name);
+    }
+
+    protected Table verify( SelectorName selectorName ) {
+        Table table = selectorsByName.get(selectorName);
+        if (table == null) {
+            problems.addError(GraphI18n.tableDoesNotExist, selectorName.getName());
+        }
+        return table;
+    }
+
+    protected Table verifyTable( SelectorName tableName ) {
+        Table table = selectorsByName.get(tableName);
+        if (table == null) {
+            problems.addError(GraphI18n.tableDoesNotExist, tableName.getName());
+        }
+        return table;
+    }
+
+    protected Schemata.Column verify( SelectorName selectorName,
+                                      Name propertyName ) {
+        Table table = selectorsByName.get(selectorName);
+        if (table == null) {
+            problems.addError(GraphI18n.tableDoesNotExist, selectorName.getName());
+            return null;
+        }
+        Schemata.Column column = table.getColumn(string(propertyName));
+        if (column == null) {
+            problems.addError(GraphI18n.columnDoesNotExistOnTable, string(propertyName), selectorName.getName());
+        }
+        return column;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-16 23:25:36 UTC (rev 1320)
@@ -121,6 +121,8 @@
 tableDoesNotExist = Table '{0}' does not exist
 columnDoesNotExistOnTable = Column '{0}' does not exist on the table '{1}'
 columnDoesNotExistInQuery = Column '{0}' does not exist in query
+columnIsNotFullTextSearchable = Column '{0}' on the table '{1}' does not support full-text searching
+tableIsNotFullTextSearchable = Table '{0}' has no columns that support full-text searching
 selectorDoesNotExistInQuery = Selector '{0}' does not exist in query
 propertyOnSelectorIsNotUsedInQuery = Property '{0}' on selector '{1}' is not used in query
 errorResolvingNodesFromLocationsUsingSourceAndWorkspace = Error resolving nodes from locations using '{1}' workspace in '{0}'

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -77,6 +77,8 @@
         builder.addTable("t1", "c11", "c12", "c13");
         builder.addTable("t2", "c21", "c22", "c23");
         builder.addTable("all", "a1", "a2", "a3", "a4", "primaryType", "mixins");
+        builder.makeSearchable("all", "a2");
+        builder.makeSearchable("all", "a1");
         builder.addKey("all", "a1");
         builder.addKey("all", "a3");
         builder.addView("v1", "SELECT c11, c12 AS c2 FROM t1 WHERE c13 < CAST('3' AS LONG)");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -34,6 +34,7 @@
 import org.jboss.dna.graph.query.model.FullTextSearch.CompoundTerm;
 import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
 import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
 import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
 import org.jboss.dna.graph.query.model.FullTextSearch.Term;
 import org.junit.Before;
@@ -159,9 +160,9 @@
         Disjunction disjunction = (Disjunction)result;
         assertThat(disjunction.getTerms().size(), is(4));
         Conjunction conjunction1 = (Conjunction)disjunction.getTerms().get(0);
-        SimpleTerm term3 = (SimpleTerm)disjunction.getTerms().get(1);
-        SimpleTerm term4 = (SimpleTerm)disjunction.getTerms().get(2);
-        SimpleTerm term5 = (SimpleTerm)disjunction.getTerms().get(3);
+        Term term3 = disjunction.getTerms().get(1);
+        Term term4 = disjunction.getTerms().get(2);
+        Term term5 = disjunction.getTerms().get(3);
         assertHasSimpleTerms(conjunction1, "term1", "term2");
         assertSimpleTerm(term3, "term3", true, false);
         assertSimpleTerm(term4, "term4", true, false);
@@ -172,12 +173,12 @@
                                              String... terms ) {
         List<Term> expectedTerms = new ArrayList<Term>();
         for (String term : terms) {
-            SimpleTerm expected = new SimpleTerm(term, false);
             if (term.startsWith("-")) {
                 term = term.substring(1);
-                expected = new SimpleTerm(term, true);
+                expectedTerms.add(new NegationTerm(new SimpleTerm(term)));
+            } else {
+                expectedTerms.add(new SimpleTerm(term));
             }
-            expectedTerms.add(expected);
         }
         assertHasTerms(compoundTerm, expectedTerms.toArray(new Term[expectedTerms.size()]));
     }
@@ -187,11 +188,20 @@
                                          boolean excluded,
                                          boolean quotingRequired ) {
         assertThat(term, is(notNullValue()));
-        assertThat(term, is(instanceOf(SimpleTerm.class)));
-        SimpleTerm simpleTerm = (SimpleTerm)term;
-        assertThat(simpleTerm.getValue(), is(value));
-        assertThat(simpleTerm.isExcluded(), is(excluded));
-        assertThat(simpleTerm.isQuotingRequired(), is(quotingRequired));
+        if (excluded) {
+            assertThat(term, is(instanceOf(NegationTerm.class)));
+            NegationTerm negationTerm = (NegationTerm)term;
+            Term negated = negationTerm.getNegatedTerm();
+            assertThat(negated, is(instanceOf(SimpleTerm.class)));
+            SimpleTerm simpleTerm = (SimpleTerm)negated;
+            assertThat(simpleTerm.getValue(), is(value));
+            assertThat(simpleTerm.isQuotingRequired(), is(quotingRequired));
+        } else {
+            assertThat(term, is(instanceOf(SimpleTerm.class)));
+            SimpleTerm simpleTerm = (SimpleTerm)term;
+            assertThat(simpleTerm.getValue(), is(value));
+            assertThat(simpleTerm.isQuotingRequired(), is(quotingRequired));
+        }
     }
 
     public static void assertHasTerms( CompoundTerm compoundTerm,

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -70,7 +70,7 @@
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
 import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
-import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
+import org.jboss.dna.graph.query.model.FullTextSearch.Term;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -680,15 +680,15 @@
     @Test
     public void shouldParseFullTextSearchExpressionFromStringWithValidExpression() {
         Position pos = new Position(100, 13);
-        FullTextSearch.Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
+        Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
         assertThat(result, is(notNullValue()));
         assertThat(result, is(instanceOf(Disjunction.class)));
         Disjunction disjunction = (Disjunction)result;
         assertThat(disjunction.getTerms().size(), is(4));
         Conjunction conjunction1 = (Conjunction)disjunction.getTerms().get(0);
-        SimpleTerm term3 = (SimpleTerm)disjunction.getTerms().get(1);
-        SimpleTerm term4 = (SimpleTerm)disjunction.getTerms().get(2);
-        SimpleTerm term5 = (SimpleTerm)disjunction.getTerms().get(3);
+        Term term3 = disjunction.getTerms().get(1);
+        Term term4 = disjunction.getTerms().get(2);
+        Term term5 = disjunction.getTerms().get(3);
         FullTextSearchParserTest.assertHasSimpleTerms(conjunction1, "term1", "term2");
         FullTextSearchParserTest.assertSimpleTerm(term3, "term3", true, false);
         FullTextSearchParserTest.assertSimpleTerm(term4, "term4", true, false);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -159,4 +159,66 @@
         assertThat(source.getChildCount(), is(0));
     }
 
+    @Test
+    public void shouldProduceErrorWhenFullTextSearchingTableWithNoSearchableColumns() {
+        schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
+        // Make sure the query without the search criteria does not have an error
+        query = builder.select("column1", "column2").from("someTable").query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(false));
+
+        query = builder.select("column1", "column2").from("someTable").where().search("someTable", "term1").end().query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(true));
+    }
+
+    @Test
+    public void shouldProducePlanWhenFullTextSearchingTableWithAtLeastOneSearchableColumn() {
+        schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3")
+                                  .makeSearchable("someTable", "column1")
+                                  .build();
+        query = builder.select("column1", "column4").from("someTable").where().search("someTable", "term1").end().query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(true));
+    }
+
+    @Test
+    public void shouldProduceErrorWhenFullTextSearchingColumnThatIsNotSearchable() {
+        schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
+        // Make sure the query without the search criteria does not have an error
+        query = builder.select("column1", "column2").from("someTable").query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(false));
+
+        query = builder.select("column1", "column2")
+                       .from("someTable")
+                       .where()
+                       .search("someTable", "column2", "term1")
+                       .end()
+                       .query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(true));
+    }
+
+    @Test
+    public void shouldProducePlanWhenFullTextSearchingColumnThatIsSearchable() {
+        schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3")
+                                  .makeSearchable("someTable", "column1")
+                                  .build();
+        query = builder.select("column1", "column4")
+                       .from("someTable")
+                       .where()
+                       .search("someTable", "column1", "term1")
+                       .end()
+                       .query();
+        queryContext = new QueryContext(context, schemata, hints, problems);
+        plan = planner.createPlan(queryContext, query);
+        assertThat(problems.hasErrors(), is(true));
+    }
+
 }

Modified: trunk/dna-search/pom.xml
===================================================================
--- trunk/dna-search/pom.xml	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/pom.xml	2009-11-16 23:25:36 UTC (rev 1320)
@@ -91,6 +91,20 @@
       <artifactId>log4j</artifactId>
       <scope>test</scope>
     </dependency>
+    
+    
+    
+    
+    
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-core</artifactId>
+      <version>2.0-beta1</version>
+    </dependency>
+
+
+
+
     <!-- 
     Java Concurrency in Practice annotations
     -->

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -28,7 +28,8 @@
 
 /**
  * Interface used to obtain the Lucene {@link Directory} instance that should be used for a workspace given the name of the
- * workspace. There are several implementations (see {@link DirectoryConfigurations}), but custom implementations can always be used.
+ * workspace. There are several implementations (see {@link DirectoryConfigurations}), but custom implementations can always be
+ * used.
  */
 @ThreadSafe
 public interface DirectoryConfiguration {
@@ -43,4 +44,16 @@
      */
     Directory getDirectory( String workspaceName,
                             String indexName ) throws SearchEngineException;
+
+    /**
+     * Destroy the {@link Directory} that is used for the workspace with the supplied name.
+     * 
+     * @param workspaceName the workspace name
+     * @param indexName the name of the index to be created
+     * @return true if the directory existed and was destroyed, or false if the directory didn't exist
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem creating the directory
+     */
+    boolean destroyDirectory( String workspaceName,
+                              String indexName ) throws SearchEngineException;
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -36,6 +36,7 @@
 import org.jboss.dna.common.text.NoOpEncoder;
 import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.FileUtil;
 import org.jboss.dna.common.util.HashCode;
 
 /**
@@ -146,6 +147,19 @@
         }
 
         /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.DirectoryConfiguration#destroyDirectory(java.lang.String, java.lang.String)
+         */
+        public boolean destroyDirectory( String workspaceName,
+                                         String indexName ) throws SearchEngineException {
+            CheckArg.isNotNull(workspaceName, "workspaceName");
+            IndexId id = new IndexId(workspaceName, indexName);
+            DirectoryType result = directories.remove(id);
+            return result != null ? doDestroy(result) : false;
+        }
+
+        /**
          * Method implemented by subclasses to create a new Directory implementation.
          * 
          * @param workspaceName the name of the workspace for which the {@link Directory} is to be created; never null
@@ -155,6 +169,8 @@
          */
         protected abstract DirectoryType createDirectory( String workspaceName,
                                                           String indexName ) throws SearchEngineException;
+
+        protected abstract boolean doDestroy( DirectoryType directory ) throws SearchEngineException;
     }
 
     /**
@@ -172,6 +188,16 @@
                                                 String indexName ) {
             return new RAMDirectory();
         }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.DirectoryConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
+         */
+        @Override
+        protected boolean doDestroy( RAMDirectory directory ) throws SearchEngineException {
+            return directory != null;
+        }
     }
 
     /**
@@ -304,6 +330,20 @@
         }
 
         /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.DirectoryConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
+         */
+        @Override
+        protected boolean doDestroy( FSDirectory directory ) throws SearchEngineException {
+                File file = directory.getFile();
+                if (file.exists()) {
+                    return FileUtil.delete(file);
+                }
+                return false;
+        }
+
+        /**
          * Override this method to define which subclass of {@link FSDirectory} should be created.
          * 
          * @param directory the file system directory; never null

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,1503 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.document.NumericField;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.WildcardQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
+import org.apache.lucene.search.regex.RegexQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Version;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.search.IndexRules.Rule;
+import org.jboss.dna.search.query.CompareNameQuery;
+import org.jboss.dna.search.query.ComparePathQuery;
+import org.jboss.dna.search.query.CompareStringQuery;
+import org.jboss.dna.search.query.MatchNoneQuery;
+import org.jboss.dna.search.query.NotQuery;
+import org.jboss.dna.search.query.UuidsQuery;
+
+/**
+ * A simple {@link IndexLayout} implementation that relies upon two separate indexes: one for the node content and a second one
+ * for paths and UUIDs.
+ */
+ at ThreadSafe
+public abstract class DualIndexLayout implements IndexLayout {
+
+    protected static final long MIN_DATE = 0;
+    protected static final long MAX_DATE = Long.MAX_VALUE;
+    protected static final long MIN_LONG = Long.MIN_VALUE;
+    protected static final long MAX_LONG = Long.MAX_VALUE;
+    protected static final double MIN_DOUBLE = Double.MIN_VALUE;
+    protected static final double MAX_DOUBLE = Double.MAX_VALUE;
+    protected static final int MIN_DEPTH = 0;
+    protected static final int MAX_DEPTH = 100;
+
+    protected static final String PATHS_INDEX_NAME = "paths";
+    protected static final String CONTENT_INDEX_NAME = "content";
+
+    protected static final String UUID_FIELD = "uuid";
+    protected static final String FULL_TEXT_SUFFIX = "/fs"; // the slash character is not allowed in a property name unescaped
+
+    static class PathIndex {
+        public static final String PATH = "path";
+        public static final String LOCAL_NAME = "name";
+        public static final String SNS_INDEX = "sns";
+        public static final String UUID = UUID_FIELD;
+        public static final String DEPTH = "depth";
+    }
+
+    static class ContentIndex {
+        public static final String UUID = UUID_FIELD;
+        public static final String FULL_TEXT = "fts";
+    }
+
+    /**
+     * The number of results that should be returned when performing queries while deleting entire branches of content. The
+     * current value is {@value} .
+     */
+    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
+
+    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+        }
+    };
+
+    /**
+     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
+     */
+    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
+        private static final long serialVersionUID = 1L;
+
+        public FieldSelectorResult accept( String fieldName ) {
+            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+        }
+    };
+
+    /**
+     * Get the date formatter that can be reused safely within the current thread.
+     * 
+     * @return the date formatter; never null
+     */
+    protected DateFormat dateFormatter() {
+        return dateFormatter.get();
+    }
+
+    /**
+     * Get the text encoder that should be used to encode namespaces in the search index.
+     * 
+     * @return the namespace text encoder; never null
+     */
+    protected TextEncoder getNamespaceEncoder() {
+        return new NoOpEncoder();
+    }
+
+    /**
+     * Create a Lucene {@link Analyzer} analyzer that should be used for indexing and searching.
+     * 
+     * @return the analyzer; never null
+     */
+    protected Analyzer createAnalyzer() {
+        return new StandardAnalyzer(Version.LUCENE_CURRENT);
+    }
+
+    protected abstract class LuceneSession implements IndexSession {
+        protected final ExecutionContext context;
+        protected final String sourceName;
+        protected final String workspaceName;
+        protected final IndexRules rules;
+        private final QueryEngine queryEngine;
+        private final Analyzer analyzer;
+        private final Directory pathsIndexDirectory;
+        private final Directory contentIndexDirectory;
+        protected final boolean overwrite;
+        protected final boolean readOnly;
+        protected final ValueFactory<String> stringFactory;
+        protected final DateTimeFactory dateFactory;
+        protected final PathFactory pathFactory;
+        private int changeCount;
+        private IndexReader pathsReader;
+        private IndexWriter pathsWriter;
+        private IndexSearcher pathsSearcher;
+        private IndexReader contentReader;
+        private IndexWriter contentWriter;
+        private IndexSearcher contentSearcher;
+
+        protected LuceneSession( ExecutionContext context,
+                                 String sourceName,
+                                 String workspaceName,
+                                 IndexRules rules,
+                                 Directory pathsIndexDirectory,
+                                 Directory contentIndexDirectory,
+                                 boolean overwrite,
+                                 boolean readOnly ) {
+            this.context = context;
+            this.sourceName = sourceName;
+            this.workspaceName = workspaceName;
+            this.rules = rules;
+            this.overwrite = overwrite;
+            this.readOnly = readOnly;
+            this.pathsIndexDirectory = pathsIndexDirectory;
+            this.contentIndexDirectory = contentIndexDirectory;
+            this.analyzer = createAnalyzer();
+            this.stringFactory = context.getValueFactories().getStringFactory();
+            this.dateFactory = context.getValueFactories().getDateFactory();
+            this.pathFactory = context.getValueFactories().getPathFactory();
+            assert this.context != null;
+            assert this.sourceName != null;
+            assert this.workspaceName != null;
+            assert this.rules != null;
+            assert this.analyzer != null;
+            assert this.pathsIndexDirectory != null;
+            assert this.contentIndexDirectory != null;
+            assert this.stringFactory != null;
+            assert this.dateFactory != null;
+            // do this last ...
+            this.queryEngine = createQueryProcessor();
+            assert this.queryEngine != null;
+        }
+
+        /**
+         * Create the field name that will be used to store the full-text searchable property values.
+         * 
+         * @param propertyName the name of the property; may not be null
+         * @return the field name for the full-text searchable property values; never null
+         */
+        protected String fullTextFieldName( String propertyName ) {
+            return propertyName + FULL_TEXT_SUFFIX;
+        }
+
+        protected IndexReader getPathsReader() throws IOException {
+            if (pathsReader == null) {
+                pathsReader = IndexReader.open(pathsIndexDirectory, readOnly);
+            }
+            return pathsReader;
+        }
+
+        protected IndexReader getContentReader() throws IOException {
+            if (contentReader == null) {
+                contentReader = IndexReader.open(contentIndexDirectory, readOnly);
+            }
+            return contentReader;
+        }
+
+        protected IndexWriter getPathsWriter() throws IOException {
+            assert !readOnly;
+            if (pathsWriter == null) {
+                pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+            }
+            return pathsWriter;
+        }
+
+        protected IndexWriter getContentWriter() throws IOException {
+            assert !readOnly;
+            if (contentWriter == null) {
+                contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+            }
+            return contentWriter;
+        }
+
+        protected IndexSearcher getPathsSearcher() throws IOException {
+            if (pathsSearcher == null) {
+                pathsSearcher = new IndexSearcher(getPathsReader());
+            }
+            return pathsSearcher;
+        }
+
+        protected IndexSearcher getContentSearcher() throws IOException {
+            if (contentSearcher == null) {
+                contentSearcher = new IndexSearcher(getContentReader());
+            }
+            return contentSearcher;
+        }
+
+        protected boolean hasWriters() {
+            return pathsWriter != null || contentWriter != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#getContext()
+         */
+        public final ExecutionContext getContext() {
+            return context;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#getSourceName()
+         */
+        public final String getSourceName() {
+            return sourceName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#getWorkspaceName()
+         */
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#hasChanges()
+         */
+        public boolean hasChanges() {
+            return changeCount > 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#index(org.jboss.dna.graph.Node)
+         */
+        public void index( Node node ) throws IOException {
+            assert !readOnly;
+            Location location = node.getLocation();
+            UUID uuid = location.getUuid();
+            if (uuid == null) uuid = UUID.randomUUID();
+            Path path = location.getPath();
+            String uuidStr = stringFactory.create(uuid);
+            String pathStr = pathAsString(path, stringFactory);
+            String nameStr = path.isRoot() ? "" : stringFactory.create(path.getLastSegment().getName());
+            int sns = path.isRoot() ? 1 : path.getLastSegment().getIndex();
+
+            Logger logger = Logger.getLogger(getClass());
+            if (logger.isTraceEnabled()) {
+                logger.trace("indexing {0}", pathStr);
+            }
+
+            // Create a separate document for the path, which makes it easier to handle moves since the path can
+            // be changed without changing any other content fields ...
+            Document doc = new Document();
+            doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+            doc.add(new Field(PathIndex.LOCAL_NAME, nameStr, Field.Store.YES, Field.Index.ANALYZED));
+            doc.add(new NumericField(PathIndex.LOCAL_NAME, Field.Store.YES, true).setIntValue(sns));
+            doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+            doc.add(new NumericField(PathIndex.DEPTH, Field.Store.YES, true).setIntValue(path.size()));
+            getPathsWriter().addDocument(doc);
+
+            // Create the document for the content (properties) ...
+            doc = new Document();
+            doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+            String stringValue = null;
+            StringBuilder fullTextSearchValue = null;
+            for (Property property : node.getProperties()) {
+                Name name = property.getName();
+                Rule rule = rules.getRule(name);
+                if (rule.isSkipped()) continue;
+                String nameString = stringFactory.create(name);
+                if (rule.isDate()) {
+                    for (Object value : property) {
+                        if (value == null) continue;
+                        DateTime dateValue = dateFactory.create(value);
+                        // Add a separate field for each property value ...
+                        doc.add(new NumericField(nameString, rule.getStoreOption(), true).setLongValue(dateValue.getMillisecondsInUtc()));
+                        // Dates are not added to the full-text search field (since this wouldn't make sense)
+                    }
+                    continue;
+                }
+                for (Object value : property) {
+                    if (value == null) continue;
+                    if (value instanceof Binary) {
+                        // don't include binary values as individual fields but do include them in the full-text search ...
+                        // TODO : add to full-text search ...
+                        continue;
+                    }
+                    stringValue = stringFactory.create(value);
+                    // Add a separate field for each property value ...
+                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+
+                    if (rule.isFullText()) {
+                        // Add this text to the full-text field ...
+                        if (fullTextSearchValue == null) {
+                            fullTextSearchValue = new StringBuilder();
+                        } else {
+                            fullTextSearchValue.append(' ');
+                        }
+                        fullTextSearchValue.append(stringValue);
+
+                        // Also create a full-text-searchable field ...
+                        String fullTextNameString = fullTextFieldName(nameString);
+                        doc.add(new Field(fullTextNameString, stringValue, Store.NO, Index.ANALYZED));
+                    }
+                }
+            }
+            // Add the full-text-search field ...
+            if (fullTextSearchValue != null) {
+                doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
+            }
+            getContentWriter().addDocument(doc);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#optimize()
+         */
+        public void optimize() throws IOException {
+            getContentWriter().optimize();
+            getPathsWriter().optimize();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#apply(java.lang.Iterable)
+         */
+        public int apply( Iterable<ChangeRequest> changes ) /*throws IOException*/{
+            for (ChangeRequest change : changes) {
+                if (change != null) continue;
+            }
+            return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes,
+         * we need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below
+         * a certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
+         * documents in the content index that represent those same nodes.
+         * </p>
+         * <p>
+         * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the
+         * number of results to a maximum number. We repeat batches as long as we find more results. This approach has the
+         * advantage that we'll never bring in a large number of results, and it allows us to delete the documents from the
+         * content node using a query.
+         * </p>
+         * 
+         * @see org.jboss.dna.search.IndexSession#deleteBelow(org.jboss.dna.graph.property.Path)
+         */
+        public int deleteBelow( Path path ) throws IOException {
+            assert !readOnly;
+            // Perform a query using the reader to find those nodes at/below the path ...
+            try {
+                IndexReader pathReader = getPathsReader();
+                IndexSearcher pathSearcher = new IndexSearcher(pathReader);
+                String pathStr = stringFactory.create(path) + "/";
+                PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
+                int numberDeleted = 0;
+                while (true) {
+                    // Execute the query and get the results ...
+                    TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
+                    int numResultsInBatch = results.scoreDocs.length;
+                    // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
+                    IndexReader contentReader = getContentReader();
+                    for (ScoreDoc result : results.scoreDocs) {
+                        int docId = result.doc;
+                        // Find the UUID of the node ...
+                        Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
+                        String uuid = doc.get(PathIndex.UUID);
+                        // Delete the document from the paths index ...
+                        pathReader.deleteDocument(docId);
+                        // Delete the corresponding document from the content index ...
+                        contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
+                    }
+                    numberDeleted += numResultsInBatch;
+                    if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
+                }
+                return numberDeleted;
+            } catch (FileNotFoundException e) {
+                // There are no index files yet, so nothing to delete ...
+                return 0;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#search(org.jboss.dna.graph.ExecutionContext, java.lang.String, int, int,
+         *      java.util.List)
+         */
+        public void search( ExecutionContext context,
+                            String fullTextString,
+                            int maxResults,
+                            int offset,
+                            List<Location> results ) throws IOException, ParseException {
+            assert fullTextString != null;
+            assert fullTextString.length() > 0;
+            assert offset >= 0;
+            assert maxResults > 0;
+            assert results != null;
+
+            // Parse the full-text search and search against the 'fts' field ...
+            QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+            Query query = parser.parse(fullTextString);
+            TopDocs docs = getContentSearcher().search(query, maxResults + offset);
+
+            // Collect the results ...
+            IndexReader contentReader = getContentReader();
+            IndexReader pathReader = getPathsReader();
+            IndexSearcher pathSearcher = getPathsSearcher();
+            ScoreDoc[] scoreDocs = docs.scoreDocs;
+            int numberOfResults = scoreDocs.length;
+            if (numberOfResults > offset) {
+                // There are enough results to satisfy the offset ...
+                PathFactory pathFactory = context.getValueFactories().getPathFactory();
+                for (int i = offset, num = scoreDocs.length; i != num; ++i) {
+                    ScoreDoc result = scoreDocs[i];
+                    int docId = result.doc;
+                    // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
+                    Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
+                    String uuid = doc.get(ContentIndex.UUID);
+                    // Find the path for this node (is there a better way to do this than one search per UUID?) ...
+                    TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
+                    if (pathDocs.scoreDocs.length < 1) {
+                        // No path record found ...
+                        continue;
+                    }
+                    Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+                    String pathString = pathDoc.get(PathIndex.PATH);
+                    Path path = pathFactory.create(pathString);
+                    // Now add the location ...
+                    results.add(Location.create(path, UUID.fromString(uuid)));
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#query(org.jboss.dna.graph.query.QueryContext,
+         *      org.jboss.dna.graph.query.model.QueryCommand)
+         */
+        public QueryResults query( QueryContext queryContext,
+                                   QueryCommand query ) {
+            return queryEngine.execute(queryContext, query);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#commit()
+         */
+        public void commit() throws IOException {
+            IOException ioError = null;
+            RuntimeException runtimeError = null;
+            if (pathsReader != null) {
+                try {
+                    pathsReader.close();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    pathsReader = null;
+                }
+            }
+            if (contentReader != null) {
+                try {
+                    contentReader.close();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    contentReader = null;
+                }
+            }
+            if (pathsWriter != null) {
+                try {
+                    pathsWriter.commit();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    try {
+                        pathsWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        pathsWriter = null;
+                    }
+                }
+            }
+            if (contentWriter != null) {
+                try {
+                    contentWriter.commit();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    try {
+                        contentWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        contentWriter = null;
+                    }
+                }
+            }
+            if (ioError != null) throw ioError;
+            if (runtimeError != null) throw runtimeError;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexSession#rollback()
+         */
+        public void rollback() throws IOException {
+            IOException ioError = null;
+            RuntimeException runtimeError = null;
+            if (pathsReader != null) {
+                try {
+                    pathsReader.close();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    pathsReader = null;
+                }
+            }
+            if (contentReader != null) {
+                try {
+                    contentReader.close();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    contentReader = null;
+                }
+            }
+            if (pathsWriter != null) {
+                try {
+                    pathsWriter.rollback();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    try {
+                        pathsWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        pathsWriter = null;
+                    }
+                }
+            }
+            if (contentWriter != null) {
+                try {
+                    contentWriter.rollback();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    try {
+                        contentWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        contentWriter = null;
+                    }
+                }
+            }
+            if (ioError != null) throw ioError;
+            if (runtimeError != null) throw runtimeError;
+        }
+
+        protected QueryEngine createQueryProcessor() {
+            // Create the query engine ...
+            Planner planner = new CanonicalPlanner();
+            Optimizer optimizer = new RuleBasedOptimizer() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+                 *      org.jboss.dna.graph.query.plan.PlanHints)
+                 */
+                @Override
+                protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+                                                  PlanHints hints ) {
+                    super.populateRuleStack(ruleStack, hints);
+                    // Add any custom rules here, either at the front of the stack or at the end
+                }
+            };
+            QueryProcessor processor = new QueryProcessor() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+                 *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
+                 *      org.jboss.dna.graph.query.QueryResults.Columns,
+                 *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+                 */
+                @Override
+                protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                     QueryContext context,
+                                                                     PlanNode accessNode,
+                                                                     Columns resultColumns,
+                                                                     org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+                    try {
+                        return LuceneSession.this.createAccessComponent(originalQuery,
+                                                                        context,
+                                                                        accessNode,
+                                                                        resultColumns,
+                                                                        analyzer);
+                    } catch (IOException e) {
+                        I18n msg = SearchI18n.errorWhilePerformingQuery;
+                        context.getProblems().addError(e,
+                                                       msg,
+                                                       Visitors.readable(originalQuery),
+                                                       getWorkspaceName(),
+                                                       getSourceName(),
+                                                       e.getMessage());
+                        return null;
+                    }
+                }
+            };
+
+            return new QueryEngine(planner, optimizer, processor);
+        }
+
+        protected abstract ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                      QueryContext context,
+                                                                      PlanNode accessNode,
+                                                                      Columns resultColumns,
+                                                                      org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer )
+            throws IOException;
+
+        /**
+         * Get the set of UUIDs for the children of the node at the given path.
+         * 
+         * @param parentPath the path to the parent node; may not be null
+         * @return the UUIDs of the child nodes; never null but possibly empty
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected Set<UUID> getUuidsForChildrenOf( Path parentPath ) throws IOException {
+            // Find the path of the parent ...
+            String stringifiedPath = pathAsString(parentPath, stringFactory);
+            // Append a '/' to the parent path, so we'll only get decendants ...
+            stringifiedPath = stringifiedPath + '/';
+
+            // Create a query to find all the nodes below the parent path ...
+            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
+            // Include only the children ...
+            int childrenDepth = parentPath.size() + 1;
+            Query depthQuery = NumericRangeQuery.newIntRange(PathIndex.DEPTH, childrenDepth, childrenDepth, true, true);
+            // And combine ...
+            BooleanQuery combinedQuery = new BooleanQuery();
+            combinedQuery.add(query, Occur.MUST);
+            combinedQuery.add(depthQuery, Occur.MUST);
+            query = combinedQuery;
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return uuidCollector.getUuids();
+        }
+
+        /**
+         * Get the set of UUIDs for the nodes that are descendants of the node at the given path.
+         * 
+         * @param parentPath the path to the parent node; may not be null and <i>may not be the root node</i>
+         * @param includeParent true if the parent node should be included in the results, or false if only the descendants should
+         *        be included
+         * @return the UUIDs of the nodes; never null but possibly empty
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected Set<UUID> getUuidsForDescendantsOf( Path parentPath,
+                                                      boolean includeParent ) throws IOException {
+            assert !parentPath.isRoot();
+
+            // Find the path of the parent ...
+            String stringifiedPath = pathAsString(parentPath, stringFactory);
+            if (!includeParent) {
+                // Append a '/' to the parent path, and we'll only get decendants ...
+                stringifiedPath = stringifiedPath + '/';
+            }
+
+            // Create a prefix query ...
+            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return uuidCollector.getUuids();
+        }
+
+        /**
+         * Get the set containing the single UUID for the node at the given path.
+         * 
+         * @param path the path to the node; may not be null
+         * @return the UUID of the supplied node; or null if the node cannot be found
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected UUID getUuidFor( Path path ) throws IOException {
+            // Create a query to find all the nodes below the parent path ...
+            IndexSearcher searcher = getPathsSearcher();
+            String stringifiedPath = pathAsString(path, stringFactory);
+            TermQuery query = new TermQuery(new Term(PathIndex.PATH, stringifiedPath));
+
+            // Now execute and collect the UUIDs ...
+            TopDocs topDocs = searcher.search(query, 1);
+            if (topDocs.totalHits == 0) return null;
+            Document pathDoc = getPathsReader().document(topDocs.scoreDocs[0].doc);
+            String uuidString = pathDoc.get(PathIndex.UUID);
+            return UUID.fromString(uuidString);
+        }
+
+        /**
+         * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
+         * 
+         * @param uuids the UUIDs of the nodes that are to be found; may not be null
+         * @return the query; never null
+         */
+        protected Query findAllNodesWithUuids( Set<UUID> uuids ) {
+            if (uuids.isEmpty()) {
+                // There are no children, so return a null query ...
+                return new MatchNoneQuery();
+            }
+            if (uuids.size() == 1) {
+                UUID uuid = uuids.iterator().next();
+                if (uuid == null) return new MatchNoneQuery();
+                return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+            }
+            if (uuids.size() < 50) {
+                // Create an OR boolean query for all the UUIDs, since this is probably more efficient ...
+                BooleanQuery query = new BooleanQuery();
+                for (UUID uuid : uuids) {
+                    Query uuidQuery = new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+                    query.add(uuidQuery, Occur.SHOULD);
+                }
+                return query;
+            }
+            // Returna query that will always find all of the UUIDs ...
+            return new UuidsQuery(ContentIndex.UUID, uuids, getContext().getValueFactories().getUuidFactory());
+        }
+
+        protected Query findAllNodesBelow( Path ancestorPath ) throws IOException {
+            if (ancestorPath.isRoot()) {
+                return new MatchAllDocsQuery();
+            }
+            Set<UUID> uuids = getUuidsForDescendantsOf(ancestorPath, false);
+            return findAllNodesWithUuids(uuids);
+        }
+
+        /**
+         * Return a query that can be used to find all of the documents that represent nodes that are children of the node at the
+         * supplied path.
+         * 
+         * @param parentPath the path of the parent node.
+         * @return the query; never null
+         * @throws IOException if there is an error finding the UUIDs of the child nodes
+         */
+        protected Query findChildNodes( Path parentPath ) throws IOException {
+            if (parentPath.isRoot()) {
+                return new MatchAllDocsQuery();
+            }
+            Set<UUID> childUuids = getUuidsForChildrenOf(parentPath);
+            return findAllNodesWithUuids(childUuids);
+        }
+
+        /**
+         * Create a query that can be used to find the one document (or node) that exists at the exact path supplied. This method
+         * first queries the {@link PathIndex path index} to find the UUID of the node at the supplied path, and then returns a
+         * query that matches the UUID.
+         * 
+         * @param path the path of the node
+         * @return the query; never null
+         * @throws IOException if there is an error finding the UUID for the supplied path
+         */
+        protected Query findNodeAt( Path path ) throws IOException {
+            UUID uuid = getUuidFor(path);
+            if (uuid == null) return null;
+            return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+        }
+
+        /**
+         * Create a query that can be used to find documents (or nodes) that have a field value that satisfies the supplied LIKE
+         * expression.
+         * 
+         * @param fieldName the name of the document field to search
+         * @param likeExpression the JCR like expression
+         * @return the query; never null
+         */
+        protected Query findNodesLike( String fieldName,
+                                       String likeExpression ) {
+            assert likeExpression != null;
+            assert likeExpression.length() > 0;
+
+            // '%' matches 0 or more characters
+            // '_' matches any single character
+            // '\x' matches 'x'
+            // all other characters match themselves
+
+            // Wildcard queries are a better match, but they can be slow and should not be used
+            // if the first character of the expression is a '%' or '_' ...
+            char firstChar = likeExpression.charAt(0);
+            if (firstChar != '%' && firstChar != '_') {
+                // Create a wildcard query ...
+                String expression = toWildcardExpression(likeExpression);
+                return new WildcardQuery(new Term(fieldName, expression));
+            }
+            // Create a regex query,
+            String regex = toRegularExpression(likeExpression);
+            RegexQuery query = new RegexQuery(new Term(fieldName, regex));
+            query.setRegexImplementation(new JavaUtilRegexCapabilities());
+            return query;
+        }
+
+        protected Query findNodesWith( PropertyValue propertyValue,
+                                       Operator operator,
+                                       Object value,
+                                       boolean caseSensitive ) {
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            PropertyType valueType = PropertyType.discoverType(value);
+            ValueFactories factories = context.getValueFactories();
+            switch (valueType) {
+                case NAME:
+                case PATH:
+                case REFERENCE:
+                case URI:
+                case UUID:
+                case STRING:
+                    String stringValue = stringFactory.create(value);
+                    if (valueType == PropertyType.PATH) {
+                        stringValue = pathAsString(pathFactory.create(value), stringFactory);
+                    }
+                    if (!caseSensitive) stringValue = stringValue.toLowerCase();
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return new TermQuery(new Term(field, stringValue));
+                        case NOT_EQUAL_TO:
+                            Query query = new TermQuery(new Term(field, stringValue));
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThan(stringValue,
+                                                                                              field,
+                                                                                              factories,
+                                                                                              caseSensitive);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(stringValue,
+                                                                                                       field,
+                                                                                                       factories,
+                                                                                                       caseSensitive);
+                        case LESS_THAN:
+                            return CompareStringQuery.createQueryForNodesWithFieldLessThan(stringValue,
+                                                                                           field,
+                                                                                           factories,
+                                                                                           caseSensitive);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(stringValue,
+                                                                                                    field,
+                                                                                                    factories,
+                                                                                                    caseSensitive);
+                        case LIKE:
+                            return findNodesLike(field, stringValue);
+                    }
+                    break;
+                case DATE:
+                    long date = factories.getLongFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, date, date, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newLongRange(field, date, date, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case LONG:
+                    long longValue = factories.getLongFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case DECIMAL:
+                case DOUBLE:
+                    double doubleValue = factories.getDoubleFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case BOOLEAN:
+                    boolean booleanValue = factories.getBooleanFactory().create(value);
+                    stringValue = stringFactory.create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return new TermQuery(new Term(field, stringValue));
+                        case NOT_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(!booleanValue)));
+                        case GREATER_THAN:
+                            if (!booleanValue) {
+                                return new TermQuery(new Term(field, stringFactory.create(true)));
+                            }
+                            // Can't be greater than 'true', per JCR spec
+                            return new MatchNoneQuery();
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(true)));
+                        case LESS_THAN:
+                            if (booleanValue) {
+                                return new TermQuery(new Term(field, stringFactory.create(false)));
+                            }
+                            // Can't be less than 'false', per JCR spec
+                            return new MatchNoneQuery();
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(false)));
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case OBJECT:
+                case BINARY:
+                    // This is not allowed ...
+                    assert false;
+                    return null;
+            }
+            return null;
+
+        }
+
+        protected Query findNodesWith( NodePath nodePath,
+                                       Operator operator,
+                                       Object value,
+                                       boolean caseSensitive ) throws IOException {
+            if (!caseSensitive) value = stringFactory.create(value).toLowerCase();
+            Path pathValue = operator != Operator.LIKE ? pathFactory.create(value) : null;
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    return findNodeAt(pathValue);
+                case NOT_EQUAL_TO:
+                    return new NotQuery(findNodeAt(pathValue));
+                case LIKE:
+                    String likeExpression = stringFactory.create(value);
+                    return findNodesLike(PathIndex.PATH, likeExpression);
+                case GREATER_THAN:
+                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThan(pathValue,
+                                                                                    PathIndex.PATH,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThanOrEqualTo(pathValue,
+                                                                                             PathIndex.PATH,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = ComparePathQuery.createQueryForNodesWithPathLessThan(pathValue,
+                                                                                 PathIndex.PATH,
+                                                                                 context.getValueFactories(),
+                                                                                 caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = ComparePathQuery.createQueryForNodesWithPathLessThanOrEqualTo(pathValue,
+                                                                                          PathIndex.PATH,
+                                                                                          context.getValueFactories(),
+                                                                                          caseSensitive);
+                    break;
+            }
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        protected Query findNodesWith( NodeName nodeName,
+                                       Operator operator,
+                                       Object value,
+                                       boolean caseSensitive ) throws IOException {
+            String stringValue = stringFactory.create(value);
+            if (!caseSensitive) stringValue = stringValue.toLowerCase();
+            Path.Segment segment = operator != Operator.LIKE ? pathFactory.createSegment(stringValue) : null;
+            int snsIndex = operator != Operator.LIKE ? segment.getIndex() : 0;
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    BooleanQuery booleanQuery = new BooleanQuery();
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
+                                     Occur.MUST);
+                    return booleanQuery;
+                case NOT_EQUAL_TO:
+                    booleanQuery = new BooleanQuery();
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
+                                     Occur.MUST);
+                    return new NotQuery(booleanQuery);
+                case GREATER_THAN:
+                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThan(segment,
+                                                                                    PathIndex.LOCAL_NAME,
+                                                                                    PathIndex.SNS_INDEX,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThanOrEqualTo(segment,
+                                                                                             PathIndex.LOCAL_NAME,
+                                                                                             PathIndex.SNS_INDEX,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = CompareNameQuery.createQueryForNodesWithNameLessThan(segment,
+                                                                                 PathIndex.LOCAL_NAME,
+                                                                                 PathIndex.SNS_INDEX,
+                                                                                 context.getValueFactories(),
+                                                                                 caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = CompareNameQuery.createQueryForNodesWithNameLessThanOrEqualTo(segment,
+                                                                                          PathIndex.LOCAL_NAME,
+                                                                                          PathIndex.SNS_INDEX,
+                                                                                          context.getValueFactories(),
+                                                                                          caseSensitive);
+                    break;
+                case LIKE:
+                    // See whether the like expression has brackets ...
+                    String likeExpression = stringValue;
+                    int openBracketIndex = likeExpression.indexOf('[');
+                    if (openBracketIndex != -1) {
+                        String localNameExpression = likeExpression.substring(0, openBracketIndex);
+                        String snsIndexExpression = likeExpression.substring(openBracketIndex);
+                        Query localNameQuery = createLocalNameQuery(localNameExpression);
+                        Query snsQuery = createSnsIndexQuery(snsIndexExpression);
+                        if (localNameQuery == null) {
+                            if (snsQuery == null) {
+                                query = new MatchNoneQuery();
+                            } else {
+                                // There is just an SNS part ...
+                                query = snsQuery;
+                            }
+                        } else {
+                            // There is a local name part ...
+                            if (snsQuery == null) {
+                                query = localNameQuery;
+                            } else {
+                                // There is both a local name part and a SNS part ...
+                                booleanQuery = new BooleanQuery();
+                                booleanQuery.add(localNameQuery, Occur.MUST);
+                                booleanQuery.add(snsQuery, Occur.MUST);
+                                query = booleanQuery;
+                            }
+                        }
+                    } else {
+                        // There is no SNS expression ...
+                        query = createLocalNameQuery(likeExpression);
+                    }
+                    assert query != null;
+                    break;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        protected Query findNodesWith( NodeLocalName nodeName,
+                                       Operator operator,
+                                       Object value,
+                                       boolean caseSensitive ) throws IOException {
+            String nameValue = stringFactory.create(value);
+            Query query = null;
+            switch (operator) {
+                case LIKE:
+                    String likeExpression = stringFactory.create(value);
+                    return findNodesLike(PathIndex.LOCAL_NAME, likeExpression); // already is a query with UUIDs
+                case EQUAL_TO:
+                    query = new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue));
+                    break;
+                case NOT_EQUAL_TO:
+                    query = new NotQuery(new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue)));
+                    break;
+                case GREATER_THAN:
+                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThan(nameValue,
+                                                                                       PathIndex.LOCAL_NAME,
+                                                                                       context.getValueFactories(),
+                                                                                       caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(nameValue,
+                                                                                                PathIndex.LOCAL_NAME,
+                                                                                                context.getValueFactories(),
+                                                                                                caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = CompareStringQuery.createQueryForNodesWithFieldLessThan(nameValue,
+                                                                                    PathIndex.LOCAL_NAME,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(nameValue,
+                                                                                             PathIndex.LOCAL_NAME,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        protected Query findNodesWith( NodeDepth depthConstraint,
+                                       Operator operator,
+                                       Object value ) throws IOException {
+            int depth = context.getValueFactories().getLongFactory().create(value).intValue();
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
+                    break;
+                case NOT_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
+                    query = new NotQuery(query);
+                    break;
+                case GREATER_THAN:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, false, true);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, true, true);
+                    break;
+                case LESS_THAN:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, false);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, true);
+                    break;
+                case LIKE:
+                    // This is not allowed ...
+                    return null;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        protected Query createLocalNameQuery( String likeExpression ) {
+            if (likeExpression == null) return null;
+            likeExpression = likeExpression.trim();
+            if (likeExpression.length() == 0) return null;
+            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
+                // The local name is a like ...
+                return findNodesLike(PathIndex.LOCAL_NAME, likeExpression);
+            }
+            // The local name is an exact match ...
+            return new TermQuery(new Term(PathIndex.LOCAL_NAME, likeExpression));
+        }
+
+        protected Query createSnsIndexQuery( String likeExpression ) {
+            if (likeExpression == null) return null;
+            likeExpression = likeExpression.trim();
+            if (likeExpression.length() == 0) return null;
+
+            // Remove the leading '[' ...
+            assert likeExpression.charAt(0) == '[';
+            likeExpression = likeExpression.substring(1);
+
+            // Remove the trailing ']' if it exists ...
+            int closeBracketIndex = likeExpression.indexOf(']');
+            if (closeBracketIndex != -1) {
+                likeExpression = likeExpression.substring(0, closeBracketIndex);
+            }
+            // If SNS expression contains '?' or '*' ...
+            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
+                // There is a LIKE expression for the SNS ...
+                return findNodesLike(PathIndex.SNS_INDEX, likeExpression);
+            }
+            // This is not a LIKE expression but an exact value specification and should be a number ...
+            try {
+                // This SNS is just a number ...
+                int sns = Integer.parseInt(likeExpression);
+                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, sns, sns, true, false);
+            } catch (NumberFormatException e) {
+                // It's not a number but it's in the SNS field, so there will be no results ...
+                return new MatchNoneQuery();
+            }
+        }
+
+    }
+
+    /**
+     * Convert the JCR like expression to a Lucene wildcard expression. The JCR like expression uses '%' to match 0 or more
+     * characters, '_' to match any single character, '\x' to match the 'x' character, and all other characters to match
+     * themselves.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    protected static String toWildcardExpression( String likeExpression ) {
+        assert likeExpression != null;
+        assert likeExpression.length() > 0;
+        return likeExpression.replace('%', '*').replace('_', '?').replaceAll("\\\\(.)", "$1");
+    }
+
+    /**
+     * Convert the JCR like expression to a regular expression. The JCR like expression uses '%' to match 0 or more characters,
+     * '_' to match any single character, '\x' to match the 'x' character, and all other characters to match themselves. Note that
+     * if any regex metacharacters appear in the like expression, they will be escaped within the resulting regular expression.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    protected static String toRegularExpression( String likeExpression ) {
+        assert likeExpression != null;
+        assert likeExpression.length() > 0;
+        // Replace all '\x' with 'x' ...
+        String result = likeExpression.replaceAll("\\\\(.)", "$1");
+        // Escape characters used as metacharacters in regular expressions, including
+        // '[', '^', '\', '$', '.', '|', '?', '*', '+', '(', and ')'
+        result = result.replaceAll("([[^\\\\$.|?*+()])", "\\$1");
+        // Replace '%'->'[.]+' and '_'->'[.]
+        result = likeExpression.replace("%", "[.]+").replace("_", "[.]");
+        return result;
+    }
+
+    protected static String pathAsString( Path path,
+                                          ValueFactory<String> stringFactory ) {
+        assert path != null;
+        if (path.isRoot()) return "/";
+        String pathStr = stringFactory.create(path);
+        if (!pathStr.endsWith("]")) {
+            pathStr = pathStr + '[' + Path.DEFAULT_INDEX + ']';
+        }
+        return pathStr;
+    }
+
+    /**
+     * A {@link Collector} implementation that only captures the UUID of the documents returned by a query. Score information is
+     * not recorded. This is often used when querying the {@link PathIndex} to collect the UUIDs of a set of nodes satisfying some
+     * path constraint.
+     * 
+     * @see DualIndexLayout.LuceneSession#findChildNodes(Path)
+     */
+    protected static class UuidCollector extends Collector {
+        private final Set<UUID> uuids = new HashSet<UUID>();
+        private String[] uuidsByDocId;
+        private int baseDocId;
+
+        protected UuidCollector() {
+        }
+
+        /**
+         * Get the UUIDs that have been collected.
+         * 
+         * @return the set of UUIDs; never null
+         */
+        public Set<UUID> getUuids() {
+            return uuids;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
+         */
+        @Override
+        public boolean acceptsDocsOutOfOrder() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
+         */
+        @Override
+        public void setScorer( Scorer scorer ) {
+            // we don't care about scoring
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#collect(int)
+         */
+        @Override
+        public void collect( int doc ) {
+            int index = doc - baseDocId;
+            assert index >= 0;
+            String uuidString = uuidsByDocId[index];
+            assert uuidString != null;
+            uuids.add(UUID.fromString(uuidString));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public void setNextReader( IndexReader reader,
+                                   int docBase ) throws IOException {
+            this.uuidsByDocId = FieldCache.DEFAULT.getStrings(reader, UUID_FIELD);
+            this.baseDocId = docBase;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,399 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.UUID;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.PrefixQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.util.Version;
-import org.jboss.dna.common.text.NoOpEncoder;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.util.Logger;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.ValueFactory;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryEngine;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.optimize.Optimizer;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.plan.Planner;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.QueryProcessor;
-import org.jboss.dna.search.IndexRules.Rule;
-
-/**
- * A simple {@link IndexStrategy} implementation that relies upon two separate indexes: one for the node content and a second
- * one for paths and UUIDs.
- */
- at ThreadSafe
-abstract class DualIndexStrategy implements IndexStrategy {
-
-    static class PathIndex {
-        public static final String PATH = "path";
-        public static final String UUID = "uuid";
-    }
-
-    static class ContentIndex {
-        public static final String UUID = PathIndex.UUID;
-        public static final String FULL_TEXT = "fts";
-    }
-
-    /**
-     * The number of results that should be returned when performing queries while deleting entire branches of content. The
-     * current value is {@value} .
-     */
-    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
-
-    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
-        }
-    };
-
-    /**
-     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
-     */
-    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
-        private static final long serialVersionUID = 1L;
-
-        public FieldSelectorResult accept( String fieldName ) {
-            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
-        }
-    };
-
-    private final IndexRules rules;
-    private final Logger logger;
-    private final QueryEngine queryEngine;
-
-    /**
-     * Create a new indexing strategy instance.
-     * 
-     * @param rules the indexing rules that govern how properties are to be index; may not be null
-     */
-    protected DualIndexStrategy( IndexRules rules ) {
-        assert rules != null;
-        this.rules = rules;
-        this.logger = Logger.getLogger(getClass());
-        // Create the query engine ...
-        Planner planner = new CanonicalPlanner();
-        Optimizer optimizer = new RuleBasedOptimizer() {
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
-             *      org.jboss.dna.graph.query.plan.PlanHints)
-             */
-            @Override
-            protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
-                                              PlanHints hints ) {
-                super.populateRuleStack(ruleStack, hints);
-                // Add any custom rules here, either at the front of the stack or at the end
-            }
-        };
-        QueryProcessor processor = new QueryProcessor() {
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
-             *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
-             *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-             */
-            @Override
-            protected ProcessingComponent createAccessComponent( QueryContext context,
-                                                                 PlanNode accessNode,
-                                                                 Columns resultColumns,
-                                                                 org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
-                return DualIndexStrategy.this.createAccessComponent((SearchContext)context, accessNode, resultColumns, analyzer);
-            }
-        };
-
-        this.queryEngine = new QueryEngine(planner, optimizer, processor);
-    }
-
-    protected abstract ProcessingComponent createAccessComponent( SearchContext context,
-                                                                  PlanNode accessNode,
-                                                                  Columns resultColumns,
-                                                                  org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer );
-
-    /**
-     * Utility method to obtain a {@link DateFormat} instance that can be used safely within a single thread.
-     * 
-     * @return the date formatter; never null
-     */
-    protected final DateFormat dateFormatter() {
-        return dateFormatter.get();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#getNamespaceEncoder()
-     */
-    public TextEncoder getNamespaceEncoder() {
-        return new NoOpEncoder();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#getChangeCountForAutomaticOptimization()
-     */
-    public int getChangeCountForAutomaticOptimization() {
-        return 0;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#createAnalyzer()
-     */
-    public Analyzer createAnalyzer() {
-        return new StandardAnalyzer(Version.LUCENE_CURRENT);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
-     * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
-     * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
-     * documents in the content index that represent those same nodes.
-     * </p>
-     * <p>
-     * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
-     * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
-     * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
-     * query.
-     * </p>
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#deleteBelow(Path, IndexContext)
-     */
-    public int deleteBelow( Path path,
-                            IndexContext indexes ) throws IOException {
-        // Perform a query using the reader to find those nodes at/below the path ...
-        try {
-            IndexReader pathReader = indexes.getPathsReader();
-            IndexSearcher pathSearcher = new IndexSearcher(pathReader);
-            String pathStr = indexes.stringFactory().create(path) + "/";
-            PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
-            int numberDeleted = 0;
-            while (true) {
-                // Execute the query and get the results ...
-                TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
-                int numResultsInBatch = results.scoreDocs.length;
-                // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
-                IndexReader contentReader = indexes.getContentReader();
-                for (ScoreDoc result : results.scoreDocs) {
-                    int docId = result.doc;
-                    // Find the UUID of the node ...
-                    Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
-                    String uuid = doc.get(PathIndex.UUID);
-                    // Delete the document from the paths index ...
-                    pathReader.deleteDocument(docId);
-                    // Delete the corresponding document from the content index ...
-                    contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
-                }
-                numberDeleted += numResultsInBatch;
-                if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
-            }
-            indexes.commit();
-            return numberDeleted;
-        } catch (FileNotFoundException e) {
-            // There are no index files yet, so nothing to delete ...
-            return 0;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#index(Node, IndexContext)
-     */
-    public void index( Node node,
-                       IndexContext indexes ) throws IOException {
-        ValueFactory<String> strings = indexes.stringFactory();
-        Location location = node.getLocation();
-        UUID uuid = location.getUuid();
-        if (uuid == null) uuid = UUID.randomUUID();
-        Path path = location.getPath();
-        String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
-        String uuidStr = uuid.toString();
-
-        if (logger.isTraceEnabled()) {
-            logger.trace("indexing {0}", pathStr);
-        }
-
-        // Create a separate document for the path, which makes it easier to handle moves since the path can
-        // be changed without changing any other content fields ...
-        Document doc = new Document();
-        doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        indexes.getPathsWriter().addDocument(doc);
-
-        // Create the document for the content (properties) ...
-        doc = new Document();
-        doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        String stringValue = null;
-        StringBuilder fullTextSearchValue = null;
-        for (Property property : node.getProperties()) {
-            Name name = property.getName();
-            Rule rule = rules.getRule(name);
-            if (rule.isSkipped()) continue;
-            String nameString = strings.create(name);
-            if (rule.isDate()) {
-                DateTimeFactory dateFactory = indexes.dateFactory();
-                for (Object value : property) {
-                    if (value == null) continue;
-                    DateTime dateValue = dateFactory.create(value);
-                    stringValue = dateFormatter().format(dateValue.toDate());
-                    // Add a separate field for each property value ...
-                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                    // Dates are not added to the full-text search field (since this wouldn't make sense)
-                }
-                continue;
-            }
-            for (Object value : property) {
-                if (value == null) continue;
-                if (value instanceof Binary) {
-                    // don't include binary values as individual fields but do include them in the full-text search ...
-                    // TODO : add to full-text search ...
-                    continue;
-                }
-                stringValue = strings.create(value);
-                // Add a separate field for each property value ...
-                doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                // And add to the full-text field ...
-                if (rule.isFullText()) {
-                    if (fullTextSearchValue == null) {
-                        fullTextSearchValue = new StringBuilder();
-                    } else {
-                        fullTextSearchValue.append(' ');
-                    }
-                    fullTextSearchValue.append(stringValue);
-                }
-            }
-        }
-        // Add the full-text-search field ...
-        if (fullTextSearchValue != null) {
-            doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
-        }
-        indexes.getContentWriter().addDocument(doc);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#search(String, int, int, IndexContext, List)
-     */
-    public void search( String fullTextString,
-                        int maxResults,
-                        int offset,
-                        IndexContext indexes,
-                        List<Location> results ) throws IOException, ParseException {
-        assert fullTextString != null;
-        assert fullTextString.length() > 0;
-        assert offset >= 0;
-        assert maxResults > 0;
-        assert indexes != null;
-        assert results != null;
-
-        // Parse the full-text search and search against the 'fts' field ...
-        QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
-        Query query = parser.parse(fullTextString);
-        TopDocs docs = indexes.getContentSearcher().search(query, maxResults + offset);
-
-        // Collect the results ...
-        IndexReader contentReader = indexes.getContentReader();
-        IndexReader pathReader = indexes.getPathsReader();
-        IndexSearcher pathSearcher = indexes.getPathsSearcher();
-        ScoreDoc[] scoreDocs = docs.scoreDocs;
-        int numberOfResults = scoreDocs.length;
-        if (numberOfResults > offset) {
-            // There are enough results to satisfy the offset ...
-            for (int i = offset, num = scoreDocs.length; i != num; ++i) {
-                ScoreDoc result = scoreDocs[i];
-                int docId = result.doc;
-                // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
-                Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
-                String uuid = doc.get(ContentIndex.UUID);
-                // Find the path for this node (is there a better way to do this than one search per UUID?) ...
-                TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
-                if (pathDocs.scoreDocs.length < 1) {
-                    // No path record found ...
-                    continue;
-                }
-                Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
-                Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
-                // Now add the location ...
-                results.add(Location.create(path, UUID.fromString(uuid)));
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#query(org.jboss.dna.search.SearchContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand)
-     */
-    public QueryResults query( SearchContext context,
-                               QueryCommand query ) {
-        return this.queryEngine.execute(context, query);
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,294 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import net.jcip.annotations.NotThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriter.MaxFieldLength;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.store.Directory;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.property.ValueFactory;
-
-/**
- * A context for working with the index readers and writers.
- */
- at NotThreadSafe
-final class IndexContext {
-
-    private final ExecutionContext context;
-    private final Directory pathsIndexDirectory;
-    private final Directory contentIndexDirectory;
-    private final Analyzer analyzer;
-    private final boolean overwrite;
-    private final boolean readOnly;
-    private final ValueFactory<String> stringFactory;
-    private final DateTimeFactory dateFactory;
-    private IndexReader pathsReader;
-    private IndexWriter pathsWriter;
-    private IndexSearcher pathsSearcher;
-    private IndexReader contentReader;
-    private IndexWriter contentWriter;
-    private IndexSearcher contentSearcher;
-
-    IndexContext( ExecutionContext context,
-                  Directory pathsIndexDirectory,
-                  Directory contentIndexDirectory,
-                  Analyzer analyzer,
-                  boolean overwrite,
-                  boolean readOnly ) {
-        assert context != null;
-        assert pathsIndexDirectory != null;
-        assert contentIndexDirectory != null;
-        this.context = context;
-        this.pathsIndexDirectory = pathsIndexDirectory;
-        this.contentIndexDirectory = contentIndexDirectory;
-        this.analyzer = analyzer;
-        this.overwrite = overwrite;
-        this.stringFactory = context.getValueFactories().getStringFactory();
-        this.dateFactory = context.getValueFactories().getDateFactory();
-        this.readOnly = readOnly;
-    }
-
-    /**
-     * @return context
-     */
-    public ExecutionContext context() {
-        return context;
-    }
-
-    /**
-     * @return stringFactory
-     */
-    public ValueFactory<String> stringFactory() {
-        return stringFactory;
-    }
-
-    public DateTimeFactory dateFactory() {
-        return dateFactory;
-    }
-
-    public PathFactory pathFactory() {
-        return context.getValueFactories().getPathFactory();
-    }
-
-    public IndexReader getPathsReader() throws IOException {
-        if (pathsReader == null) {
-            pathsReader = IndexReader.open(pathsIndexDirectory, readOnly);
-        }
-        return pathsReader;
-    }
-
-    public IndexReader getContentReader() throws IOException {
-        if (contentReader == null) {
-            contentReader = IndexReader.open(contentIndexDirectory, readOnly);
-        }
-        return contentReader;
-    }
-
-    public IndexWriter getPathsWriter() throws IOException {
-        if (pathsWriter == null) {
-            pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
-        }
-        return pathsWriter;
-    }
-
-    public IndexWriter getContentWriter() throws IOException {
-        if (contentWriter == null) {
-            contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
-        }
-        return contentWriter;
-    }
-
-    public IndexSearcher getPathsSearcher() throws IOException {
-        if (pathsSearcher == null) {
-            pathsSearcher = new IndexSearcher(getPathsReader());
-        }
-        return pathsSearcher;
-    }
-
-    public IndexSearcher getContentSearcher() throws IOException {
-        if (contentSearcher == null) {
-            contentSearcher = new IndexSearcher(getContentReader());
-        }
-        return contentSearcher;
-    }
-
-    public boolean hasWriters() {
-        return pathsWriter != null || contentWriter != null;
-    }
-
-    public void commit() throws IOException {
-        IOException ioError = null;
-        RuntimeException runtimeError = null;
-        if (pathsReader != null) {
-            try {
-                pathsReader.close();
-            } catch (IOException e) {
-                ioError = e;
-            } catch (RuntimeException e) {
-                runtimeError = e;
-            } finally {
-                pathsReader = null;
-            }
-        }
-        if (contentReader != null) {
-            try {
-                contentReader.close();
-            } catch (IOException e) {
-                if (ioError == null) ioError = e;
-            } catch (RuntimeException e) {
-                if (runtimeError == null) runtimeError = e;
-            } finally {
-                contentReader = null;
-            }
-        }
-        if (pathsWriter != null) {
-            try {
-                pathsWriter.commit();
-            } catch (IOException e) {
-                ioError = e;
-            } catch (RuntimeException e) {
-                runtimeError = e;
-            } finally {
-                try {
-                    pathsWriter.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    pathsWriter = null;
-                }
-            }
-        }
-        if (contentWriter != null) {
-            try {
-                contentWriter.commit();
-            } catch (IOException e) {
-                if (ioError == null) ioError = e;
-            } catch (RuntimeException e) {
-                if (runtimeError == null) runtimeError = e;
-            } finally {
-                try {
-                    contentWriter.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    contentWriter = null;
-                }
-            }
-        }
-        if (ioError != null) throw ioError;
-        if (runtimeError != null) throw runtimeError;
-    }
-
-    public void rollback() throws IOException {
-        IOException ioError = null;
-        RuntimeException runtimeError = null;
-        if (pathsReader != null) {
-            try {
-                pathsReader.close();
-            } catch (IOException e) {
-                ioError = e;
-            } catch (RuntimeException e) {
-                runtimeError = e;
-            } finally {
-                pathsReader = null;
-            }
-        }
-        if (contentReader != null) {
-            try {
-                contentReader.close();
-            } catch (IOException e) {
-                if (ioError == null) ioError = e;
-            } catch (RuntimeException e) {
-                if (runtimeError == null) runtimeError = e;
-            } finally {
-                contentReader = null;
-            }
-        }
-        if (pathsWriter != null) {
-            try {
-                pathsWriter.rollback();
-            } catch (IOException e) {
-                ioError = e;
-            } catch (RuntimeException e) {
-                runtimeError = e;
-            } finally {
-                try {
-                    pathsWriter.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    pathsWriter = null;
-                }
-            }
-        }
-        if (contentWriter != null) {
-            try {
-                contentWriter.rollback();
-            } catch (IOException e) {
-                if (ioError == null) ioError = e;
-            } catch (RuntimeException e) {
-                if (runtimeError == null) runtimeError = e;
-            } finally {
-                try {
-                    contentWriter.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    contentWriter = null;
-                }
-            }
-        }
-        if (ioError != null) throw ioError;
-        if (runtimeError != null) throw runtimeError;
-    }
-
-    /**
-     * Create a copy of this index context, except that it uses the supplied execution context.
-     * 
-     * @param context the new execution context that should be used in the copy
-     * @return the new context; never null
-     * @throws IllegalArgumentException if the context is null
-     */
-    public IndexContext with( ExecutionContext context ) {
-        CheckArg.isNotNull(context, "context");
-        return new IndexContext(context, pathsIndexDirectory, contentIndexDirectory, analyzer, overwrite, readOnly);
-    }
-
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,66 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+
+/**
+ * The representation of a single layout of one or more Lucene indexes.
+ */
+ at ThreadSafe
+public interface IndexLayout {
+
+    /**
+     * Create a new session to the indexes.
+     * 
+     * @param context the execution context for which this session is to be established; may not be null
+     * @param sourceName the name of the source; may not be null
+     * @param workspaceName the name of the workspace; may not be null
+     * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+     * @param readOnly true if the resulting session can be optimized for use in read-only situations, or false if the session
+     *        needs to allow calling the write methods
+     * @return the session to the indexes; never null
+     */
+    IndexSession createSession( ExecutionContext context,
+                                String sourceName,
+                                String workspaceName,
+                                boolean overwrite,
+                                boolean readOnly );
+
+    /**
+     * Destroy the indexes for the workspace with the supplied name.
+     * 
+     * @param context the execution context in which the destruction should be performed; may not be null
+     * @param sourceName the name of the source; may not be null
+     * @param workspaceName the name of the workspace; may not be null
+     * @return true if the indexes for the workspace were destroyed, or false if there was no such workspace index
+     * @throws IOException if there is a problem destroying the indexes
+     */
+    boolean destroyIndexes( ExecutionContext context,
+                            String sourceName,
+                            String workspaceName ) throws IOException;
+
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -39,7 +39,7 @@
  * The set of rules that dictate how properties should be indexed.
  */
 @Immutable
-class IndexRules {
+public class IndexRules {
 
     public static final int INDEX = 2 << 0;
     public static final int ANALYZE = 2 << 1;

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java (from rev 1319, trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,162 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.IndexSearcher;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * A stateful session that maintains {@link IndexReader}, {@link IndexWriter} and {@link IndexSearcher} resources to the indexes
+ * of a particular source and workspace.
+ */
+public interface IndexSession {
+
+    /**
+     * Get the name of the {@link RepositorySource repository source} for which this session exists. A session instance will
+     * always return the same name.
+     * 
+     * @return the source name; never null
+     */
+    String getSourceName();
+
+    /**
+     * Get the name of the workspace for which this session exists. A session instance will always return the same name.
+     * 
+     * @return the workspace name; never null
+     */
+    String getWorkspaceName();
+
+    /**
+     * Get the execution context in which this session is operating.
+     * 
+     * @return the execution context; never null
+     */
+    ExecutionContext getContext();
+
+    /**
+     * Return whether this session made changes to the indexed state.
+     * 
+     * @return true if change were made, or false otherwise
+     */
+    boolean hasChanges();
+
+    /**
+     * Perform a full-text search given the supplied query.
+     * 
+     * @param context the context in which the search should be executed; may not be null
+     * @param fullTextString the full-text query; never null or blank
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @param results the list where the results should be accumulated; never null
+     * @throws IOException if there is a problem indexing or using the indexes
+     * @throws ParseException if there is a problem parsing the query
+     */
+    void search( ExecutionContext context,
+                 String fullTextString,
+                 int maxResults,
+                 int offset,
+                 List<Location> results ) throws IOException, ParseException;
+
+    /**
+     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
+     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
+     * writers) that should be used.
+     * 
+     * @param queryContext the context in which the query should be executed; may not be null
+     * @param query the query; never null
+     * @return the results of the query; never null
+     * @throws IOException if there is a problem indexing or using the indexes
+     * @throws ParseException if there is a problem parsing the query
+     */
+    QueryResults query( QueryContext queryContext,
+                        QueryCommand query ) throws IOException, ParseException;
+
+    /**
+     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
+     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
+     * {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param node the node to be indexed; never null
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    void index( Node node ) throws IOException;
+
+    /**
+     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
+     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
+     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param changes the set of changes to the content
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int apply( Iterable<ChangeRequest> changes ) throws IOException;
+
+    /**
+     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
+     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
+     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param path the path identifying the graph content that is to be removed; never null
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int deleteBelow( Path path ) throws IOException;
+
+    /**
+     * Optimize the indexes, if required.
+     * 
+     * @throws IOException if there is a problem optimizing
+     */
+    void optimize() throws IOException;
+
+    /**
+     * Close this session by committing all of the changes. This session is no longer usable after this method is called.
+     * 
+     * @throws IOException if there is a problem committing
+     */
+    void commit() throws IOException;
+
+    /**
+     * Close this session by rolling back all of the changes that have been made. This session is no longer usable after this
+     * method is called.
+     * 
+     * @throws IOException if there is a problem rolling back
+     */
+    void rollback() throws IOException;
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,133 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.List;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * Interface defining the behaviors associated with indexing graph content.
- */
-interface IndexStrategy {
-
-    /**
-     * Get the number of changes that are allowed before optimization is automatically run.
-     * 
-     * @return a positive number denoting the minimum number of changes between automatic optimization operations, or a
-     *         non-positive number if automatic optimization should never be run
-     */
-    int getChangeCountForAutomaticOptimization();
-
-    /**
-     * Get the {@link TextEncoder} that should be used to encode the namespace URIs.
-     * 
-     * @return the encoder; may not be null
-     */
-    TextEncoder getNamespaceEncoder();
-
-    /**
-     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
-     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
-     * {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param node the node to be indexed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    void index( Node node,
-                IndexContext indexes ) throws IOException;
-
-    /**
-     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
-     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
-     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param changes the set of changes to the content
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int apply( Iterable<ChangeRequest> changes,
-               IndexContext indexes ) throws IOException;
-
-    /**
-     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
-     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
-     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param path the path identifying the graph content that is to be removed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int deleteBelow( Path path,
-                     IndexContext indexes ) throws IOException;
-
-    /**
-     * Create the analyzer that is used for reading and updating the indexes.
-     * 
-     * @return the analyzer; may not be null
-     */
-    Analyzer createAnalyzer();
-
-    /**
-     * Perform a full-text search given the supplied query.
-     * 
-     * @param fullTextString the full-text query; never null or blank
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @param indexes the set of index readers and writers; never null
-     * @param results the list where the results should be accumulated; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    void search( String fullTextString,
-                 int maxResults,
-                 int offset,
-                 IndexContext indexes,
-                 List<Location> results ) throws IOException, ParseException;
-
-    /**
-     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
-     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
-     * writers) that should be used.
-     * 
-     * @param context the context in which the query should be executed; never null
-     * @param query the query; never null
-     * @return the results of the query
-     */
-    QueryResults query( SearchContext context,
-                        QueryCommand query );
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,656 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.store.Directory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.BindVariableName;
+import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.UpperCase;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.AbstractAccessComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.SelectComponent;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+
+/**
+ * An {@link IndexLayout} implementation that stores all content within a set of two indexes: one for the node content and a
+ * second one for paths and UUIDs.
+ */
+ at ThreadSafe
+public class KitchenSinkIndexLayout extends DualIndexLayout {
+
+    /**
+     * The default set of {@link IndexRules} used by {@link KitchenSinkIndexLayout} instances when no rules are provided.
+     */
+    public static final IndexRules DEFAULT_RULES;
+
+    static {
+        IndexRules.Builder builder = IndexRules.createBuilder();
+        // Configure the default behavior ...
+        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
+        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
+        // Configure the properties that we'll treat as dates ...
+        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+        DEFAULT_RULES = builder.build();
+    }
+
+    private final IndexRules rules;
+    private final DirectoryConfiguration directoryConfiguration;
+
+    public KitchenSinkIndexLayout( DirectoryConfiguration directoryConfiguration ) {
+        this.rules = DEFAULT_RULES;
+        this.directoryConfiguration = directoryConfiguration;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexLayout#createSession(org.jboss.dna.graph.ExecutionContext, java.lang.String,
+     *      java.lang.String, boolean, boolean)
+     */
+    public IndexSession createSession( ExecutionContext context,
+                                       String sourceName,
+                                       String workspaceName,
+                                       boolean overwrite,
+                                       boolean readOnly ) {
+        Directory pathIndexDirectory = directoryConfiguration.getDirectory(workspaceName, PATHS_INDEX_NAME);
+        Directory contentIndexDirectory = directoryConfiguration.getDirectory(workspaceName, CONTENT_INDEX_NAME);
+        assert pathIndexDirectory != null;
+        assert contentIndexDirectory != null;
+        return new Session(context, sourceName, workspaceName, rules, pathIndexDirectory, contentIndexDirectory, overwrite,
+                           readOnly);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexLayout#destroyIndexes(org.jboss.dna.graph.ExecutionContext, java.lang.String,
+     *      java.lang.String)
+     */
+    public boolean destroyIndexes( ExecutionContext context,
+                                   String sourceName,
+                                   String workspaceName ) {
+        directoryConfiguration.destroyDirectory(workspaceName, PATHS_INDEX_NAME);
+        directoryConfiguration.destroyDirectory(workspaceName, CONTENT_INDEX_NAME);
+        return true;
+    }
+
+    protected class Session extends LuceneSession {
+
+        protected Session( ExecutionContext context,
+                           String sourceName,
+                           String workspaceName,
+                           IndexRules rules,
+                           Directory pathsIndexDirectory,
+                           Directory contentIndexDirectory,
+                           boolean overwrite,
+                           boolean readOnly ) {
+            super(context, sourceName, workspaceName, rules, pathsIndexDirectory, contentIndexDirectory, overwrite, readOnly);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.DualIndexLayout.LuceneSession#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+         *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
+         *      org.jboss.dna.graph.query.QueryResults.Columns, org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+         */
+        @Override
+        protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                             QueryContext context,
+                                                             PlanNode accessNode,
+                                                             Columns resultColumns,
+                                                             Analyzer analyzer ) throws IOException {
+            // Create a processing component for this access query ...
+            return new LuceneQueryComponent(this, originalQuery, context, resultColumns, accessNode, analyzer, sourceName,
+                                            workspaceName);
+        }
+
+    }
+
+    /**
+     * The {@link ProcessingComponent} implementation that executes a single atomic access query against the Lucene indexes.
+     */
+    protected static class LuceneQueryComponent extends AbstractAccessComponent {
+        private final QueryCommand originalQuery;
+        private final Session session;
+        private final IndexReader pathIndexReader;
+        private final Analyzer analyzer;
+        private final String sourceName;
+        private final String workspaceName;
+
+        protected LuceneQueryComponent( Session session,
+                                        QueryCommand originalQuery,
+                                        QueryContext context,
+                                        Columns columns,
+                                        PlanNode accessNode,
+                                        Analyzer analyzer,
+                                        String sourceName,
+                                        String workspaceName ) throws IOException {
+            super(context, columns, accessNode);
+            this.originalQuery = originalQuery;
+            this.analyzer = analyzer;
+            this.session = session;
+            this.pathIndexReader = session.getPathsReader();
+            this.sourceName = sourceName;
+            this.workspaceName = workspaceName;
+        }
+
+        protected String fieldNameFor( Name name ) {
+            return session.stringFactory.create(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+         */
+        @Override
+        public List<Object[]> execute() {
+
+            // Some kinds of constraints are not easily pushed down to Lucene as are of a Lucene Query, and
+            // instead are applied by filtering the results. For example, a FullTextSearchScore applies
+            // to the score of the tuple, which cannot be (easily?) applied as a Query.
+            //
+            // Therefore, each of the AND-ed constraints of the query are evaluated separately. After all,
+            // each of the tuples returned by the planned query must satisfy all of the AND-ed constraints.
+            // Or, to put it another way, if a tuple does not satisfy one of the AND-ed constraints, the
+            // tuple should not be included in the query results.
+            //
+            // Logically, any AND-ed criteria that cannot be pushed down to Lucene can of course be applied
+            // as a filter on the results. Thus, each AND-ed constraint is processed to first determine if
+            // it can be represented as a Lucene query; all other AND-ed constraints must be handled as
+            // a results filter. Since most queries will likely use one or more simple constraints AND-ed
+            // together, this approach will likely work very well.
+            //
+            // The only hairy case is when any AND-ed constraint is actually an OR-ed combination of multiple
+            // constraints of which at least one cannot be pushed down to Lucene. In this case, the entire
+            // AND-ed constraint must be treated as a results filter (even if many of those constraints that
+            // make up the OR-ed constraint can be pushed down). Hopefully, this will not be a common case
+            // in actual queries.
+
+            // For each of the AND-ed constraints ...
+            Query pushDownQuery = null;
+            Constraint postProcessConstraint = null;
+            try {
+                for (Constraint andedConstraint : this.andedConstraints) {
+                    // Determine if it can be represented as a Lucene query ...
+                    Query constraintQuery = createQuery(andedConstraint);
+                    if (constraintQuery != null) {
+                        // The AND-ed constraint _can_ be represented as a push-down Lucene query ...
+                        if (pushDownQuery == null) {
+                            // This must be the first query ...
+                            pushDownQuery = constraintQuery;
+                        } else if (pushDownQuery instanceof BooleanQuery) {
+                            // We have to add the constraint query to the existing boolean ...
+                            BooleanQuery booleanQuery = (BooleanQuery)pushDownQuery;
+                            booleanQuery.add(constraintQuery, Occur.MUST);
+                        } else {
+                            // This is the second push-down query, so create a BooleanQuery ...
+                            BooleanQuery booleanQuery = new BooleanQuery();
+                            booleanQuery.add(pushDownQuery, Occur.MUST);
+                            booleanQuery.add(constraintQuery, Occur.MUST);
+                            pushDownQuery = booleanQuery;
+                        }
+                    } else {
+                        // The AND-ed constraint _cannot_ be represented as a push-down Lucene query ...
+                        if (postProcessConstraint == null) {
+                            postProcessConstraint = andedConstraint;
+                        } else {
+                            postProcessConstraint = new And(postProcessConstraint, andedConstraint);
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                // There was a error working with the constraints (such as a ValueFormatException) ...
+                QueryContext context = getContext();
+                I18n msg = SearchI18n.errorWhilePerformingQuery;
+                String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+                context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
+                return emptyTuples();
+            } catch (RuntimeException e) {
+                // There was a error working with the constraints (such as a ValueFormatException) ...
+                QueryContext context = getContext();
+                I18n msg = SearchI18n.errorWhilePerformingQuery;
+                String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+                context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
+                return emptyTuples();
+            }
+
+            if (pushDownQuery == null) {
+                // There are no constraints that can be pushed down, so return _all_ the nodes ...
+                pushDownQuery = new MatchAllDocsQuery();
+            }
+
+            // Get the results from Lucene ...
+            List<Object[]> tuples = null;
+            final Columns columns = getColumns();
+            final QueryContext context = getContext();
+            final ExecutionContext execContext = context.getExecutionContext();
+            try {
+                // Execute the query against the content indexes ...
+                IndexSearcher searcher = session.getContentSearcher();
+                TupleCollector collector = new TupleCollector(columns, execContext.getValueFactories().getUuidFactory());
+                searcher.search(pushDownQuery, collector);
+                tuples = collector.getTuples();
+            } catch (IOException e) {
+                // There was a problem executing the Lucene query ...
+                I18n msg = SearchI18n.errorWhilePerformingLuceneQuery;
+                String origQueryString = Visitors.readable(originalQuery, execContext);
+                context.getProblems().addError(e, msg, pushDownQuery, origQueryString, workspaceName, sourceName, e.getMessage());
+                return emptyTuples();
+            }
+
+            if (postProcessConstraint != null && !tuples.isEmpty()) {
+                // Create a delegate processing component that will return the tuples we've already found ...
+                final List<Object[]> allTuples = tuples;
+                ProcessingComponent tuplesProcessor = new ProcessingComponent(context, columns) {
+                    @Override
+                    public List<Object[]> execute() {
+                        return allTuples;
+                    }
+                };
+                // Create a processing component that will apply these constraints to the tuples we already found ...
+                return new SelectComponent(tuplesProcessor, postProcessConstraint, context.getVariables()).execute();
+            }
+            return tuples;
+        }
+
+        protected Query createQuery( Constraint constraint ) throws IOException {
+            if (constraint instanceof And) {
+                And and = (And)constraint;
+                Query leftQuery = createQuery(and.getLeft());
+                Query rightQuery = createQuery(and.getRight());
+                if (leftQuery == null || rightQuery == null) return null;
+                BooleanQuery booleanQuery = new BooleanQuery();
+                booleanQuery.add(createQuery(and.getLeft()), Occur.MUST);
+                booleanQuery.add(createQuery(and.getRight()), Occur.MUST);
+                return booleanQuery;
+            }
+            if (constraint instanceof Or) {
+                Or or = (Or)constraint;
+                Query leftQuery = createQuery(or.getLeft());
+                Query rightQuery = createQuery(or.getRight());
+                if (leftQuery == null) {
+                    return rightQuery != null ? rightQuery : null;
+                } else if (rightQuery == null) {
+                    return leftQuery;
+                }
+                BooleanQuery booleanQuery = new BooleanQuery();
+                booleanQuery.add(createQuery(or.getLeft()), Occur.SHOULD);
+                booleanQuery.add(createQuery(or.getRight()), Occur.SHOULD);
+                return booleanQuery;
+            }
+            if (constraint instanceof Not) {
+                Not not = (Not)constraint;
+                Query notted = createQuery(not.getConstraint());
+                if (notted == null) return new MatchAllDocsQuery();
+            }
+            if (constraint instanceof SetCriteria) {
+                SetCriteria setCriteria = (SetCriteria)constraint;
+                DynamicOperand left = setCriteria.getLeftOperand();
+                int numRightOperands = setCriteria.getRightOperands().size();
+                assert numRightOperands > 0;
+                if (numRightOperands == 1) {
+                    return createQuery(left, Operator.EQUAL_TO, setCriteria.getRightOperands().iterator().next());
+                }
+                BooleanQuery setQuery = new BooleanQuery();
+                for (StaticOperand right : setCriteria.getRightOperands()) {
+                    Query rightQuery = createQuery(left, Operator.EQUAL_TO, right);
+                    if (rightQuery == null) return null;
+                    setQuery.add(rightQuery, Occur.SHOULD);
+                }
+                return setQuery;
+            }
+            if (constraint instanceof PropertyExistence) {
+                PropertyExistence existence = (PropertyExistence)constraint;
+                return createQuery(existence.getSelectorName(), existence.getPropertyName());
+            }
+            if (constraint instanceof Comparison) {
+                Comparison comparison = (Comparison)constraint;
+                return createQuery(comparison.getOperand1(), comparison.getOperator(), comparison.getOperand2());
+            }
+            if (constraint instanceof FullTextSearch) {
+                FullTextSearch search = (FullTextSearch)constraint;
+                String fieldName = ContentIndex.FULL_TEXT;
+                Name propertyName = search.getPropertyName();
+                if (propertyName != null) {
+                    fieldName = session.fullTextFieldName(fieldNameFor(propertyName));
+                }
+                return createQuery(fieldName, search.getTerm());
+            }
+            try {
+                if (constraint instanceof SameNode) {
+                    SameNode sameNode = (SameNode)constraint;
+                    return session.findNodeAt(sameNode.getPath());
+                }
+                if (constraint instanceof ChildNode) {
+                    ChildNode childNode = (ChildNode)constraint;
+                    return session.findChildNodes(childNode.getParentPath());
+                }
+                if (constraint instanceof DescendantNode) {
+                    DescendantNode descendantNode = (DescendantNode)constraint;
+                    return session.findAllNodesBelow(descendantNode.getAncestorPath());
+                }
+            } catch (IOException e) {
+                I18n msg = SearchI18n.errorWhilePerformingQuery;
+                getContext().getProblems().addError(e,
+                                                    msg,
+                                                    Visitors.readable(originalQuery),
+                                                    workspaceName,
+                                                    sourceName,
+                                                    e.getMessage());
+                return null;
+            }
+            // Should not get here ...
+            assert false;
+            return null;
+        }
+
+        protected Query createQuery( DynamicOperand left,
+                                     Operator operator,
+                                     StaticOperand right ) throws IOException {
+            return createQuery(left, operator, right, true);
+        }
+
+        protected Query createQuery( DynamicOperand left,
+                                     Operator operator,
+                                     StaticOperand right,
+                                     boolean caseSensitive ) throws IOException {
+            // Handle the static operand ...
+            Object value = null;
+            if (right instanceof Literal) {
+                Literal literal = (Literal)right;
+                value = literal.getValue();
+                if (!caseSensitive) value = lowerCase(value);
+            } else if (right instanceof BindVariableName) {
+                BindVariableName variable = (BindVariableName)right;
+                String variableName = variable.getVariableName();
+                value = getContext().getVariables().get(variableName);
+                if (!caseSensitive) value = lowerCase(value);
+            } else {
+                assert false;
+                return null;
+            }
+
+            // Address the dynamic operand ...
+            if (left instanceof FullTextSearchScore) {
+                // This can only be represented as a filter ...
+                return null;
+            } else if (left instanceof PropertyValue) {
+                return session.findNodesWith((PropertyValue)left, operator, value, caseSensitive);
+            } else if (left instanceof Length) {
+                Length length = (Length)left;
+                PropertyValue nested = length.getPropertyValue();
+
+                return null;
+            } else if (left instanceof LowerCase) {
+                LowerCase lowercase = (LowerCase)left;
+                return createQuery(lowercase.getOperand(), operator, right, false);
+            } else if (left instanceof UpperCase) {
+                UpperCase lowercase = (UpperCase)left;
+                return createQuery(lowercase.getOperand(), operator, right, false);
+            } else if (left instanceof NodeDepth) {
+                assert operator != Operator.LIKE;
+                // Could be represented as a result filter, but let's do this now ...
+                return session.findNodesWith((NodeDepth)left, operator, value);
+            } else if (left instanceof NodePath) {
+                return session.findNodesWith((NodePath)left, operator, value, caseSensitive);
+            } else if (left instanceof NodeName) {
+                return session.findNodesWith((NodeName)left, operator, value, caseSensitive);
+            } else if (left instanceof NodeLocalName) {
+                return session.findNodesWith((NodeLocalName)left, operator, value, caseSensitive);
+            } else {
+                assert false;
+                return null;
+            }
+        }
+
+        protected Object lowerCase( Object value ) {
+            if (value instanceof String) {
+                return ((String)value).toLowerCase();
+            }
+            assert !(value instanceof Binary);
+            ValueFactory<String> stringFactory = getContext().getExecutionContext().getValueFactories().getStringFactory();
+            ValueFactory<?> valueFactory = getContext().getExecutionContext().getValueFactories().getValueFactory(value);
+            return valueFactory.create(stringFactory.create(value).toLowerCase());
+        }
+
+        protected Query createQuery( SelectorName selectorName,
+                                     Name propertyName ) {
+            Term term = new Term(fieldNameFor(propertyName));
+            return new TermQuery(term);
+        }
+
+        protected Query createQuery( String fieldName,
+                                     FullTextSearch.Term term ) {
+            if (term instanceof FullTextSearch.Conjunction) {
+                FullTextSearch.Conjunction conjunction = (FullTextSearch.Conjunction)term;
+                BooleanQuery query = new BooleanQuery();
+                for (FullTextSearch.Term nested : conjunction) {
+                    if (nested instanceof NegationTerm) {
+                        query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
+                    } else {
+                        query.add(createQuery(fieldName, nested), Occur.MUST);
+                    }
+                }
+                return query;
+            }
+            if (term instanceof FullTextSearch.Disjunction) {
+                FullTextSearch.Disjunction disjunction = (FullTextSearch.Disjunction)term;
+                BooleanQuery query = new BooleanQuery();
+                for (FullTextSearch.Term nested : disjunction) {
+                    if (nested instanceof NegationTerm) {
+                        query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
+                    } else {
+                        query.add(createQuery(fieldName, nested), Occur.SHOULD);
+                    }
+                }
+                return query;
+            }
+            if (term instanceof FullTextSearch.SimpleTerm) {
+                FullTextSearch.SimpleTerm simple = (FullTextSearch.SimpleTerm)term;
+                if (simple.isQuotingRequired()) {
+                    PhraseQuery query = new PhraseQuery();
+                    query.setSlop(0); // terms must be adjacent
+                    for (String value : simple.getValues()) {
+                        query.add(new Term(fieldName, value));
+                    }
+                    return query;
+                }
+                return new TermQuery(new Term(fieldName, simple.getValue()));
+            }
+            // Should not get here ...
+            assert false;
+            return null;
+        }
+    }
+
+    /**
+     * This collector is responsible for loading the value for each of the columns into each tuple array.
+     */
+    protected static class TupleCollector extends Collector {
+        private final LinkedList<Object[]> tuples = new LinkedList<Object[]>();
+        private final Columns columns;
+        private final int numValues;
+        private final boolean recordScore;
+        private final int scoreIndex;
+        private final FieldSelector fieldSelector;
+        private final int locationIndex;
+        private final ValueFactory<UUID> uuidFactory;
+        private Scorer scorer;
+        private IndexReader currentReader;
+        private int docOffset;
+
+        protected TupleCollector( Columns columns,
+                                  ValueFactory<UUID> uuidFactory ) {
+            this.columns = columns;
+            this.uuidFactory = uuidFactory;
+            assert this.columns != null;
+            assert this.uuidFactory != null;
+            this.numValues = this.columns.getTupleSize();
+            assert this.numValues >= 0;
+            assert this.columns.getSelectorNames().size() == 1;
+            final String selectorName = this.columns.getSelectorNames().get(0);
+            this.locationIndex = this.columns.getLocationIndex(selectorName);
+            this.recordScore = this.columns.hasFullTextSearchScores();
+            this.scoreIndex = this.recordScore ? this.columns.getFullTextSearchScoreIndexFor(selectorName) : -1;
+            final Set<String> columnNames = new HashSet<String>(this.columns.getColumnNames());
+            columnNames.add(ContentIndex.UUID); // add the UUID, which we'll put into the Location ...
+            this.fieldSelector = new FieldSelector() {
+                private static final long serialVersionUID = 1L;
+
+                public FieldSelectorResult accept( String fieldName ) {
+                    return columnNames.contains(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
+                }
+            };
+        }
+
+        /**
+         * @return tuples
+         */
+        public LinkedList<Object[]> getTuples() {
+            return tuples;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
+         */
+        @Override
+        public boolean acceptsDocsOutOfOrder() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public void setNextReader( IndexReader reader,
+                                   int docBase ) {
+            this.currentReader = reader;
+            this.docOffset = docBase;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
+         */
+        @Override
+        public void setScorer( Scorer scorer ) {
+            this.scorer = scorer;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#collect(int)
+         */
+        @Override
+        public void collect( int doc ) throws IOException {
+            int docId = doc + docOffset;
+            Object[] tuple = new Object[numValues];
+            Document document = currentReader.document(docId, fieldSelector);
+            for (String columnName : columns.getColumnNames()) {
+                int index = columns.getColumnIndexForName(columnName);
+                // We just need to retrieve the first value if there is more than one ...
+                tuple[index] = document.get(columnName);
+            }
+
+            // Set the score column if required ...
+            if (recordScore) {
+                assert scorer != null;
+                tuple[scoreIndex] = scorer.score();
+            }
+
+            // Load the UUID into a Location object ...
+            UUID uuid = uuidFactory.create(document.get(ContentIndex.UUID));
+            tuple[locationIndex] = Location.create(uuid);
+            tuples.add(tuple);
+        }
+    }
+
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,115 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.util.List;
-import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.AbstractAccessComponent;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * An {@link IndexStrategy} implementation that stores all content within a set of two indexes: one for the node content and a
- * second one for paths and UUIDs.
- */
- at ThreadSafe
-class KitchenSinkIndexStrategy extends DualIndexStrategy {
-
-    /**
-     * The default set of {@link IndexRules} used by {@link KitchenSinkIndexStrategy} instances when no rules are provided.
-     */
-    public static final IndexRules DEFAULT_RULES;
-
-    static {
-        IndexRules.Builder builder = IndexRules.createBuilder();
-        // Configure the default behavior ...
-        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
-        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
-        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
-        // Configure the properties that we'll treat as dates ...
-        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
-        DEFAULT_RULES = builder.build();
-    }
-
-    /**
-     * Create a new indexing strategy instance uses the {@link #DEFAULT_RULES default indexing rules}.
-     */
-    public KitchenSinkIndexStrategy() {
-        this(null);
-    }
-
-    /**
-     * Create a new indexing strategy instance.
-     * 
-     * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
-     *        rules} are to be used
-     */
-    public KitchenSinkIndexStrategy( IndexRules rules ) {
-        super(rules != null ? rules : DEFAULT_RULES);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.DualIndexStrategy#createAccessComponent(org.jboss.dna.search.SearchContext,
-     *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
-     *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-     */
-    @Override
-    protected ProcessingComponent createAccessComponent( final SearchContext context,
-                                                         PlanNode accessNode,
-                                                         Columns resultColumns,
-                                                         Analyzer analyzer ) {
-        // Create a processing component for this access query ...
-        return new AbstractAccessComponent(context, resultColumns, accessNode) {
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
-             */
-            @Override
-            public List<Object[]> execute() {
-                return null;
-            }
-        };
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexStrategy#apply(Iterable, IndexContext)
-     */
-    public int apply( Iterable<ChangeRequest> changes,
-                      IndexContext indexes ) /*throws IOException*/{
-        for (ChangeRequest change : changes) {
-            if (change != null) continue;
-        }
-        return 0;
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,161 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.util.Map;
-import org.jboss.dna.common.collection.Problems;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.validate.Schemata;
-
-/**
- * 
- */
-class SearchContext extends QueryContext {
-
-    private final IndexContext indexes;
-
-    /**
-     * Create a new context for searching and querying.
-     * 
-     * @param indexes the indexes that should be used
-     * @param schemata the definition of the tables available to this query
-     */
-    public SearchContext( IndexContext indexes,
-                          Schemata schemata ) {
-        super(indexes.context(), schemata);
-        this.indexes = indexes;
-        assert this.indexes != null;
-    }
-
-    /**
-     * Create a new context for searching and querying.
-     * 
-     * @param queryContext
-     * @param indexes
-     */
-    public SearchContext( QueryContext queryContext,
-                          IndexContext indexes ) {
-        super(queryContext.getExecutionContext(), queryContext.getSchemata(), queryContext.getHints(),
-              queryContext.getProblems(), queryContext.getVariables());
-        this.indexes = indexes;
-        assert this.indexes != null;
-    }
-
-    /**
-     * Create a new context for searching and querying.
-     * 
-     * @param context the execution context
-     * @param schemata the schemata
-     * @param hints the hints, or null if there are no hints
-     * @param problems the problems container, or null if a new problems container should be created
-     * @param variables the mapping of variables and values, or null if there are no such variables
-     * @throws IllegalArgumentException if the context or schmata are null
-     */
-    public SearchContext( IndexContext context,
-                          Schemata schemata,
-                          PlanHints hints,
-                          Problems problems,
-                          Map<String, Object> variables ) {
-        super(context.context(), schemata, hints, problems, variables);
-        this.indexes = context;
-        assert this.indexes != null;
-    }
-
-    /**
-     * Get the {@link IndexContext} for this query context.
-     * 
-     * @return the index context; never null
-     */
-    public IndexContext getIndexes() {
-        return indexes;
-    }
-
-    /**
-     * Obtain a copy of this context, except that the copy uses the supplied index context.
-     * 
-     * @param context the index context that should be used in the new query context
-     * @return the new context; never null
-     * @throws IllegalArgumentException if the index context reference is null
-     */
-    public SearchContext with( IndexContext context ) {
-        CheckArg.isNotNull(context, "context");
-        return new SearchContext(context, getSchemata(), getHints(), getProblems(), getVariables());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.ExecutionContext)
-     */
-    @Override
-    public SearchContext with( ExecutionContext context ) {
-        CheckArg.isNotNull(context, "context");
-        return new SearchContext(indexes.with(context), getSchemata(), getHints(), getProblems(), getVariables());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.validate.Schemata)
-     */
-    @Override
-    public SearchContext with( Schemata schemata ) {
-        CheckArg.isNotNull(schemata, "schemata");
-        return new SearchContext(indexes, schemata, getHints(), getProblems(), getVariables());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.plan.PlanHints)
-     */
-    @Override
-    public SearchContext with( PlanHints hints ) {
-        CheckArg.isNotNull(hints, "hints");
-        return new SearchContext(indexes, getSchemata(), hints, getProblems(), getVariables());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.common.collection.Problems)
-     */
-    @Override
-    public SearchContext with( Problems problems ) {
-        return new SearchContext(indexes, getSchemata(), getHints(), problems, getVariables());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.QueryContext#with(java.util.Map)
-     */
-    @Override
-    public SearchContext with( Map<String, Object> variables ) {
-        return new SearchContext(indexes, getSchemata(), getHints(), getProblems(), variables);
-    }
-}

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -23,26 +23,33 @@
  */
 package org.jboss.dna.search;
 
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import net.jcip.annotations.GuardedBy;
 import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.store.Directory;
+import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -57,45 +64,68 @@
 @ThreadSafe
 public class SearchEngine {
 
-    private final ExecutionContext context;
+    /**
+     * The default maximum number of changes that can be made to an index before the indexes are automatically optimized is * * *
+     * * {@value}
+     */
+    public static final int DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION = 0;
+
+    protected final ExecutionContext context;
     private final String sourceName;
     private final RepositoryConnectionFactory connectionFactory;
-    private final DirectoryConfiguration directoryFactory;
-    private final IndexStrategy indexStrategy;
-    private final PathFactory pathFactory;
-    @GuardedBy( "workspaceEngineLock" )
-    private final Map<String, WorkspaceSearchEngine> workspaceEnginesByName;
-    private final ReadWriteLock workspaceEngineLock = new ReentrantReadWriteLock();
+    protected final IndexLayout indexLayout;
+    private final int maxChangesBeforeAutomaticOptimization;
+    @GuardedBy( "workspacesLock" )
+    private final Map<String, Workspace> workspacesByName = new HashMap<String, Workspace>();
+    private final ReadWriteLock workspacesLock = new ReentrantReadWriteLock();
 
     /**
      * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
      * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
      * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
      * 
-     * @param context the execution context in which all indexing operations should be performed
+     * @param context the execution context for indexing and optimization operations
      * @param sourceName the name of the {@link RepositorySource}
      * @param connectionFactory the connection factory
-     * @param directoryFactory the factory for Lucene {@link Directory directories}
+     * @param indexLayout the specification of the Lucene index layout
+     * @param maxChangesBeforeAutomaticOptimization the number of changes that can be made to the index before the indexes are
+     *        automatically optimized; may be 0 or a negative number if no automatic optimization should be done
      * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
      */
     public SearchEngine( ExecutionContext context,
                          String sourceName,
                          RepositoryConnectionFactory connectionFactory,
-                         DirectoryConfiguration directoryFactory ) {
+                         IndexLayout indexLayout,
+                         int maxChangesBeforeAutomaticOptimization ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(sourceName, "sourceName");
         CheckArg.isNotNull(connectionFactory, "connectionFactory");
-        CheckArg.isNotNull(directoryFactory, "directoryFactory");
         this.sourceName = sourceName;
         this.connectionFactory = connectionFactory;
-        this.directoryFactory = directoryFactory;
+        this.indexLayout = indexLayout;
         this.context = context;
-        this.pathFactory = context.getValueFactories().getPathFactory();
-        this.workspaceEnginesByName = new HashMap<String, WorkspaceSearchEngine>();
-        this.indexStrategy = new KitchenSinkIndexStrategy();
+        this.maxChangesBeforeAutomaticOptimization = maxChangesBeforeAutomaticOptimization < 0 ? 0 : maxChangesBeforeAutomaticOptimization;
     }
 
     /**
+     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
+     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
+     * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
+     * 
+     * @param context the execution context for indexing and optimization operations
+     * @param sourceName the name of the {@link RepositorySource}
+     * @param connectionFactory the connection factory
+     * @param indexLayout the specification of the Lucene index layout
+     * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
+     */
+    public SearchEngine( ExecutionContext context,
+                         String sourceName,
+                         RepositoryConnectionFactory connectionFactory,
+                         IndexLayout indexLayout ) {
+        this(context, sourceName, connectionFactory, indexLayout, DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION);
+    }
+
+    /**
      * Get the name of the RepositorySource that this engine is to use.
      * 
      * @return the source name; never null
@@ -114,6 +144,13 @@
     }
 
     /**
+     * @return maxChangesBeforeAutomaticOptimization
+     */
+    public int getMaxChangesBeforeAutomaticOptimization() {
+        return maxChangesBeforeAutomaticOptimization;
+    }
+
+    /**
      * Utility to create a Graph for the source.
      * 
      * @return the graph instance; never null
@@ -123,46 +160,58 @@
     }
 
     /**
-     * Get the search engine for the workspace with the supplied name.
+     * Utility to obtain the root path.
      * 
+     * @return the root path; never null
+     */
+    final Path rootPath() {
+        return context.getValueFactories().getPathFactory().createRootPath();
+    }
+
+    final String readable( Path path ) {
+        return context.getValueFactories().getStringFactory().create(path);
+    }
+
+    /**
+     * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
+     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
+     * the workspace and source.
+     * <p>
+     * This method operates synchronously and returns when the requested indexing is completed.
+     * </p>
+     * 
      * @param workspaceName the name of the workspace
-     * @return the workspace's search engine
+     * @param startingPoint the location that represents the content to be indexed; must have a path
+     * @param depthPerRead the depth of each subgraph read operation
+     * @throws IllegalArgumentException if the workspace name or location are null
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
      * @throws InvalidWorkspaceException if the workspace does not exist
      */
-    protected WorkspaceSearchEngine getWorkspaceEngine( String workspaceName ) {
-        WorkspaceSearchEngine engine = null;
-        try {
-            workspaceEngineLock.readLock().lock();
-            engine = workspaceEnginesByName.get(workspaceName);
-        } finally {
-            workspaceEngineLock.readLock().unlock();
-        }
+    public void index( String workspaceName,
+                       Location startingPoint,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        CheckArg.isNotNull(startingPoint, "startingPoint");
+        assert startingPoint.hasPath();
 
-        if (engine == null) {
-            // Verify the workspace does exist ...
-            if (!graph().getWorkspaces().contains(workspaceName)) {
-                String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, getSourceName());
-                throw new InvalidWorkspaceException(msg);
-            }
-            try {
-                workspaceEngineLock.writeLock().lock();
-                // Check whether another thread got in and created the engine while we waited ...
-                engine = workspaceEnginesByName.get(workspaceName);
-                if (engine == null) {
-                    // Create the engine and register it ...
-                    engine = new WorkspaceSearchEngine(context, directoryFactory, indexStrategy, sourceName, workspaceName,
-                                                       connectionFactory);
-                    workspaceEnginesByName.put(workspaceName, engine);
-                }
-            } finally {
-                workspaceEngineLock.writeLock().unlock();
-            }
+        Workspace workspace = getWorkspace(workspaceName);
+        if (startingPoint.getPath().isRoot()) {
+            // More efficient to just start over with a new index ...
+            workspace.execute(true, addContent(startingPoint, depthPerRead));
+        } else {
+            // Have to first remove the content below the starting point, then add it again ...
+            workspace.execute(false, removeContent(startingPoint), addContent(startingPoint, depthPerRead));
         }
-        return engine;
     }
 
     /**
      * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
+     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
+     * the workspace and source.
+     * <p>
+     * This method operates synchronously and returns when the requested indexing is completed.
+     * </p>
      * 
      * @param workspaceName the name of the workspace
      * @param startingPoint the path that represents the content to be indexed
@@ -172,16 +221,17 @@
      * @throws SearchEngineException if there is a problem updating the indexes
      * @throws InvalidWorkspaceException if the workspace does not exist
      */
-    public void indexContent( String workspaceName,
-                              Path startingPoint,
-                              int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+    public void index( String workspaceName,
+                       Path startingPoint,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
         CheckArg.isNotNull(workspaceName, "workspaceName");
         CheckArg.isNotNull(startingPoint, "startingPoint");
-        getWorkspaceEngine(workspaceName).indexContent(startingPoint, depthPerRead);
+        index(workspaceName, Location.create(startingPoint), depthPerRead);
     }
 
     /**
-     * Index all of the content in the named workspace within the {@link #getSourceName() source}.
+     * Index all of the content in the named workspace within the {@link #getSourceName() source}. This method operates
+     * synchronously and returns when the requested indexing is completed.
      * 
      * @param workspaceName the name of the workspace
      * @param depthPerRead the depth of each subgraph read operation
@@ -190,23 +240,24 @@
      * @throws SearchEngineException if there is a problem updating the indexes
      * @throws InvalidWorkspaceException if the workspace does not exist
      */
-    public void indexContent( String workspaceName,
-                              int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+    public void index( String workspaceName,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
         CheckArg.isNotNull(workspaceName, "workspaceName");
-        indexContent(workspaceName, pathFactory.createRootPath(), depthPerRead);
+        index(workspaceName, rootPath(), depthPerRead);
     }
 
     /**
-     * Index (or re-index) all of the content in all of the workspaces within the source.
+     * Index (or re-index) all of the content in all of the workspaces within the source. This method operates synchronously and
+     * returns when the requested indexing is completed.
      * 
      * @param depthPerRead the depth of each subgraph read operation
      * @throws RepositorySourceException if there is a problem accessing the content
      * @throws SearchEngineException if there is a problem updating the indexes
      */
-    public void indexContent( int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        Path rootPath = pathFactory.createRootPath();
+    public void index( int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        Path rootPath = rootPath();
         for (String workspaceName : graph().getWorkspaces()) {
-            getWorkspaceEngine(workspaceName).indexContent(rootPath, depthPerRead);
+            index(workspaceName, rootPath, depthPerRead);
         }
     }
 
@@ -218,7 +269,7 @@
      * @throws RepositorySourceException if there is a problem accessing the content
      * @throws SearchEngineException if there is a problem updating the indexes
      */
-    public void indexChanges( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
+    public void index( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
         // First break up all the changes into different collections, one collection per workspace ...
         Map<String, Collection<ChangeRequest>> changesByWorkspace = new HashMap<String, Collection<ChangeRequest>>();
         for (ChangeRequest request : changes) {
@@ -235,7 +286,7 @@
         for (Map.Entry<String, Collection<ChangeRequest>> entry : changesByWorkspace.entrySet()) {
             String workspaceName = entry.getKey();
             Collection<ChangeRequest> changesForWorkspace = entry.getValue();
-            getWorkspaceEngine(workspaceName).indexChanges(changesForWorkspace);
+            getWorkspace(workspaceName).execute(false, updateContent(changesForWorkspace));
         }
     }
 
@@ -247,7 +298,7 @@
      */
     public void optimize() throws SearchEngineException {
         for (String workspaceName : graph().getWorkspaces()) {
-            getWorkspaceEngine(workspaceName).optimize();
+            getWorkspace(workspaceName).execute(false, optimizeContent());
         }
     }
 
@@ -262,46 +313,498 @@
      */
     public void optimize( String workspaceName ) throws SearchEngineException {
         CheckArg.isNotNull(workspaceName, "workspaceName");
-        getWorkspaceEngine(workspaceName).optimize();
+        getWorkspace(workspaceName).execute(false, optimizeContent());
     }
 
     /**
      * Perform a full-text search of the content in the named workspace, given the maximum number of results and the offset
      * defining the first result the caller is interested in.
      * 
+     * @param context the execution context in which the search is to take place; may not be null
      * @param workspaceName the name of the workspace
      * @param fullTextSearch the full-text search to be performed; may not be null
      * @param maxResults the maximum number of results that are to be returned; always positive
      * @param offset the number of initial results to skip, or 0 if the first results are to be returned
      * @return the activity that will perform the work
-     * @throws IllegalArgumentException if the workspace name is null
+     * @throws IllegalArgumentException if the execution context or workspace name are null
      * @throws SearchEngineException if there is a problem during optimization
      * @throws InvalidWorkspaceException if the workspace does not exist
      */
-    public List<Location> fullTextSearch( String workspaceName,
+    public List<Location> fullTextSearch( ExecutionContext context,
+                                          String workspaceName,
                                           String fullTextSearch,
                                           int maxResults,
                                           int offset ) {
+        CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(workspaceName, "workspaceName");
-        return getWorkspaceEngine(workspaceName).fullTextSearch(fullTextSearch, maxResults, offset);
+        Search searchActivity = searchContent(context, fullTextSearch, maxResults, offset);
+        getWorkspace(workspaceName).execute(false, searchActivity);
+        return searchActivity.getResults();
     }
 
     /**
      * Perform a query of the content in the named workspace, given the Abstract Query Model representation of the query.
      * 
+     * @param context the execution context in which the search is to take place; may not be null
      * @param workspaceName the name of the workspace
      * @param query the query that is to be executed, in the form of the Abstract Query Model
      * @param schemata the definition of the tables and views that can be used in the query; may not be null
      * @return the query results; never null
      * @throws IllegalArgumentException if the context, query, or schemata references are null
      */
-    public QueryResults execute( String workspaceName,
-                                 QueryCommand query,
-                                 Schemata schemata ) {
+    public QueryResults query( ExecutionContext context,
+                               String workspaceName,
+                               QueryCommand query,
+                               Schemata schemata ) {
+        CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(workspaceName, "workspaceName");
         CheckArg.isNotNull(query, "query");
         CheckArg.isNotNull(schemata, "schemata");
-        return getWorkspaceEngine(workspaceName).execute(query, schemata);
+        QueryContext queryContext = new QueryContext(context, schemata);
+        Query queryActivity = queryContent(queryContext, query);
+        getWorkspace(workspaceName).execute(false, queryActivity);
+        return queryActivity.getResults();
     }
 
+    /**
+     * Remove the supplied index from the search engine. This is typically done when the workspace has been deleted from the
+     * source, or when
+     * 
+     * @param workspaceName the name of the workspace
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem removing the workspace
+     */
+    public void removeWorkspace( String workspaceName ) throws SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        try {
+            workspacesLock.writeLock().lock();
+            // Check whether another thread got in and created the engine while we waited ...
+            Workspace workspace = workspacesByName.remove(workspaceName);
+            if (workspace != null) {
+                indexLayout.destroyIndexes(context, getSourceName(), workspaceName);
+            }
+        } catch (IOException e) {
+            String message = SearchI18n.errorWhileRemovingIndexesForWorkspace.text(sourceName, workspaceName, e.getMessage());
+            throw new SearchEngineException(message, e);
+        } finally {
+            workspacesLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Remove from the search engine all workspace-related indexes, thereby cleaning up any resources used by this search engine.
+     * 
+     * @throws SearchEngineException if there is a problem removing any of the workspace
+     */
+    public void removeWorkspaces() throws SearchEngineException {
+        try {
+            workspacesLock.writeLock().lock();
+            for (String workspaceName : new HashSet<String>(workspacesByName.keySet())) {
+                removeWorkspace(workspaceName);
+            }
+        } finally {
+            workspacesLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Get the search engine for the workspace with the supplied name.
+     * 
+     * @param workspaceName the name of the workspace
+     * @return the workspace's search engine
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    protected Workspace getWorkspace( String workspaceName ) {
+        Workspace workspace = null;
+        try {
+            workspacesLock.readLock().lock();
+            workspace = workspacesByName.get(workspaceName);
+        } finally {
+            workspacesLock.readLock().unlock();
+        }
+
+        if (workspace == null) {
+            // Verify the workspace does exist ...
+            if (!graph().getWorkspaces().contains(workspaceName)) {
+                String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, getSourceName());
+                throw new InvalidWorkspaceException(msg);
+            }
+            try {
+                workspacesLock.writeLock().lock();
+                // Check whether another thread got in and created the engine while we waited ...
+                workspace = workspacesByName.get(workspaceName);
+                if (workspace == null) {
+                    // Create the engine and register it ...
+                    workspace = new Workspace(workspaceName);
+                    workspacesByName.put(workspaceName, workspace);
+                }
+            } finally {
+                workspacesLock.writeLock().unlock();
+            }
+        }
+        return workspace;
+    }
+
+    protected class Workspace {
+        private final String sourceName;
+        private final String workspaceName;
+        protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
+
+        protected Workspace( String workspaceName ) {
+            this.workspaceName = workspaceName;
+            this.sourceName = getSourceName();
+        }
+
+        /**
+         * Get the workspace name.
+         * 
+         * @return the workspace name; never null
+         */
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        /**
+         * Execute the supplied activities against the indexes.
+         * 
+         * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+         * @param activities the activities to execute
+         * @throws SearchEngineException if there is a problem performing the activities
+         */
+        protected final void execute( boolean overwrite,
+                                      Activity... activities ) throws SearchEngineException {
+            // Determine if the activities are readonly ...
+            boolean readOnly = true;
+            for (Activity activity : activities) {
+                if (!(activity instanceof ReadOnlyActivity)) {
+                    readOnly = false;
+                    break;
+                }
+            }
+
+            // Create a session ...
+            IndexSession session = indexLayout.createSession(context, sourceName, workspaceName, overwrite, readOnly);
+            assert session != null;
+
+            // Execute the various activities ...
+            Throwable error = null;
+            try {
+                int numChanges = 0;
+                for (Activity activity : activities) {
+                    try {
+                        numChanges += activity.execute(session);
+                    } catch (IOException e) {
+                        error = e;
+                        throw new SearchEngineException(activity.messageFor(e, sourceName, workspaceName), e);
+                    } catch (ParseException e) {
+                        error = e;
+                        throw new SearchEngineException(activity.messageFor(e, sourceName, workspaceName), e);
+                    } catch (RuntimeException e) {
+                        error = e;
+                        throw e;
+                    }
+                }
+                if (numChanges > 0) {
+                    numChanges = this.modifiedNodesSinceLastOptimize.addAndGet(numChanges);
+                    // Determine if there have been enough changes made to run the optimizer ...
+                    int maxChanges = getMaxChangesBeforeAutomaticOptimization();
+                    if (maxChanges > 0 && numChanges >= maxChanges) {
+                        Activity optimizer = optimizeContent();
+                        try {
+                            optimizer.execute(session);
+                        } catch (ParseException e) {
+                            error = e;
+                            throw new SearchEngineException(optimizer.messageFor(e, sourceName, workspaceName), e);
+                        } catch (IOException e) {
+                            error = e;
+                            throw new SearchEngineException(optimizer.messageFor(e, sourceName, workspaceName), e);
+                        } catch (RuntimeException e) {
+                            error = e;
+                            throw e;
+                        }
+                    }
+                }
+            } finally {
+                try {
+                    if (error == null) {
+                        session.commit();
+                    } else {
+                        session.rollback();
+                    }
+                } catch (IOException e2) {
+                    // We don't want to lose the existing error, if there is one ...
+                    if (error == null) {
+                        I18n msg = SearchI18n.errorWhileCommittingIndexChanges;
+                        throw new SearchEngineException(msg.text(workspaceName, sourceName, e2.getMessage()), e2);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create an activity that will optimize the indexes.
+     * 
+     * @return the activity that will perform the work
+     */
+    protected Activity optimizeContent() {
+        return new Activity() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
+             */
+            public int execute( IndexSession indexSession ) throws IOException {
+                indexSession.optimize();
+                return 0; // no lines changed
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return SearchI18n.errorWhileOptimizingIndexes.text(sourceName, workspaceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will read from the source the content at the supplied location and add the content to the search
+     * index.
+     * 
+     * @param location the location of the content to read; may not be null
+     * @param depthPerRead the depth of each read operation; always positive
+     * @return the activity that will perform the work
+     */
+    protected Activity addContent( final Location location,
+                                   final int depthPerRead ) {
+        return new Activity() {
+            public int execute( IndexSession indexSession ) throws IOException {
+
+                // Create a queue that we'll use to walk the content ...
+                LinkedList<Location> locationsToRead = new LinkedList<Location>();
+                locationsToRead.add(location);
+                int count = 0;
+
+                // Now read and index the content ...
+                Graph graph = graph();
+                graph.useWorkspace(indexSession.getWorkspaceName());
+                while (!locationsToRead.isEmpty()) {
+                    Location location = locationsToRead.poll();
+                    if (location == null) continue;
+                    Subgraph subgraph = graph.getSubgraphOfDepth(depthPerRead).at(location);
+                    // Index all of the nodes within this subgraph ...
+                    for (SubgraphNode node : subgraph) {
+                        // Index the node ...
+                        indexSession.index(node);
+                        ++count;
+
+                        // Process the children ...
+                        for (Location child : node.getChildren()) {
+                            if (!subgraph.includes(child)) {
+                                // Record this location as needing to be read ...
+                                locationsToRead.add(child);
+                            }
+                        }
+                    }
+                }
+                return count;
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                String path = readable(location.getPath());
+                return SearchI18n.errorWhileIndexingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will remove from the indexes all documents that represent content at or below the specified
+     * location.
+     * 
+     * @param location the location of the content to removed; may not be null
+     * @return the activity that will perform the work
+     */
+    protected Activity removeContent( final Location location ) {
+        return new Activity() {
+
+            public int execute( IndexSession indexSession ) throws IOException {
+                // Delete the content at/below the path ...
+                return indexSession.deleteBelow(location.getPath());
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                String path = readable(location.getPath());
+                return SearchI18n.errorWhileRemovingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will update the indexes with changes that were already made to the content.
+     * 
+     * @param changes the changes that have been made to the content; may not be null
+     * @return the activity that will perform the work
+     */
+    protected Activity updateContent( final Iterable<ChangeRequest> changes ) {
+        return new Activity() {
+
+            public int execute( IndexSession indexSession ) throws IOException {
+                return indexSession.apply(changes);
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return SearchI18n.errorWhileUpdatingContent.text(workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will perform a full-text search given the supplied query.
+     * 
+     * @param context the context in which the search is to be performed; may not be null
+     * @param fullTextSearch the full-text search to be performed; may not be null
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @return the activity that will perform the work; never null
+     */
+    protected Search searchContent( final ExecutionContext context,
+                                    final String fullTextSearch,
+                                    final int maxResults,
+                                    final int offset ) {
+        final List<Location> results = new ArrayList<Location>(maxResults);
+        return new Search() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
+             */
+            public int execute( IndexSession session ) throws IOException, ParseException {
+                session.search(context, fullTextSearch, maxResults, offset, results);
+                return 0;
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
+             *      java.lang.String)
+             */
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return SearchI18n.errorWhilePerformingSearch.text(fullTextSearch, workspaceName, sourceName, error.getMessage());
+            }
+
+            public List<Location> getResults() {
+                return results;
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will perform a query against the index.
+     * 
+     * @param context the context in which the search is to be performed; may not be null
+     * @param query the query to be performed; may not be null
+     * @return the activity that will perform the query; never null
+     */
+    protected Query queryContent( final QueryContext context,
+                                  final QueryCommand query ) {
+        return new Query() {
+            private QueryResults results = null;
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
+             */
+            public int execute( IndexSession session ) throws IOException, ParseException {
+                results = session.query(context, query);
+                return 0;
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
+             *      java.lang.String)
+             */
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return SearchI18n.errorWhilePerformingQuery.text(query, workspaceName, sourceName, error.getMessage());
+            }
+
+            public QueryResults getResults() {
+                return results;
+            }
+        };
+    }
+
+    /**
+     * Interface for activities that will be executed against a workspace. These activities don't have to commit or roll back the
+     * writer, nor do they have to translate the exceptions, since this is done by the
+     * {@link Workspace#execute(boolean, Activity...)} method.
+     */
+    protected interface Activity {
+
+        /**
+         * Perform the activity by using the index writer.
+         * 
+         * @param indexSession the index session that should be used by the activity; never null
+         * @return the number of changes that were made by this activity
+         * @throws IOException if there is an error using the writer
+         * @throws ParseException if there is an error due to parsing
+         */
+        int execute( IndexSession indexSession ) throws IOException, ParseException;
+
+        /**
+         * Translate an exception obtained during {@link #execute(IndexSession) execution} into a single message.
+         * 
+         * @param t the exception
+         * @param sourceName the name of the source
+         * @param workspaceName the name of the workspace
+         * @return the error message
+         */
+        String messageFor( Throwable t,
+                           String sourceName,
+                           String workspaceName );
+    }
+
+    /**
+     * A read-only activity.
+     */
+    protected interface ReadOnlyActivity extends Activity {
+    }
+
+    /**
+     * A search activity.
+     */
+    protected interface Search extends ReadOnlyActivity {
+        /**
+         * Get the results of the search.
+         * 
+         * @return the list of {@link Location} objects for each node satisfying the results; never null
+         */
+        List<Location> getResults();
+    }
+
+    /**
+     * A query activity.
+     */
+    protected interface Query extends ReadOnlyActivity {
+        /**
+         * Get the results of the query.
+         * 
+         * @return the results of a query; never null
+         */
+        QueryResults getResults();
+    }
+
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -40,11 +40,14 @@
     public static I18n errorWhileRemovingContentAtPath;
     public static I18n errorWhileUpdatingContent;
     public static I18n errorWhileCommittingIndexChanges;
+    public static I18n errorWhileRollingBackIndexChanges;
     public static I18n errorCreatingIndexWriter;
     public static I18n errorWhileOptimizingIndexes;
     public static I18n errorWhilePerformingSearch;
     public static I18n errorWhilePerformingQuery;
     public static I18n errorWhileInitializingSearchEngine;
+    public static I18n errorWhileRemovingIndexesForWorkspace;
+    public static I18n errorWhilePerformingLuceneQuery;
 
     static {
         try {

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,569 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriter.MaxFieldLength;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.store.Directory;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Subgraph;
-import org.jboss.dna.graph.SubgraphNode;
-import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * A search engine dedicated to a single workspace.
- */
- at ThreadSafe
-class WorkspaceSearchEngine {
-
-    protected static final String PATHS_INDEX_NAME = "paths";
-    protected static final String CONTENT_INDEX_NAME = "content";
-
-    private final Directory pathsDirectory;
-    private final Directory contentDirectory;
-    private final ExecutionContext context;
-    private final ExecutionContext encodedContext;
-    private final String sourceName;
-    private final String workspaceName;
-    private final RepositoryConnectionFactory connectionFactory;
-    private final IndexStrategy indexingStrategy;
-    protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
-
-    /**
-     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
-     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
-     * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
-     * 
-     * @param context the execution context in which all indexing operations should be performed
-     * @param directoryFactory the factory from which can be obtained the Lucene directory where the indexes should be persisted
-     * @param indexingStrategy the indexing strategy that governs how properties are to be indexed; may not be null
-     * @param sourceName the name of the {@link RepositorySource}
-     * @param workspaceName the name of the workspace
-     * @param connectionFactory the connection factory
-     * @throws IllegalArgumentException if any of the parameters are null
-     * @throws SearchEngineException if there is a problem initializing this engine
-     */
-    protected WorkspaceSearchEngine( ExecutionContext context,
-                                     DirectoryConfiguration directoryFactory,
-                                     IndexStrategy indexingStrategy,
-                                     String sourceName,
-                                     String workspaceName,
-                                     RepositoryConnectionFactory connectionFactory ) throws SearchEngineException {
-        CheckArg.isNotNull(context, "context");
-        CheckArg.isNotNull(directoryFactory, "directoryFactory");
-        CheckArg.isNotNull(sourceName, "sourceName");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        CheckArg.isNotNull(connectionFactory, "connectionFactory");
-        CheckArg.isNotNull(indexingStrategy, "indexingStrategy");
-        this.sourceName = sourceName;
-        this.workspaceName = workspaceName;
-        this.connectionFactory = connectionFactory;
-        this.context = context;
-        this.indexingStrategy = indexingStrategy;
-        this.encodedContext = context.with(new EncodingNamespaceRegistry(context.getNamespaceRegistry(),
-                                                                         this.indexingStrategy.getNamespaceEncoder()));
-        this.pathsDirectory = directoryFactory.getDirectory(workspaceName, PATHS_INDEX_NAME);
-        this.contentDirectory = directoryFactory.getDirectory(workspaceName, CONTENT_INDEX_NAME);
-
-        initialize();
-    }
-
-    protected void initialize() throws SearchEngineException {
-        // Always create the index if not there ...
-        try {
-            Analyzer analyzer = this.indexingStrategy.createAnalyzer();
-            ensureIndexesExist(this.pathsDirectory, analyzer);
-            ensureIndexesExist(this.contentDirectory, analyzer);
-        } catch (IOException e) {
-            String msg = SearchI18n.errorWhileInitializingSearchEngine.text(workspaceName, sourceName, e.getMessage());
-            throw new SearchEngineException(msg, e);
-        }
-    }
-
-    private static void ensureIndexesExist( Directory directory,
-                                            Analyzer analyzer ) throws IOException {
-        IndexWriter writer = null;
-        Throwable error = null;
-        try {
-            writer = new IndexWriter(directory, analyzer, false, MaxFieldLength.UNLIMITED);
-        } catch (FileNotFoundException e) {
-            // The index files don't yet exist, so we need to create them ...
-            try {
-                writer = new IndexWriter(directory, analyzer, true, MaxFieldLength.UNLIMITED);
-            } catch (Throwable t) {
-                error = t;
-            }
-        } catch (Throwable t) {
-            error = t;
-        } finally {
-            if (writer != null) {
-                // Either way, make sure we close the writer that we created ...
-                try {
-                    writer.close();
-                } catch (IOException e) {
-                    if (error == null) throw e;
-                } catch (RuntimeException e) {
-                    if (error == null) throw e;
-                }
-            }
-        }
-    }
-
-    final Graph graph() {
-        Graph graph = Graph.create(sourceName, connectionFactory, context);
-        graph.useWorkspace(workspaceName);
-        return graph;
-    }
-
-    final String workspaceName() {
-        return workspaceName;
-    }
-
-    final String sourceName() {
-        return sourceName;
-    }
-
-    final String readable( Path path ) {
-        return context.getValueFactories().getStringFactory().create(path);
-    }
-
-    final IndexStrategy strategy() {
-        return indexingStrategy;
-    }
-
-    /**
-     * Index all of the content at or below the supplied path.
-     * 
-     * @param startingPoint the path that represents the content to be indexed
-     * @param depthPerBatch the depth of each subgraph read operation
-     * @throws IllegalArgumentException if the path is null or the depth is not positive
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     */
-    public void indexContent( Path startingPoint,
-                              int depthPerBatch ) throws RepositorySourceException, SearchEngineException {
-        CheckArg.isNotNull(startingPoint, "startingPoint");
-        indexContent(Location.create(startingPoint), depthPerBatch);
-    }
-
-    /**
-     * Index all of the content at or below the supplied location.
-     * 
-     * @param startingPoint the location that represents the content to be indexed
-     * @param depthPerRead the depth of each subgraph read operation
-     * @throws IllegalArgumentException if the location is null or the depth is not positive
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     */
-    public void indexContent( Location startingPoint,
-                              int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        CheckArg.isNotNull(startingPoint, "startingPoint");
-        CheckArg.isPositive(depthPerRead, "depthPerBatch");
-        assert startingPoint.hasPath();
-
-        if (startingPoint.getPath().isRoot()) {
-            // More efficient to just start over with a new index ...
-            execute(true, addContent(startingPoint, depthPerRead));
-        } else {
-            // Have to first remove the content below the starting point, then add it again ...
-            execute(false, removeContent(startingPoint), addContent(startingPoint, depthPerRead));
-        }
-    }
-
-    /**
-     * Update the indexes with the supplied set of changes to the content.
-     * 
-     * @param changes the set of changes to the content
-     * @throws IllegalArgumentException if the path is null
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     */
-    public void indexChanges( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
-        CheckArg.isNotNull(changes, "changes");
-        execute(false, updateContent(changes));
-    }
-
-    /**
-     * Invoke the engine's garbage collection on all indexes used by this workspace. This method reclaims space and optimizes the
-     * index. This should be done on a periodic basis after changes are made to the engine's indexes.
-     * 
-     * @throws SearchEngineException if there is a problem during optimization
-     */
-    public void optimize() throws SearchEngineException {
-        execute(false, optimizeContent());
-    }
-
-    /**
-     * Create an activity that will perform a full-text search given the supplied query.
-     * 
-     * @param fullTextSearch the full-text search to be performed; may not be null
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @return the activity that will perform the work
-     */
-    public List<Location> fullTextSearch( final String fullTextSearch,
-                                          final int maxResults,
-                                          final int offset ) {
-        return execute(false, searchContent(fullTextSearch, maxResults, offset)).getResults();
-    }
-
-    /**
-     * Create an activity that will perform a query of the content in this workspace, given the Abstract Query Model
-     * representation of the query.
-     * 
-     * @param query the query that is to be executed, in the form of the Abstract Query Model
-     * @param schemata the definition of the tables available for the query; may not be null
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     */
-    public QueryResults execute( QueryCommand query,
-                                 Schemata schemata ) {
-        return execute(false, queryContent(query, schemata)).getResults();
-    }
-
-    /**
-     * Execute the supplied activities against the indexes.
-     * 
-     * @param <ActivityType> the type of activity
-     * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
-     * @param activity the activity to execute
-     * @return the same activity that was supplied as a parameter, returned as a convenience
-     * @throws SearchEngineException if there is a problem performing the activities
-     */
-    protected final <ActivityType extends Activity> ActivityType execute( boolean overwrite,
-                                                                          ActivityType activity ) throws SearchEngineException {
-        execute(overwrite, new Activity[] {activity});
-        return activity;
-    }
-
-    /**
-     * Execute the supplied activities against the indexes.
-     * 
-     * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
-     * @param activities the activities to execute
-     * @throws SearchEngineException if there is a problem performing the activities
-     */
-    protected final void execute( boolean overwrite,
-                                  Activity... activities ) throws SearchEngineException {
-        // Determine if the activities are readonly ...
-        boolean readOnly = true;
-        for (Activity activity : activities) {
-            if (!(activity instanceof ReadOnlyActivity)) {
-                readOnly = false;
-                break;
-            }
-        }
-
-        Analyzer analyzer = this.indexingStrategy.createAnalyzer();
-        IndexContext indexes = new IndexContext(encodedContext, pathsDirectory, contentDirectory, analyzer, overwrite, readOnly);
-
-        // Execute the various activities ...
-        Throwable error = null;
-        try {
-            for (Activity activity : activities) {
-                try {
-                    activity.execute(indexes);
-                } catch (IOException e) {
-                    error = e;
-                    throw new SearchEngineException(activity.messageFor(e), e);
-                } catch (ParseException e) {
-                    error = e;
-                    throw new SearchEngineException(activity.messageFor(e), e);
-                } catch (RuntimeException e) {
-                    error = e;
-                    throw e;
-                }
-            }
-            if (indexes.hasWriters()) {
-                // Determine if there have been enough changes made to run the optimizer ...
-                int maxChanges = this.indexingStrategy.getChangeCountForAutomaticOptimization();
-                if (maxChanges > 0 && this.modifiedNodesSinceLastOptimize.get() >= maxChanges) {
-                    Activity optimizer = optimizeContent();
-                    try {
-                        optimizer.execute(indexes);
-                    } catch (ParseException e) {
-                        error = e;
-                        throw new SearchEngineException(optimizer.messageFor(e), e);
-                    } catch (IOException e) {
-                        error = e;
-                        throw new SearchEngineException(optimizer.messageFor(e), e);
-                    } catch (RuntimeException e) {
-                        error = e;
-                        throw e;
-                    }
-                }
-            }
-        } finally {
-            try {
-                if (error == null) {
-                    indexes.commit();
-                } else {
-                    indexes.rollback();
-                }
-            } catch (IOException e2) {
-                // We don't want to lose the existing error, if there is one ...
-                if (error == null) {
-                    I18n msg = SearchI18n.errorWhileCommittingIndexChanges;
-                    throw new SearchEngineException(msg.text(workspaceName(), sourceName(), e2.getMessage()), e2);
-                }
-            }
-        }
-    }
-
-    /**
-     * Interface for activities that will be executed against the set of indexes. These activities don't have to commit or roll
-     * back the writer, nor do they have to translate the exceptions, since this is done by the
-     * {@link WorkspaceSearchEngine#execute(boolean, Activity...)} method.
-     * 
-     * @see ReadOnlyActivity
-     */
-    protected interface Activity {
-
-        /**
-         * Perform the activity by using the index writer.
-         * 
-         * @param indexes the set of indexes to use; never null
-         * @throws IOException if there is an error using the writer
-         * @throws ParseException if there is an error due to parsing
-         */
-        void execute( IndexContext indexes ) throws IOException, ParseException;
-
-        /**
-         * Translate an exception obtained during {@link #execute(IndexContext) execution} into a single message.
-         * 
-         * @param t the exception
-         * @return the error message
-         */
-        String messageFor( Throwable t );
-    }
-
-    protected interface ReadOnlyActivity extends Activity {
-    }
-
-    protected interface Search extends ReadOnlyActivity {
-        /**
-         * Get the results of the search.
-         * 
-         * @return the list of {@link Location} objects for each node satisfying the results; never null
-         */
-        List<Location> getResults();
-    }
-
-    protected interface Query extends ReadOnlyActivity {
-        /**
-         * Get the results of the query.
-         * 
-         * @return the results of a query; never null
-         */
-        QueryResults getResults();
-    }
-
-    /**
-     * Create an activity that will read from the source the content at the supplied location and add the content to the search
-     * index.
-     * 
-     * @param location the location of the content to read; may not be null
-     * @param depthPerRead the depth of each read operation; always positive
-     * @return the activity that will perform the work
-     */
-    protected Activity addContent( final Location location,
-                                   final int depthPerRead ) {
-        return new Activity() {
-            public void execute( IndexContext indexes ) throws IOException {
-
-                // Create a queue that we'll use to walk the content ...
-                LinkedList<Location> locationsToRead = new LinkedList<Location>();
-                locationsToRead.add(location);
-                int count = 0;
-
-                // Now read and index the content ...
-                Graph graph = graph();
-                while (!locationsToRead.isEmpty()) {
-                    Location location = locationsToRead.poll();
-                    if (location == null) continue;
-                    Subgraph subgraph = graph.getSubgraphOfDepth(depthPerRead).at(location);
-                    // Index all of the nodes within this subgraph ...
-                    for (SubgraphNode node : subgraph) {
-                        // Index the node ...
-                        strategy().index(node, indexes);
-                        ++count;
-
-                        // Process the children ...
-                        for (Location child : node.getChildren()) {
-                            if (!subgraph.includes(child)) {
-                                // Record this location as needing to be read ...
-                                locationsToRead.add(child);
-                            }
-                        }
-                    }
-                }
-                modifiedNodesSinceLastOptimize.addAndGet(count);
-            }
-
-            public String messageFor( Throwable error ) {
-                String path = readable(location.getPath());
-                return SearchI18n.errorWhileIndexingContentAtPath.text(path, workspaceName(), sourceName(), error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will remove from the indexes all documents that represent content at or below the specified
-     * location.
-     * 
-     * @param location the location of the content to removed; may not be null
-     * @return the activity that will perform the work
-     */
-    protected Activity removeContent( final Location location ) {
-        return new Activity() {
-
-            public void execute( IndexContext indexes ) throws IOException {
-                // Delete the content at/below the path ...
-                modifiedNodesSinceLastOptimize.addAndGet(strategy().deleteBelow(location.getPath(), indexes));
-            }
-
-            public String messageFor( Throwable error ) {
-                String path = readable(location.getPath());
-                return SearchI18n.errorWhileRemovingContentAtPath.text(path, workspaceName(), sourceName(), error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will optimize the indexes.
-     * 
-     * @return the activity that will perform the work
-     */
-    protected Activity optimizeContent() {
-        return new Activity() {
-            public void execute( IndexContext indexes ) throws IOException {
-                // Don't block ...
-                indexes.getContentWriter().optimize();
-                indexes.getPathsWriter().optimize();
-            }
-
-            public String messageFor( Throwable error ) {
-                return SearchI18n.errorWhileOptimizingIndexes.text(workspaceName(), sourceName(), error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will update the indexes with changes that were already made to the content.
-     * 
-     * @param changes the changes that have been made to the content; may not be null
-     * @return the activity that will perform the work
-     */
-    protected Activity updateContent( final Iterable<ChangeRequest> changes ) {
-        return new Activity() {
-
-            public void execute( IndexContext indexes ) throws IOException {
-                // Iterate over the changes ...
-                modifiedNodesSinceLastOptimize.addAndGet(strategy().apply(changes, indexes));
-            }
-
-            public String messageFor( Throwable error ) {
-                return SearchI18n.errorWhileUpdatingContent.text(workspaceName(), sourceName(), error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will perform a full-text search given the supplied query.
-     * 
-     * @param fullTextSearch the full-text search to be performed; may not be null
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @return the activity that will perform the work
-     */
-    protected Search searchContent( final String fullTextSearch,
-                                    final int maxResults,
-                                    final int offset ) {
-        final List<Location> results = new ArrayList<Location>(maxResults);
-        return new Search() {
-            public void execute( IndexContext indexes ) throws IOException, ParseException {
-                strategy().search(fullTextSearch, maxResults, offset, indexes, results);
-            }
-
-            public String messageFor( Throwable error ) {
-                return SearchI18n.errorWhilePerformingSearch.text(fullTextSearch,
-                                                                  workspaceName(),
-                                                                  sourceName(),
-                                                                  error.getMessage());
-            }
-
-            public List<Location> getResults() {
-                return results;
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will perform a query against the index.
-     * 
-     * @param query the query to be performed; may not be null
-     * @param schemata the definition of the tables being used in the query; may not be null
-     * @return the activity that will perform the work
-     */
-    protected Query queryContent( final QueryCommand query,
-                                  final Schemata schemata ) {
-        return new Query() {
-            private QueryResults results = null;
-            private SearchContext context = null;
-
-            public void execute( IndexContext indexes ) {
-                context = new SearchContext(indexes, schemata);
-                results = strategy().query(context, query);
-            }
-
-            public String messageFor( Throwable error ) {
-                return SearchI18n.errorWhilePerformingQuery.text(query, workspaceName(), sourceName(), error.getMessage());
-            }
-
-            public QueryResults getResults() {
-                return results;
-            }
-        };
-    }
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,36 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.filters;
+
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ * 
+ */
+public interface ResultFilter {
+
+    boolean allows( Object[] tuple,
+                    Columns columns );
+
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareNameQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareNameQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareNameQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,258 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.model.Comparison;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link Comparison} constraint against the name of nodes. This
+ * query implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * that correspond to nodes with Names that satisfy the constraint.
+ */
+public class CompareNameQuery extends CompareQuery<Path.Segment> {
+
+    private static final long serialVersionUID = 1L;
+    protected static final Evaluator<Path.Segment> IS_LESS_THAN = new Evaluator<Path.Segment>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path.Segment nodeValue,
+                                            Path.Segment constraintValue ) {
+            return ValueComparators.PATH_SEGMENT_COMPARATOR.compare(nodeValue, constraintValue) < 0;
+        }
+
+        @Override
+        public String toString() {
+            return " < ";
+        }
+    };
+    protected static final Evaluator<Path.Segment> IS_LESS_THAN_OR_EQUAL_TO = new Evaluator<Path.Segment>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path.Segment nodeValue,
+                                            Path.Segment constraintValue ) {
+            return ValueComparators.PATH_SEGMENT_COMPARATOR.compare(nodeValue, constraintValue) <= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " <= ";
+        }
+    };
+    protected static final Evaluator<Path.Segment> IS_GREATER_THAN = new Evaluator<Path.Segment>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path.Segment nodeValue,
+                                            Path.Segment constraintValue ) {
+            return ValueComparators.PATH_SEGMENT_COMPARATOR.compare(nodeValue, constraintValue) > 0;
+        }
+
+        @Override
+        public String toString() {
+            return " > ";
+        }
+    };
+    protected static final Evaluator<Path.Segment> IS_GREATER_THAN_OR_EQUAL_TO = new Evaluator<Path.Segment>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path.Segment nodeValue,
+                                            Path.Segment constraintValue ) {
+            return ValueComparators.PATH_SEGMENT_COMPARATOR.compare(nodeValue, constraintValue) >= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " >= ";
+        }
+    };
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a name
+     * that is greater than the supplied constraint name.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param localNameField the name of the document field containing the local name value; may not be null
+     * @param snsIndexFieldName the name of the document field containing the same-name-sibling index; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareNameQuery createQueryForNodesWithNameGreaterThan( Path.Segment constraintValue,
+                                                                           String localNameField,
+                                                                           String snsIndexFieldName,
+                                                                           ValueFactories factories,
+                                                                           boolean caseSensitive ) {
+        return new CompareNameQuery(localNameField, snsIndexFieldName, constraintValue, factories.getPathFactory(),
+                                    factories.getStringFactory(), factories.getLongFactory(), IS_GREATER_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a name
+     * that is greater than or equal to the supplied constraint name.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param localNameField the name of the document field containing the local name value; may not be null
+     * @param snsIndexFieldName the name of the document field containing the same-name-sibling index; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareNameQuery createQueryForNodesWithNameGreaterThanOrEqualTo( Path.Segment constraintValue,
+                                                                                    String localNameField,
+                                                                                    String snsIndexFieldName,
+                                                                                    ValueFactories factories,
+                                                                                    boolean caseSensitive ) {
+        return new CompareNameQuery(localNameField, snsIndexFieldName, constraintValue, factories.getPathFactory(),
+                                    factories.getStringFactory(), factories.getLongFactory(), IS_GREATER_THAN_OR_EQUAL_TO,
+                                    caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a name
+     * that is less than the supplied constraint name.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param localNameField the name of the document field containing the local name value; may not be null
+     * @param snsIndexFieldName the name of the document field containing the same-name-sibling index; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareNameQuery createQueryForNodesWithNameLessThan( Path.Segment constraintValue,
+                                                                        String localNameField,
+                                                                        String snsIndexFieldName,
+                                                                        ValueFactories factories,
+                                                                        boolean caseSensitive ) {
+        return new CompareNameQuery(localNameField, snsIndexFieldName, constraintValue, factories.getPathFactory(),
+                                    factories.getStringFactory(), factories.getLongFactory(), IS_LESS_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a name
+     * that is less than or equal to the supplied constraint name.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param localNameField the name of the document field containing the local name value; may not be null
+     * @param snsIndexFieldName the name of the document field containing the same-name-sibling index; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareNameQuery createQueryForNodesWithNameLessThanOrEqualTo( Path.Segment constraintValue,
+                                                                                 String localNameField,
+                                                                                 String snsIndexFieldName,
+                                                                                 ValueFactories factories,
+                                                                                 boolean caseSensitive ) {
+        return new CompareNameQuery(localNameField, snsIndexFieldName, constraintValue, factories.getPathFactory(),
+                                    factories.getStringFactory(), factories.getLongFactory(), IS_LESS_THAN_OR_EQUAL_TO,
+                                    caseSensitive);
+    }
+
+    private final String snsIndexFieldName;
+    private final ValueFactory<Long> longFactory;
+    private final PathFactory pathFactory;
+    private final boolean caseSensitive;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param localNameField the name of the document field containing the local name value; may not be null
+     * @param snsIndexFieldName the name of the document field containing the same-name-sibling index; may not be null
+     * @param constraintValue the constraint path; may not be null
+     * @param pathFactory the path factory that can be used during the scoring; may not be null
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param longFactory the long factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link CompareQuery.Evaluator} implementation that returns whether the node path satisfies the
+     *        constraint; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     */
+    protected CompareNameQuery( final String localNameField,
+                                final String snsIndexFieldName,
+                                Path.Segment constraintValue,
+                                PathFactory pathFactory,
+                                ValueFactory<String> stringFactory,
+                                ValueFactory<Long> longFactory,
+                                Evaluator<Path.Segment> evaluator,
+                                boolean caseSensitive ) {
+        super(localNameField, constraintValue, null, stringFactory, evaluator, new FieldSelector() {
+            private static final long serialVersionUID = 1L;
+
+            public FieldSelectorResult accept( String fieldName ) {
+                if (fieldName.equals(localNameField)) return FieldSelectorResult.LOAD;
+                if (fieldName.equals(snsIndexFieldName)) return FieldSelectorResult.LOAD;
+                return FieldSelectorResult.NO_LOAD;
+            }
+        });
+        this.snsIndexFieldName = snsIndexFieldName;
+        this.longFactory = longFactory;
+        this.pathFactory = pathFactory;
+        this.caseSensitive = caseSensitive;
+        assert this.snsIndexFieldName != null;
+        assert this.longFactory != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.query.CompareQuery#readFromDocument(org.apache.lucene.index.IndexReader, int)
+     */
+    @Override
+    protected Path.Segment readFromDocument( IndexReader reader,
+                                             int docId ) throws IOException {
+        Document doc = reader.document(docId, fieldSelector);
+        String localName = doc.get(fieldName);
+        if (!caseSensitive) localName = localName.toLowerCase();
+        int sns = longFactory.create(doc.get(snsIndexFieldName)).intValue();
+        return pathFactory.createSegment(localName, sns);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new CompareNameQuery(fieldName, snsIndexFieldName, constraintValue, pathFactory, stringFactory, longFactory,
+                                    evaluator, caseSensitive);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareNameQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/ComparePathQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/ComparePathQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/ComparePathQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,223 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.model.Comparison;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link Comparison} constraint against the Path of nodes. This
+ * query implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * that correspond to nodes with Paths that satisfy the constraint.
+ */
+public class ComparePathQuery extends CompareQuery<Path> {
+
+    private static final long serialVersionUID = 1L;
+    protected static final Evaluator<Path> PATH_IS_LESS_THAN = new Evaluator<Path>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path nodePath,
+                                            Path constraintPath ) {
+            return ValueComparators.PATH_COMPARATOR.compare(nodePath, constraintPath) < 0;
+        }
+
+        @Override
+        public String toString() {
+            return " < ";
+        }
+    };
+    protected static final Evaluator<Path> PATH_IS_LESS_THAN_OR_EQUAL_TO = new Evaluator<Path>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path nodePath,
+                                            Path constraintPath ) {
+            return ValueComparators.PATH_COMPARATOR.compare(nodePath, constraintPath) <= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " <= ";
+        }
+    };
+    protected static final Evaluator<Path> PATH_IS_GREATER_THAN = new Evaluator<Path>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path nodePath,
+                                            Path constraintPath ) {
+            return ValueComparators.PATH_COMPARATOR.compare(nodePath, constraintPath) > 0;
+        }
+
+        @Override
+        public String toString() {
+            return " > ";
+        }
+    };
+    protected static final Evaluator<Path> PATH_IS_GREATER_THAN_OR_EQUAL_TO = new Evaluator<Path>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Path nodePath,
+                                            Path constraintPath ) {
+            return ValueComparators.PATH_COMPARATOR.compare(nodePath, constraintPath) >= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " >= ";
+        }
+    };
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a path
+     * that is greater than the supplied constraint path.
+     * 
+     * @param constraintPath the constraint path; may not be null
+     * @param fieldName the name of the document field containing the path value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the path query; never null
+     */
+    public static ComparePathQuery createQueryForNodesWithPathGreaterThan( Path constraintPath,
+                                                                           String fieldName,
+                                                                           ValueFactories factories,
+                                                                           boolean caseSensitive ) {
+        return new ComparePathQuery(fieldName, constraintPath, factories.getPathFactory(), factories.getStringFactory(),
+                                    PATH_IS_GREATER_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a path
+     * that is greater than or equal to the supplied constraint path.
+     * 
+     * @param constraintPath the constraint path; may not be null
+     * @param fieldName the name of the document field containing the path value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the path query; never null
+     */
+    public static ComparePathQuery createQueryForNodesWithPathGreaterThanOrEqualTo( Path constraintPath,
+                                                                                    String fieldName,
+                                                                                    ValueFactories factories,
+                                                                                    boolean caseSensitive ) {
+        return new ComparePathQuery(fieldName, constraintPath, factories.getPathFactory(), factories.getStringFactory(),
+                                    PATH_IS_GREATER_THAN_OR_EQUAL_TO, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a path
+     * that is less than the supplied constraint path.
+     * 
+     * @param constraintPath the constraint path; may not be null
+     * @param fieldName the name of the document field containing the path value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the path query; never null
+     */
+    public static ComparePathQuery createQueryForNodesWithPathLessThan( Path constraintPath,
+                                                                        String fieldName,
+                                                                        ValueFactories factories,
+                                                                        boolean caseSensitive ) {
+        return new ComparePathQuery(fieldName, constraintPath, factories.getPathFactory(), factories.getStringFactory(),
+                                    PATH_IS_LESS_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents such that the node represented by the document has a path
+     * that is less than or equal to the supplied constraint path.
+     * 
+     * @param constraintPath the constraint path; may not be null
+     * @param fieldName the name of the document field containing the path value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the path query; never null
+     */
+    public static ComparePathQuery createQueryForNodesWithPathLessThanOrEqualTo( Path constraintPath,
+                                                                                 String fieldName,
+                                                                                 ValueFactories factories,
+                                                                                 boolean caseSensitive ) {
+        return new ComparePathQuery(fieldName, constraintPath, factories.getPathFactory(), factories.getStringFactory(),
+                                    PATH_IS_LESS_THAN_OR_EQUAL_TO, caseSensitive);
+    }
+
+    private final boolean caseSensitive;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the path value; may not be null
+     * @param constraintPath the constraint path; may not be null
+     * @param pathFactory the value factory that can be used during the scoring; may not be null
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link CompareQuery.Evaluator} implementation that returns whether the node path satisfies the
+     *        constraint; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     */
+    protected ComparePathQuery( String fieldName,
+                                Path constraintPath,
+                                ValueFactory<Path> pathFactory,
+                                ValueFactory<String> stringFactory,
+                                Evaluator<Path> evaluator,
+                                boolean caseSensitive ) {
+        super(fieldName, constraintPath, pathFactory, stringFactory, evaluator);
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.query.CompareQuery#readFromDocument(org.apache.lucene.index.IndexReader, int)
+     */
+    @Override
+    protected Path readFromDocument( IndexReader reader,
+                                     int docId ) throws IOException {
+        Document doc = reader.document(docId, fieldSelector);
+        String valueString = doc.get(fieldName);
+        if (!caseSensitive) valueString = valueString.toLowerCase();
+        return valueTypeFactory.create(valueString);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new ComparePathQuery(fieldName, constraintValue, valueTypeFactory, stringFactory, evaluator, caseSensitive);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/ComparePathQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,308 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import java.io.Serializable;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.model.Comparison;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link Comparison} constraint against the Path of nodes. This
+ * query implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * that correspond to nodes with Paths that satisfy the constraint.
+ * 
+ * @param <ValueType>
+ */
+public abstract class CompareQuery<ValueType> extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    protected static interface Evaluator<ValueType> extends Serializable {
+        boolean satisfiesConstraint( ValueType nodeValue,
+                                     ValueType constraintValue );
+    }
+
+    /**
+     * The operand that is being negated by this query.
+     */
+    protected final String fieldName;
+    protected final FieldSelector fieldSelector;
+    protected final ValueType constraintValue;
+    protected final Evaluator<ValueType> evaluator;
+    protected final ValueFactory<ValueType> valueTypeFactory;
+    protected final ValueFactory<String> stringFactory;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param constraintValue the constraint value; may not be null
+     * @param valueTypeFactory the value factory that can be used during the scoring; may not be null
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link Evaluator} implementation that returns whether the node value satisfies the constraint; may not
+     *        be null
+     */
+    protected CompareQuery( String fieldName,
+                            ValueType constraintValue,
+                            ValueFactory<ValueType> valueTypeFactory,
+                            ValueFactory<String> stringFactory,
+                            Evaluator<ValueType> evaluator ) {
+        this(fieldName, constraintValue, valueTypeFactory, stringFactory, evaluator, null);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param constraintValue the constraint value; may not be null
+     * @param valueTypeFactory the value factory that can be used during the scoring; may not be null unless
+     *        {@link #readFromDocument(IndexReader, int)} is overloaded to not use it
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link Evaluator} implementation that returns whether the node value satisfies the constraint; may not
+     *        be null
+     * @param fieldSelector the field selector that should load the fields needed to recover the value; may be null if the field
+     *        selector should be generated automatically
+     */
+    protected CompareQuery( String fieldName,
+                            ValueType constraintValue,
+                            ValueFactory<ValueType> valueTypeFactory,
+                            ValueFactory<String> stringFactory,
+                            Evaluator<ValueType> evaluator,
+                            FieldSelector fieldSelector ) {
+        this.fieldName = fieldName;
+        this.constraintValue = constraintValue;
+        this.valueTypeFactory = valueTypeFactory;
+        this.stringFactory = stringFactory;
+        this.evaluator = evaluator;
+        assert this.fieldName != null;
+        assert this.constraintValue != null;
+        assert this.evaluator != null;
+        this.fieldSelector = fieldSelector != null ? fieldSelector : new FieldSelector() {
+            private static final long serialVersionUID = 1L;
+
+            public FieldSelectorResult accept( String fieldName ) {
+                return fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+            }
+        };
+    }
+
+    protected ValueType readFromDocument( IndexReader reader,
+                                          int docId ) throws IOException {
+        Document doc = reader.document(docId, fieldSelector);
+        String valueString = doc.get(fieldName);
+        return valueTypeFactory.create(valueString);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new NotWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return fieldName + " " + evaluator.toString() + " " + stringFactory != null ? stringFactory.create(constraintValue) : constraintValue.toString();
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class NotWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+        private final Searcher searcher;
+
+        protected NotWeight( Searcher searcher ) {
+            this.searcher = searcher;
+            assert this.searcher != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return CompareQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) {
+            // Return a custom scorer ...
+            return new NotScorer(reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) {
+            return new Explanation(getValue(), getQuery().toString());
+        }
+    }
+
+    /**
+     * A scorer for the Path query.
+     */
+    protected class NotScorer extends Scorer {
+        private int docId = -1;
+        private final int maxDocId;
+        private final IndexReader reader;
+
+        protected NotScorer( IndexReader reader ) {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+            this.reader = reader;
+            assert this.reader != null;
+            this.maxDocId = this.reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() throws IOException {
+            do {
+                ++docId;
+                if (reader.isDeleted(docId)) {
+                    // We should skip this document ...
+                    continue;
+                }
+                ValueType value = readFromDocument(reader, docId);
+                if (evaluator.satisfiesConstraint(value, constraintValue)) return docId;
+            } while (docId < maxDocId);
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) throws IOException {
+            if (target == Scorer.NO_MORE_DOCS) return target;
+            while (true) {
+                int doc = nextDoc();
+                if (doc >= target) return doc;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,219 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.model.Comparison;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link Comparison} constraint against a string field. This query
+ * implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * with string fields that satisfy the constraint.
+ */
+public class CompareStringQuery extends CompareQuery<String> {
+
+    private static final long serialVersionUID = 1L;
+    protected static final Evaluator<String> IS_LESS_THAN = new Evaluator<String>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( String nodeValue,
+                                            String constraintValue ) {
+            return ValueComparators.STRING_COMPARATOR.compare(nodeValue, constraintValue) < 0;
+        }
+
+        @Override
+        public String toString() {
+            return " < ";
+        }
+    };
+    protected static final Evaluator<String> IS_LESS_THAN_OR_EQUAL_TO = new Evaluator<String>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( String nodeValue,
+                                            String constraintValue ) {
+            return ValueComparators.STRING_COMPARATOR.compare(nodeValue, constraintValue) <= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " <= ";
+        }
+    };
+    protected static final Evaluator<String> IS_GREATER_THAN = new Evaluator<String>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( String nodeValue,
+                                            String constraintValue ) {
+            return ValueComparators.STRING_COMPARATOR.compare(nodeValue, constraintValue) > 0;
+        }
+
+        @Override
+        public String toString() {
+            return " > ";
+        }
+    };
+    protected static final Evaluator<String> IS_GREATER_THAN_OR_EQUAL_TO = new Evaluator<String>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( String nodeValue,
+                                            String constraintValue ) {
+            return ValueComparators.STRING_COMPARATOR.compare(nodeValue, constraintValue) >= 0;
+        }
+
+        @Override
+        public String toString() {
+            return " >= ";
+        }
+    };
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is greater than the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareStringQuery createQueryForNodesWithFieldGreaterThan( String constraintValue,
+                                                                              String fieldName,
+                                                                              ValueFactories factories,
+                                                                              boolean caseSensitive ) {
+        return new CompareStringQuery(fieldName, constraintValue, factories.getStringFactory(), factories.getStringFactory(),
+                                      IS_GREATER_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is greater than or equal to
+     * the supplied constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareStringQuery createQueryForNodesWithFieldGreaterThanOrEqualTo( String constraintValue,
+                                                                                       String fieldName,
+                                                                                       ValueFactories factories,
+                                                                                       boolean caseSensitive ) {
+        return new CompareStringQuery(fieldName, constraintValue, factories.getStringFactory(), factories.getStringFactory(),
+                                      IS_GREATER_THAN_OR_EQUAL_TO, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is less than the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareStringQuery createQueryForNodesWithFieldLessThan( String constraintValue,
+                                                                           String fieldName,
+                                                                           ValueFactories factories,
+                                                                           boolean caseSensitive ) {
+        return new CompareStringQuery(fieldName, constraintValue, factories.getStringFactory(), factories.getStringFactory(),
+                                      IS_LESS_THAN, caseSensitive);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is less than or equal to the
+     * supplied constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static CompareStringQuery createQueryForNodesWithFieldLessThanOrEqualTo( String constraintValue,
+                                                                                    String fieldName,
+                                                                                    ValueFactories factories,
+                                                                                    boolean caseSensitive ) {
+        return new CompareStringQuery(fieldName, constraintValue, factories.getStringFactory(), factories.getStringFactory(),
+                                      IS_LESS_THAN_OR_EQUAL_TO, caseSensitive);
+    }
+
+    private final boolean caseSensitive;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param constraintValue the constraint value; may not be null
+     * @param valueFactory the value factory that can be used during the scoring; may not be null
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link CompareQuery.Evaluator} implementation that returns whether the node path satisfies the
+     *        constraint; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     */
+    protected CompareStringQuery( String fieldName,
+                                  String constraintValue,
+                                  ValueFactory<String> valueFactory,
+                                  ValueFactory<String> stringFactory,
+                                  Evaluator<String> evaluator,
+                                  boolean caseSensitive ) {
+        super(fieldName, caseSensitive ? constraintValue : constraintValue.toLowerCase(), valueFactory, stringFactory, evaluator);
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.query.CompareQuery#readFromDocument(org.apache.lucene.index.IndexReader, int)
+     */
+    @Override
+    protected String readFromDocument( IndexReader reader,
+                                       int docId ) throws IOException {
+        String result = super.readFromDocument(reader, docId);
+        return caseSensitive ? result : result.toLowerCase();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new CompareStringQuery(fieldName, constraintValue, valueTypeFactory, stringFactory, evaluator, caseSensitive);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/MatchNoneQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/MatchNoneQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/MatchNoneQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,216 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+
+/**
+ * A Lucene {@link Query} implementation that always matches no documents.
+ */
+public class MatchNoneQuery extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Construct a query that always returns no documents.
+     */
+    public MatchNoneQuery() {
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new NoneWeight();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new MatchNoneQuery();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return "NO DOCS";
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class NoneWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+
+        protected NoneWeight() {
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return MatchNoneQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) {
+            return new NoneScorer();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) {
+            return new Explanation(getValue(), "NO VALUES");
+        }
+    }
+
+    /**
+     * A scorer for the NOT query that iterates over documents (in increasing docId order), using the given scorer implementation
+     * for the operand of the NOT.
+     */
+    protected static class NoneScorer extends Scorer {
+        private int docId = -1;
+
+        protected NoneScorer() {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() {
+            docId = Scorer.NO_MORE_DOCS;
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) {
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/MatchNoneQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/NotQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/NotQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/NotQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,274 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+
+/**
+ * A Lucene {@link Query} implementation that is used to represent a NOT expression of another wrapped Query object. This query
+ * implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * that were <i>not</i> scored by the wrapped query. In other words, if the wrapped query ended up scoring any document, that
+ * document is <i>not</i> scored (i.e., skipped) by this query.
+ */
+public class NotQuery extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The operand that is being negated by this query.
+     */
+    protected final Query operand;
+
+    /**
+     * Construct a NOT(x) constraint where the 'x' operand is supplied.
+     * 
+     * @param operand the operand being negated
+     */
+    public NotQuery( Query operand ) {
+        this.operand = operand;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new NotWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new NotQuery(operand);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return "NOT(" + operand.toString(field) + ")";
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class NotWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+        private final Searcher searcher;
+
+        protected NotWeight( Searcher searcher ) {
+            this.searcher = searcher;
+            assert this.searcher != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return NotQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) throws IOException {
+            // Get the operand's score, and set this on the NOT query
+            Scorer operandScorer = operand.weight(searcher).scorer(reader, scoreDocsInOrder, topScorer);
+            // Return a custom scorer ...
+            return new NotScorer(operandScorer, reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) throws IOException {
+            Explanation operandExplanation = operand.weight(searcher).explain(reader, doc);
+            String desc = operandExplanation.getDescription();
+            return new Explanation(getValue(), "NOT(" + desc + ")");
+        }
+    }
+
+    /**
+     * A scorer for the NOT query that iterates over documents (in increasing docId order), using the given scorer implementation
+     * for the operand of the NOT.
+     */
+    protected static class NotScorer extends Scorer {
+        private int docId = -1;
+        private int nextScoredDocId = -1;
+        private final Scorer operandScorer;
+        private final IndexReader reader;
+        private final int pastMaxDocId;
+
+        /**
+         * @param operandScorer the scorer that is used to score the documents based upon the operand of the NOT; may not be null
+         * @param reader the reader that has access to all the docs ...
+         */
+        protected NotScorer( Scorer operandScorer,
+                             IndexReader reader ) {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+            this.operandScorer = operandScorer;
+            this.reader = reader;
+            assert this.operandScorer != null;
+            assert this.reader != null;
+            this.pastMaxDocId = this.reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() throws IOException {
+            if (nextScoredDocId == -1) {
+                // Find the first document that is scored by the operand's scorer ...
+                nextScoredDocId = operandScorer.nextDoc();
+            }
+            do {
+                ++docId;
+                if (docId == pastMaxDocId) {
+                    // We're aleady to the end of the documents in the index, so return no more docs
+                    return Scorer.NO_MORE_DOCS;
+                }
+                if (docId == nextScoredDocId) {
+                    // Find the next document that is scored by the operand's scorer ...
+                    nextScoredDocId = operandScorer.nextDoc();
+                    continue;
+                }
+                if (reader.isDeleted(docId)) {
+                    // We should skip this document ...
+                    continue;
+                }
+                return docId;
+            } while (true);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) throws IOException {
+            if (target == Scorer.NO_MORE_DOCS) return target;
+            while (true) {
+                int doc = nextDoc();
+                if (doc >= target) return doc;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/NotQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/ScoreQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/ScoreQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/ScoreQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,275 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link FullTextSearchScore} criteria a NOT expression of another
+ * wrapped Query object. This query implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * that were <i>not</i> scored by the wrapped query. In other words, if the wrapped query ended up scoring any document, that
+ * document is <i>not</i> scored (i.e., skipped) by this query.
+ */
+public class ScoreQuery extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The operand that is being negated by this query.
+     */
+    protected final Query operand;
+
+    /**
+     * Construct a NOT(x) constraint where the 'x' operand is supplied.
+     * 
+     * @param operand the operand being negated
+     */
+    public ScoreQuery( Query operand ) {
+        this.operand = operand;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new NotWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new ScoreQuery(operand);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return "NOT(" + operand.toString(field) + ")";
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class NotWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+        private final Searcher searcher;
+
+        protected NotWeight( Searcher searcher ) {
+            this.searcher = searcher;
+            assert this.searcher != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return ScoreQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) throws IOException {
+            // Get the operand's score, and set this on the NOT query
+            Scorer operandScorer = operand.weight(searcher).scorer(reader, scoreDocsInOrder, topScorer);
+            // Return a custom scorer ...
+            return new NotScorer(operandScorer, reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) throws IOException {
+            Explanation operandExplanation = operand.weight(searcher).explain(reader, doc);
+            String desc = operandExplanation.getDescription();
+            return new Explanation(getValue(), "NOT(" + desc + ")");
+        }
+    }
+
+    /**
+     * A scorer for the NOT query that iterates over documents (in increasing docId order), using the given scorer implementation
+     * for the operand of the NOT.
+     */
+    protected static class NotScorer extends Scorer {
+        private int docId = -1;
+        private int nextScoredDocId = -1;
+        private final Scorer operandScorer;
+        private final IndexReader reader;
+        private final int pastMaxDocId;
+
+        /**
+         * @param operandScorer the scorer that is used to score the documents based upon the operand of the NOT; may not be null
+         * @param reader the reader that has access to all the docs ...
+         */
+        protected NotScorer( Scorer operandScorer,
+                             IndexReader reader ) {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+            this.operandScorer = operandScorer;
+            this.reader = reader;
+            assert this.operandScorer != null;
+            assert this.reader != null;
+            this.pastMaxDocId = this.reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() throws IOException {
+            if (nextScoredDocId == -1) {
+                // Find the first document that is scored by the operand's scorer ...
+                nextScoredDocId = operandScorer.nextDoc();
+            }
+            do {
+                ++docId;
+                if (docId == pastMaxDocId) {
+                    // We're aleady to the end of the documents in the index, so return no more docs
+                    return Scorer.NO_MORE_DOCS;
+                }
+                if (docId == nextScoredDocId) {
+                    // Find the next document that is scored by the operand's scorer ...
+                    nextScoredDocId = operandScorer.nextDoc();
+                    continue;
+                }
+                if (reader.isDeleted(docId)) {
+                    // We should skip this document ...
+                    continue;
+                }
+                return docId;
+            } while (true);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) throws IOException {
+            if (target == Scorer.NO_MORE_DOCS) return target;
+            while (true) {
+                int doc = nextDoc();
+                if (doc >= target) return doc;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/ScoreQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,268 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.ValueFactory;
+
+/**
+ * A Lucene {@link Query} implementation that is used to score positively those documents that have a UUID in the supplied set.
+ * This works for large sets of UUIDs; in smaller numbers, it may be more efficient to create a boolean query that checks for each
+ * fo the UUIDs.
+ */
+public class UuidsQuery extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The operand that is being negated by this query.
+     */
+    protected final Set<UUID> uuids;
+    protected final FieldSelector fieldSelector;
+    protected final String fieldName;
+    protected final ValueFactory<UUID> uuidFactory;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param uuids the set of UUID values; may not be null
+     * @param uuidFactory the factory to create UUID values; may not be null
+     */
+    public UuidsQuery( String fieldName,
+                       Set<UUID> uuids,
+                       ValueFactory<UUID> uuidFactory ) {
+        this.fieldName = fieldName;
+        this.uuids = uuids;
+        this.uuidFactory = uuidFactory;
+        assert this.fieldName != null;
+        assert this.uuids != null;
+        assert this.uuidFactory != null;
+        this.fieldSelector = new FieldSelector() {
+            private static final long serialVersionUID = 1L;
+
+            public FieldSelectorResult accept( String fieldName ) {
+                return fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+            }
+        };
+    }
+
+    protected boolean includeDocument( IndexReader reader,
+                                       int docId ) throws IOException {
+        Document doc = reader.document(docId, fieldSelector);
+        String valueString = doc.get(fieldName);
+        return uuids.contains(uuidFactory.create(valueString));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new UuidSetWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return fieldName + " IN UUIDs";
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class UuidSetWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+        private final Searcher searcher;
+
+        protected UuidSetWeight( Searcher searcher ) {
+            this.searcher = searcher;
+            assert this.searcher != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return UuidsQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) {
+            // Return a custom scorer ...
+            return new UuidScorer(reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) {
+            return new Explanation(getValue(), getQuery().toString());
+        }
+    }
+
+    /**
+     * A scorer for the Path query.
+     */
+    protected class UuidScorer extends Scorer {
+        private int docId = -1;
+        private final int maxDocId;
+        private final IndexReader reader;
+
+        protected UuidScorer( IndexReader reader ) {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+            this.reader = reader;
+            assert this.reader != null;
+            this.maxDocId = this.reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() throws IOException {
+            do {
+                ++docId;
+                if (reader.isDeleted(docId)) {
+                    // We should skip this document ...
+                    continue;
+                }
+                if (includeDocument(reader, docId)) return docId;
+            } while (docId < maxDocId);
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) throws IOException {
+            if (target == Scorer.NO_MORE_DOCS) return target;
+            while (true) {
+                int doc = nextDoc();
+                if (doc >= target) return doc;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
===================================================================
--- trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties	2009-11-16 23:25:36 UTC (rev 1320)
@@ -30,8 +30,11 @@
 errorWhileRemovingContentAtPath = Error while removing the content at/below "{0}" in the "{1}" workspace of the "{2}" source: {3}
 errorWhileUpdatingContent = Error while updating content in the "{0}" workspace of the "{1}" source: {2}
 errorWhileCommittingIndexChanges = Error while committing changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorWhileRollingBackIndexChanges = Error while rolling back changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
 errorCreatingIndexWriter = Error attempting to create an index writer for the "{0}" index for the "{1}" workspace of the "{2}" source: {3}
 errorWhileOptimizingIndexes = Error while optimizing the indexes for the "{0}" workspace of the "{1}" source: {2}
 errorWhilePerformingSearch = Error while searching for "{0}" in the "{1}" workspace of the "{2}" source: {3}
 errorWhilePerformingQuery = Error while performing the query "{0}" against the content in the "{1}" workspace of the "{2}" source: {3}
-errorWhileInitializingSearchEngine = Error while initializing the search engine for the "{0}" workspace of the "{1}" source: {2}
\ No newline at end of file
+errorWhileInitializingSearchEngine = Error while initializing the search engine for the "{0}" workspace of the "{1}" source: {2}
+errorWhileRemovingIndexesForWorkspace = Error while removing the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorWhilePerformingLuceneQuery = Error while performing the Lucene query "{0}" as part of the "{1}" query against the "{2}" workspace of the "{3}" source: {4}
\ No newline at end of file

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -25,16 +25,32 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
-import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import java.util.List;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.connector.RepositoryConnection;
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Query;
+import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.search.IndexLayout;
+import org.jboss.dna.search.IndexSession;
+import org.jboss.dna.search.SearchEngine;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -47,7 +63,10 @@
     private String workspaceName2;
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
-    private DirectoryConfiguration directoryFactory;
+    private IndexLayout layout;
+    private IndexSession sessionWs1;
+    private IndexSession sessionWs2;
+    private IndexSession sessionDefault;
     private Graph content;
 
     @Before
@@ -66,12 +85,6 @@
         content.createWorkspace().named(workspaceName1);
         content.createWorkspace().named(workspaceName2);
 
-        // Load some content ...
-        content.useWorkspace(workspaceName1);
-        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
-        content.useWorkspace(workspaceName2);
-        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
-
         // Set up the connection factory ...
         connectionFactory = new RepositoryConnectionFactory() {
             @SuppressWarnings( "synthetic-access" )
@@ -80,67 +93,261 @@
             }
         };
 
+        // Set up the index layout ...
+        layout = mock(IndexLayout.class);
+        sessionWs1 = mockSession(layout, workspaceName1);
+        sessionWs2 = mockSession(layout, workspaceName2);
+        sessionDefault = mockSession(layout, "");
+
         // Now set up the search engine ...
-        directoryFactory = DirectoryConfigurations.inMemory();
-        engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory);
+        engine = new SearchEngine(context, sourceName, connectionFactory, layout);
     }
 
-    protected Path path( String string ) {
-        return context.getValueFactories().getPathFactory().create(string);
+    protected IndexSession mockSession( IndexLayout mockLayout,
+                                        String workspaceName ) {
+        IndexSession session = mock(IndexSession.class);
+        stub(layout.createSession(context, sourceName, workspaceName, false, false)).toReturn(session);
+        stub(layout.createSession(context, sourceName, workspaceName, false, true)).toReturn(session);
+        stub(layout.createSession(context, sourceName, workspaceName, true, false)).toReturn(session);
+        stub(layout.createSession(context, sourceName, workspaceName, true, true)).toReturn(session);
+        stub(session.getWorkspaceName()).toReturn(workspaceName);
+        stub(session.getSourceName()).toReturn(sourceName);
+        return session;
     }
 
-    @Test
-    public void shouldHaveLoadedTestContentIntoRepositorySource() {
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected void loadContent() throws Exception {
+        // Load some content ...
         content.useWorkspace(workspaceName1);
-        assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
         content.useWorkspace(workspaceName2);
-        assertThat(content.getNodeAt("/Aircraft/Commercial/Boeing 787").getProperty("range").getFirstValue(),
-                   is((Object)"3050nm"));
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
     }
 
     @Test
-    public void shouldHaveExecutionContext() {
-        assertThat(engine.getContext(), is(sameInstance(context)));
+    public void shouldReturnSearchWorkspaceForExistingWorkspaceInSource() {
+        SearchEngine.Workspace workspace = engine.getWorkspace(workspaceName1);
+        assertThat(workspace, is(notNullValue()));
+        assertThat(workspace.modifiedNodesSinceLastOptimize.get(), is(0));
+        assertThat(workspace.getWorkspaceName(), is(workspaceName1));
     }
 
+    @Test( expected = InvalidWorkspaceException.class )
+    public void shouldFailToReturnSearchWorkspaceForNonExistantWorkspaceInSource() {
+        engine.getWorkspace(workspaceName1 + "foobar");
+    }
+
     @Test
-    public void shouldHaveSourceName() {
-        assertThat(engine.getSourceName(), is(sourceName));
+    public void shouldDoNothingDuringRemoveWorkspaceIfWorkspaceHasNotBeenLoaded() throws Exception {
+        engine.removeWorkspace(workspaceName1);
+        verifyZeroInteractions(layout);
     }
 
     @Test
-    public void shouldFindExistingWorkspaces() {
-        assertThat(engine.getWorkspaceEngine(workspaceName1), is(notNullValue()));
-        assertThat(engine.getWorkspaceEngine(workspaceName2), is(notNullValue()));
+    public void shouldForwardRemoveWorkspaceToIndexLayout() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.removeWorkspace(workspaceName1);
+        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
+        verifyNoMoreInteractions(layout);
     }
 
-    @Test( expected = InvalidWorkspaceException.class )
-    public void shouldNotFindNonExistingWorkspaces() {
-        engine.getWorkspaceEngine("Non-existant workspace");
+    @Test
+    public void shouldForwardRemoveWorkspaceToIndexLayoutForEachWorkspaceThatWasLoaded() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.removeWorkspaces();
+        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
+        verifyZeroInteractions(layout);
     }
 
     @Test
-    public void shouldIndexAllContentInRepositorySource() {
-        engine.indexContent(3);
+    public void shouldForwardRemoveWorkspaceToIndexLayoutForAllWorkspacesThatWereLoaded() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.getWorkspace(workspaceName2);
+        engine.removeWorkspaces();
+        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
+        verify(layout).destroyIndexes(context, sourceName, workspaceName2);
+        verifyNoMoreInteractions(layout);
     }
 
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailIfNullWorkspaceNamePassedToRemoveWorkspace() throws Exception {
+        engine.removeWorkspace(null);
+    }
+
     @Test
-    public void shouldIndexAllContentInWorkspace() {
-        engine.indexContent(workspaceName1, 3);
-        engine.indexContent(workspaceName2, 5);
+    public void shouldForwardOptimizeOfWorkspaceToIndexSession() throws Exception {
+        engine.optimize(workspaceName1);
+        verify(sessionWs1).optimize();
+        verify(sessionWs1).commit();
+        verifyNoMoreInteractions(sessionWs1);
     }
 
     @Test
-    public void shouldIndexAllContentInWorkspaceBelowPath() {
-        engine.indexContent(workspaceName1, path("/Cars/Hybrid"), 3);
-        engine.indexContent(workspaceName2, path("/Aircraft/Commercial"), 5);
+    public void shouldForwardOptimizeOfAllWorkspacesToEachIndexSession() throws Exception {
+        engine.optimize(); // will find all three workspaces
+        verify(sessionWs1).optimize();
+        verify(sessionWs1).commit();
+        verifyNoMoreInteractions(sessionWs1);
+        verify(sessionWs2).optimize();
+        verify(sessionWs2).commit();
+        verifyNoMoreInteractions(sessionWs2);
+        verify(sessionDefault).optimize();
+        verify(sessionDefault).commit();
+        verifyNoMoreInteractions(sessionDefault);
     }
 
     @Test
-    public void shouldReIndexAllContentInWorkspaceBelowPath() {
-        for (int i = 0; i != 0; i++) {
-            engine.indexContent(workspaceName1, path("/Cars/Hybrid"), 3);
-            engine.indexContent(workspaceName2, path("/Aircraft/Commercial"), 5);
-        }
+    public void shouldForwardIndexOfWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, 3);
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
     }
+
+    @Test
+    public void shouldForwardIndexOfSubgraphInWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, path("/Cars"), 3);
+        verify(sessionWs1).deleteBelow(path("/Cars"));
+        verify(sessionWs1, times(17)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardIndexEntireWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, path("/"), 3);
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardIndexOfAllWorkspacesToEachIndexSession() throws Exception {
+        loadContent();
+        engine.index(3); // will find all three workspaces
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+        verify(sessionWs2, times(24)).index((Node)anyObject());
+        verify(sessionWs2).commit();
+        verify(sessionDefault, times(1)).index((Node)anyObject());
+        verify(sessionDefault).commit();
+    }
+
+    @SuppressWarnings( "unchecked" )
+    @Test
+    public void shouldForwardSearchToIndexSession() throws Exception {
+        String query = "term1 term2";
+        engine.fullTextSearch(context, workspaceName1, query, 3, 0);
+        verify(sessionWs1).search(eq(context), eq(query), eq(3), eq(0), (List<Location>)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardQueryToIndexSession() throws Exception {
+        Query query = mock(Query.class);
+        Schemata schemata = mock(Schemata.class);
+        engine.query(context, workspaceName1, query, schemata);
+        verify(sessionWs1).query(eq(new QueryContext(context, schemata)), eq(query));
+        verify(sessionWs1).commit();
+    }
+
+    // These tests expect there to be some real IndexLayout ...
+
+    // @Test
+    // public void shouldIndexAllContentInRepositorySource() throws Exception {
+    // loadContent();
+    // engine.index(3);
+    // }
+    //
+    // @Test
+    // public void shouldIndexAllContentInWorkspace() throws Exception {
+    // loadContent();
+    // engine.index(workspaceName1, 3);
+    // engine.index(workspaceName2, 5);
+    // }
+    //
+    // @Test
+    // public void shouldIndexAllContentInWorkspaceBelowPath() throws Exception {
+    // loadContent();
+    // engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
+    // engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
+    // }
+    //
+    // @Test
+    // public void shouldReIndexAllContentInWorkspaceBelowPath() throws Exception {
+    // loadContent();
+    // for (int i = 0; i != 0; i++) {
+    // engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
+    // engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
+    // }
+    // }
+    //
+    // @Test
+    // public void shouldHaveLoadedTestContentIntoRepositorySource() {
+    // assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfOne() {
+    // engine.index(workspaceName1, path("/"), 1);
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTwo() {
+    // engine.index(workspaceName1, path("/"), 2);
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfThree() {
+    // engine.index(workspaceName1, path("/"), 3);
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfFour() {
+    // engine.index(workspaceName1, path("/"), 4);
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTen() {
+    // engine.index(workspaceName1, path("/"), 10);
+    // }
+    //
+    // @Test
+    // public void shouldIndexRepositoryContentStartingAtNonRootNode() {
+    // engine.index(workspaceName1, path("/Cars"), 10);
+    // }
+    //
+    // @Test
+    // public void shouldReIndexRepositoryContentStartingAtNonRootNode() {
+    // for (int i = 0; i != 3; ++i) {
+    // engine.index(workspaceName1, path("/Cars"), 10);
+    // }
+    // }
+    //
+    // @Test
+    // public void shouldFindNodesByFullTextSearch() {
+    // engine.index(workspaceName1, path("/"), 100);
+    // List<Location> results = engine.fullTextSearch(context, workspaceName1, "Toyota Prius", 10, 0);
+    // assertThat(results, is(notNullValue()));
+    // assertThat(results.size(), is(2));
+    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+    // assertThat(results.get(1).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+    // }
+    //
+    // @Test
+    // public void shouldFindNodesByFullTextSearchWithOffset() {
+    // engine.index(workspaceName1, path("/"), 100);
+    // List<Location> results = engine.fullTextSearch(context, workspaceName1, "toyota prius", 1, 0);
+    // assertThat(results, is(notNullValue()));
+    // assertThat(results.size(), is(1));
+    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+    //
+    // results = engine.fullTextSearch(context, workspaceName1, "+Toyota", 1, 1);
+    // assertThat(results, is(notNullValue()));
+    // assertThat(results.size(), is(1));
+    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+    // }
 }

Deleted: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -1,171 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import java.util.List;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.property.Path;
-import org.junit.Before;
-import org.junit.Test;
-
-public class WorkspaceSearchEngineTest {
-
-    private WorkspaceSearchEngine engine;
-    private ExecutionContext context;
-    private String sourceName;
-    private String workspaceName;
-    private InMemoryRepositorySource source;
-    private RepositoryConnectionFactory connectionFactory;
-    private DirectoryConfiguration directoryFactory;
-    private IndexStrategy indexingStrategy;
-    private Graph content;
-
-    @Before
-    public void beforeEach() throws Exception {
-        context = new ExecutionContext();
-        sourceName = "sourceA";
-        workspaceName = "workspace1";
-
-        // Set up the source and graph instance ...
-        source = new InMemoryRepositorySource();
-        source.setName(sourceName);
-        source.setDefaultWorkspaceName(workspaceName);
-        content = Graph.create(source, context);
-
-        // Load some content ...
-        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
-
-        // Set up the connection factory ...
-        connectionFactory = new RepositoryConnectionFactory() {
-            @SuppressWarnings( "synthetic-access" )
-            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
-                return source.getConnection();
-            }
-        };
-
-        // Set up the indexing strategy ...
-        IndexRules rules = IndexRules.createBuilder(KitchenSinkIndexStrategy.DEFAULT_RULES)
-                                           .defaultTo(IndexRules.INDEX | IndexRules.ANALYZE | IndexRules.FULL_TEXT)
-                                           .build();
-        indexingStrategy = new KitchenSinkIndexStrategy(rules);
-
-        // Now set up the search engine ...
-        directoryFactory = DirectoryConfigurations.inMemory();
-        engine = new WorkspaceSearchEngine(context, directoryFactory, indexingStrategy, sourceName, workspaceName,
-                                           connectionFactory);
-    }
-
-    protected Path path( String string ) {
-        return context.getValueFactories().getPathFactory().create(string);
-    }
-
-    protected void assertSearchResults( String fullTextSearch,
-                                        Path... expectedPaths ) {
-        int numExpected = expectedPaths.length;
-        List<Location> results = engine.fullTextSearch(fullTextSearch, numExpected, 0);
-        int numFound = results.size();
-        assertThat("Different number of results were found", numExpected, is(numFound));
-        Path[] actualPaths = new Path[numFound];
-        int i = 0;
-        for (Location actual : results) {
-            actualPaths[i++] = actual.getPath();
-        }
-        assertThat(expectedPaths, is(actualPaths));
-    }
-
-    @Test
-    public void shouldHaveLoadedTestContentIntoRepositorySource() {
-        assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfOne() {
-        engine.indexContent(path("/"), 1);
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTwo() {
-        engine.indexContent(path("/"), 2);
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfThree() {
-        engine.indexContent(path("/"), 3);
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfFour() {
-        engine.indexContent(path("/"), 4);
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTen() {
-        engine.indexContent(path("/"), 10);
-    }
-
-    @Test
-    public void shouldIndexRepositoryContentStartingAtNonRootNode() {
-        engine.indexContent(path("/Cars"), 10);
-    }
-
-    @Test
-    public void shouldReIndexRepositoryContentStartingAtNonRootNode() {
-        for (int i = 0; i != 3; ++i) {
-            engine.indexContent(path("/Cars"), 10);
-        }
-    }
-
-    @Test
-    public void shouldFindNodesByFullTextSearch() {
-        engine.indexContent(path("/"), 100);
-        List<Location> results = engine.fullTextSearch("Toyota Prius", 10, 0);
-        assertThat(results, is(notNullValue()));
-        assertThat(results.size(), is(2));
-        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
-        assertThat(results.get(1).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
-    }
-
-    @Test
-    public void shouldFindNodesByFullTextSearchWithOffset() {
-        engine.indexContent(path("/"), 100);
-        List<Location> results = engine.fullTextSearch("toyota prius", 1, 0);
-        assertThat(results, is(notNullValue()));
-        assertThat(results.size(), is(1));
-        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
-
-        results = engine.fullTextSearch("+Toyota", 1, 1);
-        assertThat(results, is(notNullValue()));
-        assertThat(results.size(), is(1));
-        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
-    }
-}

Added: trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java	                        (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java	2009-11-16 23:25:36 UTC (rev 1320)
@@ -0,0 +1,126 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Similarity;
+import org.jboss.dna.search.query.NotQuery;
+import org.junit.Test;
+
+public class LuceneNotQueryTest {
+
+    @Test
+    public void scorerShouldSkipAdjacentDocsIfScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(0, 1, 2, 3, 4);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 5, 6, 7, 8, 9);
+    }
+
+    @Test
+    public void scorerShouldSkipDocsAtEndIfScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(8, 9);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 2, 3, 4, 5, 6, 7);
+    }
+
+    @Test
+    public void scorerShouldScoreFirstDocsIfNotScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(2, 3, 4);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 5, 6, 7, 8, 9);
+    }
+
+    @Test
+    public void scorerShouldScoreNonAdjacentDocsNotScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(2, 4, 8);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 3, 5, 6, 7, 9);
+    }
+
+    protected void assertScores( Scorer scorer,
+                                 int... docIds ) throws IOException {
+        for (int docId : docIds) {
+            assertThat(scorer.nextDoc(), is(docId));
+            assertThat(scorer.score(), is(1.0f));
+        }
+        assertThat(scorer.nextDoc(), is(Scorer.NO_MORE_DOCS));
+    }
+
+    protected static class MockScorer extends Scorer {
+        private final Iterator<Integer> docIds;
+
+        protected MockScorer( int... docIds ) {
+            super(Similarity.getDefault());
+            List<Integer> ids = new ArrayList<Integer>();
+            for (int docId : docIds) {
+                ids.add(new Integer(docId));
+            }
+            this.docIds = ids.iterator();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() {
+            if (docIds.hasNext()) return docIds.next();
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            throw new UnsupportedOperationException("Should not be called");
+        }
+    }
+
+}


Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Mon Nov 16 18:57:05 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Nov 2009 18:57:05 -0500
Subject: [dna-commits] DNA SVN:  r1321 - trunk/dna-search.
Message-ID: <200911162357.nAGNv5SI028045@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-16 18:57:04 -0500 (Mon, 16 Nov 2009)
New Revision: 1321

Modified:
   trunk/dna-search/pom.xml
Log:
DNA-467 Removed dependency that was accidentally added/committed

Modified: trunk/dna-search/pom.xml
===================================================================
--- trunk/dna-search/pom.xml	2009-11-16 23:25:36 UTC (rev 1320)
+++ trunk/dna-search/pom.xml	2009-11-16 23:57:04 UTC (rev 1321)
@@ -91,20 +91,6 @@
       <artifactId>log4j</artifactId>
       <scope>test</scope>
     </dependency>
-    
-    
-    
-    
-    
-    <dependency>
-      <groupId>org.apache.jackrabbit</groupId>
-      <artifactId>jackrabbit-core</artifactId>
-      <version>2.0-beta1</version>
-    </dependency>
-
-
-
-
     <!-- 
     Java Concurrency in Practice annotations
     -->


From dna-commits at lists.jboss.org  Mon Nov 16 18:57:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Nov 2009 18:57:21 -0500
Subject: [dna-commits] DNA SVN: r1322 -
	trunk/dna-search/src/main/java/org/jboss/dna/search.
Message-ID: <200911162357.nAGNvLPt028087@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-16 18:57:21 -0500 (Mon, 16 Nov 2009)
New Revision: 1322

Modified:
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
Log:
DNA-467 Removed fields that are no longer used

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-16 23:57:04 UTC (rev 1321)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-16 23:57:21 UTC (rev 1322)
@@ -179,7 +179,7 @@
                                                              QueryContext context,
                                                              PlanNode accessNode,
                                                              Columns resultColumns,
-                                                             Analyzer analyzer ) throws IOException {
+                                                             Analyzer analyzer ) {
             // Create a processing component for this access query ...
             return new LuceneQueryComponent(this, originalQuery, context, resultColumns, accessNode, analyzer, sourceName,
                                             workspaceName);
@@ -193,8 +193,6 @@
     protected static class LuceneQueryComponent extends AbstractAccessComponent {
         private final QueryCommand originalQuery;
         private final Session session;
-        private final IndexReader pathIndexReader;
-        private final Analyzer analyzer;
         private final String sourceName;
         private final String workspaceName;
 
@@ -205,12 +203,10 @@
                                         PlanNode accessNode,
                                         Analyzer analyzer,
                                         String sourceName,
-                                        String workspaceName ) throws IOException {
+                                        String workspaceName ) {
             super(context, columns, accessNode);
             this.originalQuery = originalQuery;
-            this.analyzer = analyzer;
             this.session = session;
-            this.pathIndexReader = session.getPathsReader();
             this.sourceName = sourceName;
             this.workspaceName = workspaceName;
         }


From dna-commits at lists.jboss.org  Tue Nov 17 13:46:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Nov 2009 13:46:21 -0500
Subject: [dna-commits] DNA SVN: r1323 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/model and 5
	other directories.
Message-ID: <200911171846.nAHIkLgo019691@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-17 13:46:21 -0500 (Tue, 17 Nov 2009)
New Revision: 1323

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
Log:
DNA-552 Added to the abstract query model a new Constraint subclass 'Between' that represents a constraint on a DynamicOperand such that the values are within a certain range.  Also added the corresponding support to the SQL parser for 'BETWEEN x AND y' and 'NOT BETWEEN x AND y', which is the syntax commonly used in other SQL grammars.  (Note that this is an extension beyond JCRSQL2.)  Also added support for 'BETWEEN x EXCLUSIVE AND y' and 'BETWEEN x AND y EXCLUSIVE' to be able to specify that the ranges do or do not include the boundary value.  This feature allows the Lucene search/query implementation to apply a more efficient query to the indexes than does an AND of two Comparison constraints.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -44,6 +44,7 @@
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.BindVariableName;
 import org.jboss.dna.graph.query.model.ChildNode;
 import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
@@ -1290,36 +1291,27 @@
         }
     }
 
-    public class CastAs {
-        private final RightHandSide rhs;
-        private final Object value;
+    public abstract class CastAs<ReturnType> {
+        protected final Object value;
 
-        protected CastAs( RightHandSide rhs,
-                          Object value ) {
-            this.rhs = rhs;
+        protected CastAs( Object value ) {
             this.value = value;
         }
 
-        private ValueFactories factories() {
-            return QueryBuilder.this.context.getValueFactories();
-        }
-
         /**
          * Define the right-hand side literal value cast as the specified type.
          * 
          * @param type the property type; may not be null
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder as( PropertyType type ) {
-            return rhs.comparisonBuilder.is(rhs.operator, factories().getValueFactory(type).create(value));
-        }
+        public abstract ReturnType as( PropertyType type );
 
         /**
          * Define the right-hand side literal value cast as a {@link PropertyType#STRING}.
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asString() {
+        public ReturnType asString() {
             return as(PropertyType.STRING);
         }
 
@@ -1328,7 +1320,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asBoolean() {
+        public ReturnType asBoolean() {
             return as(PropertyType.BOOLEAN);
         }
 
@@ -1337,7 +1329,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asLong() {
+        public ReturnType asLong() {
             return as(PropertyType.LONG);
         }
 
@@ -1346,7 +1338,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asDouble() {
+        public ReturnType asDouble() {
             return as(PropertyType.DOUBLE);
         }
 
@@ -1355,7 +1347,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asDecimal() {
+        public ReturnType asDecimal() {
             return as(PropertyType.DECIMAL);
         }
 
@@ -1364,7 +1356,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asDate() {
+        public ReturnType asDate() {
             return as(PropertyType.DATE);
         }
 
@@ -1373,7 +1365,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asName() {
+        public ReturnType asName() {
             return as(PropertyType.NAME);
         }
 
@@ -1382,7 +1374,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asPath() {
+        public ReturnType asPath() {
             return as(PropertyType.PATH);
         }
 
@@ -1391,7 +1383,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asBinary() {
+        public ReturnType asBinary() {
             return as(PropertyType.BINARY);
         }
 
@@ -1400,7 +1392,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asReference() {
+        public ReturnType asReference() {
             return as(PropertyType.REFERENCE);
         }
 
@@ -1409,7 +1401,7 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asUri() {
+        public ReturnType asUri() {
             return as(PropertyType.URI);
         }
 
@@ -1418,11 +1410,88 @@
          * 
          * @return the constraint builder; never null
          */
-        public ConstraintBuilder asUuid() {
+        public ReturnType asUuid() {
             return as(PropertyType.UUID);
         }
     }
 
+    public class CastAsRightHandSide extends CastAs<ConstraintBuilder> {
+        private final RightHandSide rhs;
+
+        protected CastAsRightHandSide( RightHandSide rhs,
+                                       Object value ) {
+            super(value);
+            this.rhs = rhs;
+        }
+
+        private ValueFactories factories() {
+            return QueryBuilder.this.context.getValueFactories();
+        }
+
+        /**
+         * Define the right-hand side literal value cast as the specified type.
+         * 
+         * @param type the property type; may not be null
+         * @return the constraint builder; never null
+         */
+        @Override
+        public ConstraintBuilder as( PropertyType type ) {
+            return rhs.comparisonBuilder.is(rhs.operator, factories().getValueFactory(type).create(value));
+        }
+    }
+
+    public class CastAsUpperBoundary extends CastAs<ConstraintBuilder> {
+        private final UpperBoundary upperBoundary;
+
+        protected CastAsUpperBoundary( UpperBoundary upperBoundary,
+                                       Object value ) {
+            super(value);
+            this.upperBoundary = upperBoundary;
+        }
+
+        private ValueFactories factories() {
+            return QueryBuilder.this.context.getValueFactories();
+        }
+
+        /**
+         * Define the right-hand side literal value cast as the specified type.
+         * 
+         * @param type the property type; may not be null
+         * @return the constraint builder; never null
+         */
+        @Override
+        public ConstraintBuilder as( PropertyType type ) {
+            return upperBoundary.comparisonBuilder.isBetween(upperBoundary.lowerBound, factories().getValueFactory(type)
+                                                                                                  .create(value));
+        }
+    }
+
+    public class CastAsLowerBoundary extends CastAs<AndBuilder<UpperBoundary>> {
+        private final ComparisonBuilder builder;
+
+        protected CastAsLowerBoundary( ComparisonBuilder builder,
+                                       Object value ) {
+            super(value);
+            this.builder = builder;
+        }
+
+        private ValueFactories factories() {
+            return QueryBuilder.this.context.getValueFactories();
+        }
+
+        /**
+         * Define the left-hand side literal value cast as the specified type.
+         * 
+         * @param type the property type; may not be null
+         * @return the builder to complete the constraint; never null
+         */
+        @Override
+        public AndBuilder<UpperBoundary> as( PropertyType type ) {
+            Object literal = factories().getValueFactory(type).create(value);
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(builder, new Literal(literal)));
+        }
+    }
+
     public class RightHandSide {
         protected final Operator operator;
         protected final ComparisonBuilder comparisonBuilder;
@@ -1579,8 +1648,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( int literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( int literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1589,8 +1658,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( String literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( String literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1599,8 +1668,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( boolean literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( boolean literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1609,8 +1678,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( long literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( long literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1619,8 +1688,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( double literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( double literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1629,8 +1698,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( BigDecimal literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1639,8 +1708,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( DateTime literal ) {
-            return new CastAs(this, literal.toUtcTimeZone());
+        public CastAs<ConstraintBuilder> cast( DateTime literal ) {
+            return new CastAsRightHandSide(this, literal.toUtcTimeZone());
         }
 
         /**
@@ -1649,8 +1718,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( Name literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( Name literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1659,8 +1728,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( Path literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( Path literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1669,8 +1738,8 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( UUID literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( UUID literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
 
         /**
@@ -1679,17 +1748,537 @@
          * @param literal the literal value that is to be cast
          * @return the constraint builder; never null
          */
-        public CastAs cast( URI literal ) {
-            return new CastAs(this, literal);
+        public CastAs<ConstraintBuilder> cast( URI literal ) {
+            return new CastAsRightHandSide(this, literal);
         }
     }
 
+    public class UpperBoundary {
+        protected final StaticOperand lowerBound;
+        protected final ComparisonBuilder comparisonBuilder;
+
+        protected UpperBoundary( ComparisonBuilder comparisonBuilder,
+                                 StaticOperand lowerBound ) {
+            this.lowerBound = lowerBound;
+            this.comparisonBuilder = comparisonBuilder;
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( String literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( int literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( long literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( float literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( double literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( DateTime literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Path literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Name literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( URI literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( UUID literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Binary literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( BigDecimal literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( boolean literal ) {
+            return comparisonBuilder.isBetween(lowerBound, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param variableName the name of the variable
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder variable( String variableName ) {
+            return comparisonBuilder.constraintBuilder.setConstraint(new Between(comparisonBuilder.left, lowerBound,
+                                                                                 new BindVariableName(variableName)));
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( int literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( String literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( boolean literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( long literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( double literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( DateTime literal ) {
+            return new CastAsUpperBoundary(this, literal.toUtcTimeZone());
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( Name literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( Path literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( UUID literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+
+        /**
+         * Define the upper boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<ConstraintBuilder> cast( URI literal ) {
+            return new CastAsUpperBoundary(this, literal);
+        }
+    }
+
+    public class LowerBoundary {
+        protected final ComparisonBuilder comparisonBuilder;
+
+        protected LowerBoundary( ComparisonBuilder comparisonBuilder ) {
+            this.comparisonBuilder = comparisonBuilder;
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( String literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( int literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( long literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( float literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( double literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( DateTime literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( Path literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( Name literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( URI literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( UUID literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( Binary literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( BigDecimal literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> literal( boolean literal ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param variableName the name of the variable
+         * @return the constraint builder; never null
+         */
+        public AndBuilder<UpperBoundary> variable( String variableName ) {
+            return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new BindVariableName(variableName)));
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( int literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( String literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( boolean literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( long literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( double literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( BigDecimal literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( DateTime literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( Name literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( Path literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( UUID literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+
+        /**
+         * Define the lower boundary value of a range.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs<AndBuilder<UpperBoundary>> cast( URI literal ) {
+            return new CastAsLowerBoundary(comparisonBuilder, literal);
+        }
+    }
+
     /**
      * An interface used to set the right-hand side of a constraint.
      */
     public class ComparisonBuilder {
-        private final DynamicOperand left;
-        private final ConstraintBuilder constraintBuilder;
+        protected final DynamicOperand left;
+        protected final ConstraintBuilder constraintBuilder;
 
         protected ComparisonBuilder( ConstraintBuilder constraintBuilder,
                                      DynamicOperand left ) {
@@ -1815,10 +2404,28 @@
         public ConstraintBuilder is( Operator operator,
                                      Object literal ) {
             assert operator != null;
-            return this.constraintBuilder.setConstraint(new Comparison(left, operator, new Literal(literal)));
+            Literal value = literal instanceof Literal ? (Literal)literal : new Literal(literal);
+            return this.constraintBuilder.setConstraint(new Comparison(left, operator, value));
         }
 
         /**
+         * Define the right-hand-side of the constraint using the supplied operator.
+         * 
+         * @param lowerBoundLiteral the literal value that represents the lower bound of the range (inclusive)
+         * @param upperBoundLiteral the literal value that represents the upper bound of the range (inclusive)
+         * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
+         *         complete already-started clauses; never null
+         */
+        public ConstraintBuilder isBetween( Object lowerBoundLiteral,
+                                            Object upperBoundLiteral ) {
+            assert lowerBoundLiteral != null;
+            assert upperBoundLiteral != null;
+            Literal lower = lowerBoundLiteral instanceof Literal ? (Literal)lowerBoundLiteral : new Literal(lowerBoundLiteral);
+            Literal upper = upperBoundLiteral instanceof Literal ? (Literal)upperBoundLiteral : new Literal(upperBoundLiteral);
+            return this.constraintBuilder.setConstraint(new Between(left, lower, upper));
+        }
+
+        /**
          * Define the right-hand-side of the constraint to be equivalent to the value of the supplied variable.
          * 
          * @param variableName the name of the variable
@@ -1971,5 +2578,33 @@
         public ConstraintBuilder isNotEqualTo( Object literal ) {
             return is(Operator.NOT_EQUAL_TO, literal);
         }
+
+        /**
+         * Define the constraint as a range between a lower boundary and an upper boundary.
+         * 
+         * @return the interface used to specify the lower boundary boundary, the upper boundary, and which will return the
+         *         builder interface; never null
+         */
+        public LowerBoundary isBetween() {
+            return new LowerBoundary(this);
+        }
     }
+
+    public class AndBuilder<T> {
+        private final T object;
+
+        protected AndBuilder( T object ) {
+            assert object != null;
+            this.object = object;
+        }
+
+        /**
+         * Return the component
+         * 
+         * @return the component; never null
+         */
+        public T and() {
+            return this.object;
+        }
+    }
 }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -0,0 +1,174 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+
+/**
+ * A constraint that evaluates to true when the value defined by the dynamic operand evaluates to be within the specified range.
+ */
+ at Immutable
+public class Between extends Constraint {
+
+    private final DynamicOperand operand;
+    private final StaticOperand lowerBound;
+    private final StaticOperand upperBound;
+    private final boolean includeLowerBound;
+    private final boolean includeUpperBound;
+    private final int hc;
+
+    /**
+     * Create a constraint that the values of the supplied dynamic operand are between the lower and upper bounds (inclusive).
+     * 
+     * @param operand the dynamic operand describing the values that are to be constrained
+     * @param lowerBound the lower bound of the range
+     * @param upperBound the upper bound of the range
+     * @throws IllegalArgumentException if any of the arguments are null
+     */
+    public Between( DynamicOperand operand,
+                    StaticOperand lowerBound,
+                    StaticOperand upperBound ) {
+        this(operand, lowerBound, upperBound, true, true);
+    }
+
+    /**
+     * Create a constraint that the values of the supplied dynamic operand are between the lower and upper bounds, specifying
+     * whether the boundary values are to be included in the range.
+     * 
+     * @param operand the dynamic operand describing the values that are to be constrained
+     * @param lowerBound the lower bound of the range
+     * @param upperBound the upper bound of the range
+     * @param includeLowerBound true if the lower boundary value is not be included
+     * @param includeUpperBound true if the upper boundary value is not be included
+     * @throws IllegalArgumentException if any of the arguments are null
+     */
+    public Between( DynamicOperand operand,
+                    StaticOperand lowerBound,
+                    StaticOperand upperBound,
+                    boolean includeLowerBound,
+                    boolean includeUpperBound ) {
+        CheckArg.isNotNull(operand, "operand");
+        CheckArg.isNotNull(lowerBound, "lowerBound");
+        CheckArg.isNotNull(upperBound, "upperBound");
+        this.operand = operand;
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+        this.includeLowerBound = includeLowerBound;
+        this.includeUpperBound = includeUpperBound;
+        this.hc = HashCode.compute(this.operand, this.lowerBound, this.upperBound);
+    }
+
+    /**
+     * Get the dynamic operand specification.
+     * 
+     * @return the dynamic operand; never null
+     */
+    public final DynamicOperand getOperand() {
+        return operand;
+    }
+
+    /**
+     * @return lowerBound
+     */
+    public StaticOperand getLowerBound() {
+        return lowerBound;
+    }
+
+    /**
+     * @return upperBound
+     */
+    public StaticOperand getUpperBound() {
+        return upperBound;
+    }
+
+    /**
+     * Return whether the lower bound is to be included in the results.
+     * 
+     * @return true if the {@link #getLowerBound() lower bound} is to be included, or false otherwise
+     */
+    public boolean isLowerBoundIncluded() {
+        return includeLowerBound;
+    }
+
+    /**
+     * Return whether the upper bound is to be included in the results.
+     * 
+     * @return true if the {@link #getUpperBound() upper bound} is to be included, or false otherwise
+     */
+    public boolean isUpperBoundIncluded() {
+        return includeUpperBound;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return Visitors.readable(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof Between) {
+            Between that = (Between)obj;
+            if (this.hc != that.hc) return false;
+            if (!this.operand.equals(that.operand)) return false;
+            if (!this.lowerBound.equals(that.lowerBound)) return false;
+            if (!this.upperBound.equals(that.upperBound)) return false;
+            if (this.includeLowerBound != that.includeLowerBound) return false;
+            if (this.includeUpperBound != that.includeUpperBound) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitable#accept(org.jboss.dna.graph.query.model.Visitor)
+     */
+    public void accept( Visitor visitor ) {
+        visitor.visit(this);
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -32,6 +32,8 @@
 
     void visit( And obj );
 
+    void visit( Between obj );
+
     void visit( BindVariableName obj );
 
     void visit( ChildNode obj );

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -239,6 +239,14 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Between)
+         */
+        public void visit( Between obj ) {
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
          */
         public void visit( BindVariableName obj ) {
@@ -573,6 +581,19 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Between)
+         */
+        public void visit( Between between ) {
+            strategy.visit(between);
+            enqueue(between.getOperand());
+            enqueue(between.getLowerBound());
+            enqueue(between.getUpperBound());
+            visitNext();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
          */
         public void visit( BindVariableName variableName ) {
@@ -1004,6 +1025,21 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Between)
+         */
+        public void visit( Between between ) {
+            between.getOperand().accept(this);
+            append(" BETWEEN ");
+            between.getLowerBound().accept(this);
+            if (!between.isLowerBoundIncluded()) append(" EXCLUSIVE");
+            append(" AND ");
+            between.getUpperBound().accept(this);
+            if (!between.isUpperBoundIncluded()) append(" EXCLUSIVE");
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
          */
         public void visit( BindVariableName variable ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -48,6 +48,7 @@
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.BindVariableName;
 import org.jboss.dna.graph.query.model.ChildNode;
 import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
@@ -416,13 +417,21 @@
                         String msg = GraphI18n.expectingConstraintCondition.text(name, pos2.getLine(), pos2.getColumn());
                         throw new ParsingException(pos, msg);
                     }
-                    if (tokens.matches("IN", "(")) {
+                    if (tokens.matches("IN", "(") || tokens.matches("NOT", "IN", "(")) {
+                        boolean not = tokens.canConsume("NOT");
                         Collection<StaticOperand> staticOperands = parseInClause(tokens, context);
                         constraint = new SetCriteria(left, staticOperands);
-                    } else if (tokens.matches("NOT", "IN", "(")) {
-                        tokens.consume("NOT");
-                        Collection<StaticOperand> staticOperands = parseInClause(tokens, context);
-                        constraint = new Not(new SetCriteria(left, staticOperands));
+                        if (not) constraint = new Not(constraint);
+                    } else if (tokens.matches("BETWEEN") || tokens.matches("NOT", "BETWEEN")) {
+                        boolean not = tokens.canConsume("NOT");
+                        tokens.consume("BETWEEN");
+                        StaticOperand lowerBound = parseStaticOperand(tokens, context);
+                        boolean lowerInclusive = !tokens.canConsume("EXCLUSIVE");
+                        tokens.consume("AND");
+                        StaticOperand upperBound = parseStaticOperand(tokens, context);
+                        boolean upperInclusive = !tokens.canConsume("EXCLUSIVE");
+                        constraint = new Between(left, lowerBound, upperBound, lowerInclusive, upperInclusive);
+                        if (not) constraint = new Not(constraint);
                     } else {
                         Operator operator = parseComparisonOperator(tokens);
                         StaticOperand right = parseStaticOperand(tokens, context);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -36,6 +36,7 @@
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.ChildNode;
 import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
 import org.jboss.dna.graph.query.model.Column;
@@ -433,6 +434,15 @@
             if (replacement == null) return search;
             return new FullTextSearch(replacement, search.getPropertyName(), search.getFullTextSearchExpression());
         }
+        if (constraint instanceof Between) {
+            Between between = (Between)constraint;
+            DynamicOperand lhs = between.getOperand();
+            StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no reference to selector
+            StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no reference to selector
+            DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
+            if (lhs == newLhs) return between;
+            return new Between(newLhs, lower, upper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
+        }
         if (constraint instanceof Comparison) {
             Comparison comparison = (Comparison)constraint;
             DynamicOperand lhs = comparison.getOperand1();
@@ -653,6 +663,15 @@
             return new FullTextSearch(sourceColumn.getSelectorName(), sourceColumn.getPropertyName(),
                                       search.getFullTextSearchExpression());
         }
+        if (constraint instanceof Between) {
+            Between between = (Between)constraint;
+            DynamicOperand lhs = between.getOperand();
+            StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no reference to selector
+            StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no reference to selector
+            DynamicOperand newLhs = replaceViewReferences(context, lhs, mapping, node);
+            if (lhs == newLhs) return between;
+            return new Between(newLhs, lower, upper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
+        }
         if (constraint instanceof Comparison) {
             Comparison comparison = (Comparison)constraint;
             DynamicOperand lhs = comparison.getOperand1();

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -280,6 +280,50 @@
     }
 
     @Test
+    public void shouldBuildQueryWithBetweenRange() {
+        query = builder.selectStar()
+                       .from("table AS nodes")
+                       .where()
+                       .propertyValue("nodes", "col1")
+                       .isBetween()
+                       .literal("lower")
+                       .and()
+                       .literal(true)
+                       .end()
+                       .query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE nodes.col1 BETWEEN 'lower' AND true"));
+
+        query = builder.selectStar()
+                       .from("table AS nodes")
+                       .where()
+                       .propertyValue("nodes", "col1")
+                       .isBetween()
+                       .literal("lower")
+                       .and()
+                       .literal("upper")
+                       .end()
+                       .query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE nodes.col1 BETWEEN 'lower' AND 'upper'"));
+    }
+
+    @Test
+    public void shouldBuildQueryWithBetweenRangeWithCast() {
+        query = builder.selectStar()
+                       .from("table AS nodes")
+                       .where()
+                       .propertyValue("nodes", "col1")
+                       .isBetween()
+                       .cast("true")
+                       .asBoolean()
+                       .and()
+                       .cast("false")
+                       .asBoolean()
+                       .end()
+                       .query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE nodes.col1 BETWEEN true AND false"));
+    }
+
+    @Test
     public void shouldBuildQueryWithOneHasPropertyConstraint() {
         query = builder.selectStar().from("table AS nodes").where().hasProperty("nodes", "col1").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes WHERE nodes.col1 IS NOT NULL"));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -39,6 +39,7 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.BindVariableName;
 import org.jboss.dna.graph.query.model.ChildNode;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -268,6 +269,108 @@
     }
 
     // ----------------------------------------------------------------------------------------------------------------
+    // parseConstraint - between
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseConstraintFromStringWithValidBetweenExpressionUsing() {
+        NamedSelector selector = new NamedSelector(selectorName("tableA"));
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' AND 'upper'"), context, selector);
+        assertThat(constraint, is(instanceOf(Between.class)));
+        Between between = (Between)constraint;
+        assertThat(between.isLowerBoundIncluded(), is(true));
+        assertThat(between.isUpperBoundIncluded(), is(true));
+        assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
+        PropertyValue operand = (PropertyValue)between.getOperand();
+        assertThat(operand.getSelectorName(), is(selector.getName()));
+        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat((Literal)between.getLowerBound(), is(literal("lower")));
+        assertThat((Literal)between.getUpperBound(), is(literal("upper")));
+    }
+
+    @Test
+    public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingExclusiveAndExclusive() {
+        NamedSelector selector = new NamedSelector(selectorName("tableA"));
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' EXCLUSIVE AND 'upper' EXCLUSIVE"),
+                                                       context,
+                                                       selector);
+        assertThat(constraint, is(instanceOf(Between.class)));
+        Between between = (Between)constraint;
+        assertThat(between.isLowerBoundIncluded(), is(false));
+        assertThat(between.isUpperBoundIncluded(), is(false));
+        assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
+        PropertyValue operand = (PropertyValue)between.getOperand();
+        assertThat(operand.getSelectorName(), is(selector.getName()));
+        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat((Literal)between.getLowerBound(), is(literal("lower")));
+        assertThat((Literal)between.getUpperBound(), is(literal("upper")));
+    }
+
+    @Test
+    public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingInclusiveAndExclusive() {
+        NamedSelector selector = new NamedSelector(selectorName("tableA"));
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' AND 'upper' EXCLUSIVE"),
+                                                       context,
+                                                       selector);
+        assertThat(constraint, is(instanceOf(Between.class)));
+        Between between = (Between)constraint;
+        assertThat(between.isLowerBoundIncluded(), is(true));
+        assertThat(between.isUpperBoundIncluded(), is(false));
+        assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
+        PropertyValue operand = (PropertyValue)between.getOperand();
+        assertThat(operand.getSelectorName(), is(selector.getName()));
+        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat((Literal)between.getLowerBound(), is(literal("lower")));
+        assertThat((Literal)between.getUpperBound(), is(literal("upper")));
+    }
+
+    @Test
+    public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingExclusiveAndInclusive() {
+        NamedSelector selector = new NamedSelector(selectorName("tableA"));
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' EXCLUSIVE AND 'upper'"),
+                                                       context,
+                                                       selector);
+        assertThat(constraint, is(instanceOf(Between.class)));
+        Between between = (Between)constraint;
+        assertThat(between.isLowerBoundIncluded(), is(false));
+        assertThat(between.isUpperBoundIncluded(), is(true));
+        assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
+        PropertyValue operand = (PropertyValue)between.getOperand();
+        assertThat(operand.getSelectorName(), is(selector.getName()));
+        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat((Literal)between.getLowerBound(), is(literal("lower")));
+        assertThat((Literal)between.getUpperBound(), is(literal("upper")));
+    }
+
+    @Test
+    public void shouldParseConstraintFromStringWithValidNotBetweenExpression() {
+        NamedSelector selector = new NamedSelector(selectorName("tableA"));
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id NOT BETWEEN 'lower' AND 'upper'"), context, selector);
+        assertThat(constraint, is(instanceOf(Not.class)));
+        constraint = ((Not)constraint).getConstraint();
+        assertThat(constraint, is(instanceOf(Between.class)));
+        Between between = (Between)constraint;
+        assertThat(between.isLowerBoundIncluded(), is(true));
+        assertThat(between.isUpperBoundIncluded(), is(true));
+        assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
+        PropertyValue operand = (PropertyValue)between.getOperand();
+        assertThat(operand.getSelectorName(), is(selector.getName()));
+        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
+        assertThat((Literal)between.getLowerBound(), is(literal("lower")));
+        assertThat((Literal)between.getUpperBound(), is(literal("upper")));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
     // parseConstraint - parentheses
     // ----------------------------------------------------------------------------------------------------------------
 

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -1114,6 +1114,52 @@
 
         }
 
+        protected Query findNodesWithNumericRange( PropertyValue propertyValue,
+                                                   Object lowerValue,
+                                                   Object upperValue,
+                                                   boolean includesLower,
+                                                   boolean includesUpper ) {
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            return findNodesWithNumericRange(field, lowerValue, upperValue, includesLower, includesUpper);
+        }
+
+        protected Query findNodesWithNumericRange( NodeDepth depth,
+                                                   Object lowerValue,
+                                                   Object upperValue,
+                                                   boolean includesLower,
+                                                   boolean includesUpper ) {
+            return findNodesWithNumericRange(PathIndex.DEPTH, lowerValue, upperValue, includesLower, includesUpper);
+        }
+
+        protected Query findNodesWithNumericRange( String field,
+                                                   Object lowerValue,
+                                                   Object upperValue,
+                                                   boolean includesLower,
+                                                   boolean includesUpper ) {
+            PropertyType type = PropertyType.discoverType(lowerValue);
+            assert type == PropertyType.discoverType(upperValue);
+            ValueFactories factories = context.getValueFactories();
+            switch (type) {
+                case DATE:
+                    long lowerDate = factories.getLongFactory().create(lowerValue);
+                    long upperDate = factories.getLongFactory().create(upperValue);
+                    return NumericRangeQuery.newLongRange(field, lowerDate, upperDate, includesLower, includesUpper);
+                case LONG:
+                    long lowerLong = factories.getLongFactory().create(lowerValue);
+                    long upperLong = factories.getLongFactory().create(upperValue);
+                    return NumericRangeQuery.newLongRange(field, lowerLong, upperLong, includesLower, includesUpper);
+                case DECIMAL:
+                case DOUBLE:
+                    double lowerDouble = factories.getDoubleFactory().create(lowerValue);
+                    double upperDouble = factories.getDoubleFactory().create(upperValue);
+                    return NumericRangeQuery.newDoubleRange(field, lowerDouble, upperDouble, includesLower, includesUpper);
+                default:
+                    // This is not allowed ...
+                    assert false;
+                    return null;
+            }
+        }
+
         protected Query findNodesWith( NodePath nodePath,
                                        Operator operator,
                                        Object value,

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-16 23:57:21 UTC (rev 1322)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-17 18:46:21 UTC (rev 1323)
@@ -52,10 +52,12 @@
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.BindVariableName;
 import org.jboss.dna.graph.query.model.ChildNode;
 import org.jboss.dna.graph.query.model.Comparison;
@@ -381,6 +383,10 @@
                 PropertyExistence existence = (PropertyExistence)constraint;
                 return createQuery(existence.getSelectorName(), existence.getPropertyName());
             }
+            if (constraint instanceof Between) {
+                Between between = (Between)constraint;
+                return createQuery(between);
+            }
             if (constraint instanceof Comparison) {
                 Comparison comparison = (Comparison)constraint;
                 return createQuery(comparison.getOperand1(), comparison.getOperator(), comparison.getOperand2());
@@ -433,20 +439,8 @@
                                      StaticOperand right,
                                      boolean caseSensitive ) throws IOException {
             // Handle the static operand ...
-            Object value = null;
-            if (right instanceof Literal) {
-                Literal literal = (Literal)right;
-                value = literal.getValue();
-                if (!caseSensitive) value = lowerCase(value);
-            } else if (right instanceof BindVariableName) {
-                BindVariableName variable = (BindVariableName)right;
-                String variableName = variable.getVariableName();
-                value = getContext().getVariables().get(variableName);
-                if (!caseSensitive) value = lowerCase(value);
-            } else {
-                assert false;
-                return null;
-            }
+            Object value = createOperand(right, caseSensitive);
+            assert value != null;
 
             // Address the dynamic operand ...
             if (left instanceof FullTextSearchScore) {
@@ -481,6 +475,71 @@
             }
         }
 
+        protected Object createOperand( StaticOperand operand,
+                                        boolean caseSensitive ) {
+            Object value = null;
+            if (operand instanceof Literal) {
+                Literal literal = (Literal)operand;
+                value = literal.getValue();
+                if (!caseSensitive) value = lowerCase(value);
+            } else if (operand instanceof BindVariableName) {
+                BindVariableName variable = (BindVariableName)operand;
+                String variableName = variable.getVariableName();
+                value = getContext().getVariables().get(variableName);
+                if (!caseSensitive) value = lowerCase(value);
+            } else {
+                assert false;
+            }
+            return value;
+        }
+
+        protected Query createQuery( DynamicOperand left,
+                                     StaticOperand lower,
+                                     StaticOperand upper,
+                                     boolean includesLower,
+                                     boolean includesUpper,
+                                     boolean caseSensitive ) throws IOException {
+            // Handle the static operands ...
+            Object lowerValue = createOperand(lower, caseSensitive);
+            Object upperValue = createOperand(upper, caseSensitive);
+            assert lowerValue != null;
+            assert upperValue != null;
+
+            // Only in the case of a PropertyValue and Depth will we need to do something special ...
+            if (left instanceof NodeDepth) {
+                return session.findNodesWithNumericRange((NodeDepth)left, lowerValue, upperValue, includesLower, includesUpper);
+            } else if (left instanceof PropertyValue) {
+                PropertyType lowerType = PropertyType.discoverType(lowerValue);
+                PropertyType upperType = PropertyType.discoverType(upperValue);
+                if (upperType == lowerType) {
+                    switch (upperType) {
+                        case DATE:
+                        case LONG:
+                        case DOUBLE:
+                        case DECIMAL:
+                            return session.findNodesWithNumericRange((PropertyValue)left,
+                                                                     lowerValue,
+                                                                     upperValue,
+                                                                     includesLower,
+                                                                     includesUpper);
+                        default:
+                            // continue on and handle as boolean query ...
+                    }
+                }
+            }
+
+            // Otherwise, just create a boolean query ...
+            BooleanQuery query = new BooleanQuery();
+            Operator lowerOp = includesLower ? Operator.GREATER_THAN_OR_EQUAL_TO : Operator.GREATER_THAN;
+            Operator upperOp = includesUpper ? Operator.LESS_THAN_OR_EQUAL_TO : Operator.LESS_THAN;
+            Query lowerQuery = createQuery(left, lowerOp, lower, caseSensitive);
+            Query upperQuery = createQuery(left, upperOp, upper, caseSensitive);
+            if (lowerQuery == null || upperQuery == null) return null;
+            query.add(lowerQuery, Occur.MUST);
+            query.add(upperQuery, Occur.MUST);
+            return query;
+        }
+
         protected Object lowerCase( Object value ) {
             if (value instanceof String) {
                 return ((String)value).toLowerCase();


From dna-commits at lists.jboss.org  Tue Nov 17 13:47:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Nov 2009 13:47:00 -0500
Subject: [dna-commits] DNA SVN: r1324 - in trunk/dna-graph/src:
	main/java/org/jboss/dna/graph/query/optimize and 3 other
	directories.
Message-ID: <200911171847.nAHIl0VL019780@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-17 13:47:00 -0500 (Tue, 17 Nov 2009)
New Revision: 1324

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RemoveEmptyAccessNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
Log:
DNA-552 The abstract query model now has a new Constraint subclass called Between that has a DynamicOperand, a StaticOperand lower boundary, a StaticOperand upper boundary, and a flag for each boundary that specifies whether the boundary is included or excluded in the range.  This very capably handles all kinds of continuous ranges.

The dna-searc project was enhanced to support this new Constraint type for numeric values (long, double, decimal, and date).

The SQL language parser now handles the '<dynamicOperand> BETWEEN <staticOperandLowerBound> [EXCLUSIVE] AND <staticOperandUpperBound> [EXCLUSIVE]'.

Additionally, a query optimizer rule was created to look for pairs of Comparison constraints that can be merged or rewritten:
- two range constraints are replaced with a single Between constraint (e.g., 'table.column' >=4 and table.column <=10' replaced with 'table.column BETWEEN 4 AND 10', with the exclusive flags set properly)
- two range constraints that specify a range of a single value are replaced with a single equality constraint (e.g., 'table.column >=4 AND table.column <= 4' replaced with 'table.column = 4')
- unnecessary Comparison constraints are removed (e.g., given 'table.column < 4 AND table.column < 10' the latter constraint is removed)
- conflicting constraints that will never be satisfied (e.g., 'table.column < 4 AND table.column > 10), marking the ACCESS node as having no results (this is handled in the query processor)

The QueryProcessor was also changed to look for an ACCESS node with the (new) ACCESS_NO_RESULTS property, and to create a NoResultsComponent rather than a real access component.  This will work in that an access query will never be made when we know the range constraints are invalid, but it does not look for other always-false constraints nor does it optimize the plan.  (A RemoveEmptyAccessNodes optimizer plan was created, though it basically is a no-op at the moment.)

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -87,6 +87,27 @@
     }
 
     /**
+     * Determine whether this operator is one that is used to define a range of values: {@link #LESS_THAN <},
+     * {@link #GREATER_THAN >}, {@link #LESS_THAN_OR_EQUAL_TO <=}, or {@link #GREATER_THAN_OR_EQUAL_TO >=}.
+     * 
+     * @return true if this operator is a range operator, or false otherwise
+     */
+    public boolean isRangeOperator() {
+        switch (this) {
+            case GREATER_THAN:
+            case GREATER_THAN_OR_EQUAL_TO:
+            case LESS_THAN:
+            case LESS_THAN_OR_EQUAL_TO:
+                return true;
+            case EQUAL_TO:
+            case LIKE:
+            case NOT_EQUAL_TO:
+            default:
+                return false;
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Enum#toString()

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RemoveEmptyAccessNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RemoveEmptyAccessNodes.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RemoveEmptyAccessNodes.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -0,0 +1,65 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.optimize;
+
+import java.util.LinkedList;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that removes any ACCESS nodes that are known to never return any tuples because of
+ * conflicting constraints.
+ */
+ at Immutable
+public class RemoveEmptyAccessNodes implements OptimizerRule {
+
+    public static final RemoveEmptyAccessNodes INSTANCE = new RemoveEmptyAccessNodes();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        // Find all access nodes ...
+        for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
+            if (access.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class)) {
+                // This node has conflicting constraints and will never return any results ...
+
+                // TODO: implement this rule.
+
+                // At least the QueryProcessor looks for this property and always creates a NoResultsComponent,
+                // saving some work. But implementing this rule will make queries more efficient.
+            }
+        }
+
+        return plan;
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RemoveEmptyAccessNodes.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -0,0 +1,335 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.optimize;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
+import org.jboss.dna.graph.query.model.BindVariableName;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.Visitor;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that rewrites two {@link And AND-ed} {@link Constraint}s that constraint a dynamic
+ * operand to a range of values as a single {@link Between} constraint. This rule also collapses and removes any constraints that
+ * are unnecessary because other constraints are more restrictive or because they cancel out other constraints.
+ */
+ at Immutable
+public class RewriteAsRangeCriteria implements OptimizerRule {
+
+    protected static final Constraint CONFLICTING_CONSTRAINT = new Constraint() {
+        public void accept( Visitor visitor ) {
+            throw new UnsupportedOperationException();
+        }
+    };
+
+    public static final RewriteAsRangeCriteria INSTANCE = new RewriteAsRangeCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        // Find all the access nodes ...
+        boolean rewritten = false;
+        boolean foundNoResults = false;
+        for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
+            // Look for select nodes below an ACCESS node that have a single Comparison constraint,
+            // and accumulate them keyed by the dynamic operand ...
+            Multimap<DynamicOperand, PlanNode> selectNodeByOperand = Multimaps.newArrayListMultimap();
+            for (PlanNode select : access.findAllAtOrBelow(Type.SELECT)) {
+                Constraint constraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                // Look for Comparison constraints that use a range operator
+                if (constraint instanceof Comparison) {
+                    Comparison comparison = (Comparison)constraint;
+                    if (comparison.getOperator().isRangeOperator()) {
+                        selectNodeByOperand.put(comparison.getOperand1(), select);
+                    }
+                }
+            }
+
+            if (!selectNodeByOperand.isEmpty()) {
+
+                // Go through the constraints we've found ...
+                for (DynamicOperand operand : selectNodeByOperand.keySet()) {
+                    Collection<PlanNode> nodes = selectNodeByOperand.get(operand);
+                    if (nodes.size() <= 1) continue;
+
+                    // Extract the constraints from the nodes ...
+                    List<Comparison> rangeConstraints = new ArrayList<Comparison>(nodes.size());
+                    List<PlanNode> selectNodes = new ArrayList<PlanNode>(nodes.size());
+                    Set<SelectorName> selectors = null;
+                    for (PlanNode select : nodes) {
+                        selectNodes.add(select);
+                        Comparison constraint = select.getProperty(Property.SELECT_CRITERIA, Comparison.class);
+                        rangeConstraints.add(constraint);
+                        // Record the selector names (should all be the same) ...
+                        if (selectors == null) selectors = select.getSelectors();
+                        else assert selectors.equals(select.getSelectors());
+                    }
+
+                    // Attempt to merge the constraints ...
+                    Constraint merged = rewrite(context, rangeConstraints);
+                    if (merged == CONFLICTING_CONSTRAINT) {
+                        // The ANDed constraints cancel each other out, so this whole access node will return no results ...
+                        access.setProperty(Property.ACCESS_NO_RESULTS, Boolean.TRUE);
+                        foundNoResults = true;
+                        break; // don't do anything else under this access node
+                    }
+                    if (merged != null) {
+                        // Add a SELECT node for the new merged constraint ...
+                        PlanNode newSelect = new PlanNode(Type.SELECT);
+                        newSelect.getSelectors().addAll(selectors);
+                        newSelect.setProperty(Property.SELECT_CRITERIA, merged);
+
+                        // And insert the SELECT node into the tree (just below the ACCESS, we'll rerun pushdown selects) ...
+                        assert access.getChildCount() == 1;
+                        access.getFirstChild().insertAsParent(newSelect);
+                        rewritten = true;
+                    }
+
+                    // Remove any of the SELECT nodes that were not needed (this can happen if the constraints are not needed) ...
+                    Iterator<PlanNode> nodeIter = selectNodes.iterator();
+                    Iterator<Comparison> constraintIter = rangeConstraints.iterator();
+                    while (nodeIter.hasNext()) {
+                        assert constraintIter.hasNext();
+                        PlanNode node = nodeIter.next();
+                        Comparison comparison = constraintIter.next();
+                        if (comparison == null) {
+                            // This comparison was rewritten, so remove the PlanNode ...
+                            node.extractFromParent();
+                            nodeIter.remove();
+                        }
+                    }
+                    assert !constraintIter.hasNext();
+                }
+            }
+        }
+
+        if (rewritten) {
+            // We mucked with the SELECT nodes, adding SELECT node for each rewritten constraint.
+            // Rerun the rule that pushes SELECT nodes ...
+            ruleStack.addFirst(PushSelectCriteria.INSTANCE);
+        }
+        if (foundNoResults) {
+            ruleStack.addFirst(RemoveEmptyAccessNodes.INSTANCE);
+        }
+
+        return plan;
+    }
+
+    /**
+     * Rewrite the supplied comparisons, returning the new constraint and nulling in the supplied list those comparisons that were
+     * rewritten (and leaving those that were not rewritten)
+     * 
+     * @param context the query context
+     * @param comparisons the list of comparisons that sould be rewritten if possible; never null
+     * @return the rewritten constraint, or null if no comparisons were rewritten
+     */
+    @SuppressWarnings( "fallthrough" )
+    protected Constraint rewrite( QueryContext context,
+                                  List<Comparison> comparisons ) {
+        // Look for the lower bound (greater-than) and upper bound (less-than) ...
+        Comparison lessThan = null;
+        Comparison greaterThan = null;
+        List<Comparison> notNeeded = new LinkedList<Comparison>();
+        boolean inclusive = false;
+        for (Comparison comparison : comparisons) {
+            switch (comparison.getOperator()) {
+                case GREATER_THAN_OR_EQUAL_TO:
+                    inclusive = true;
+                case GREATER_THAN:
+                    if (greaterThan != null) {
+                        // Find the smallest value ...
+                        Comparison newGreaterThan = getComparison(context, greaterThan, comparison, true);
+                        notNeeded.add(newGreaterThan == greaterThan ? comparison : greaterThan);
+                        greaterThan = newGreaterThan;
+                    } else {
+                        greaterThan = comparison;
+                    }
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    inclusive = true;
+                case LESS_THAN:
+                    if (lessThan != null) {
+                        // Find the largest value ...
+                        Comparison newLessThan = getComparison(context, lessThan, comparison, false);
+                        notNeeded.add(newLessThan == lessThan ? comparison : lessThan);
+                        greaterThan = newLessThan;
+                    } else {
+                        lessThan = comparison;
+                    }
+                    break;
+                default:
+                    assert false;
+                    return null;
+            }
+        }
+        if (lessThan == null || greaterThan == null) return null;
+
+        // Create the new Comparison ...
+        Constraint result = null;
+
+        // Compute the difference between the lessThan value and greaterThan value ...
+        int diff = compareStaticOperands(context, greaterThan, lessThan);
+        if (diff == 0) {
+            // The static operands are equivalent ...
+            if (inclusive) {
+                // At least one of the sides was inclusive, meaning the constraints were something
+                // like 'x >= 2 AND x < 2', so we can replace these with an equality constraint ...
+                result = new Comparison(lessThan.getOperand1(), Operator.EQUAL_TO, lessThan.getOperand2());
+                notNeeded.add(lessThan);
+                notNeeded.add(greaterThan);
+            } else {
+                // Neither is inclusive, so really the constraints are not needed anymore.
+                // And, because the constraints conflict, the whole access will return no nodes.
+                // So return the placeholder ...
+                return CONFLICTING_CONSTRAINT;
+            }
+        } else if (diff < 0) {
+            // The range is valid as is ...
+            boolean lowerInclusive = greaterThan.getOperator() == Operator.GREATER_THAN_OR_EQUAL_TO;
+            boolean upperInclusive = lessThan.getOperator() == Operator.LESS_THAN_OR_EQUAL_TO;
+            result = new Between(lessThan.getOperand1(), greaterThan.getOperand2(), lessThan.getOperand2(), lowerInclusive,
+                                 upperInclusive);
+            notNeeded.add(lessThan);
+            notNeeded.add(greaterThan);
+        } else {
+            // The range is actually something like 'x < 2 AND x > 4', which can never happen ...
+            return CONFLICTING_CONSTRAINT;
+        }
+
+        // Now null out those comparison objects that are not needed ...
+        nullReference(comparisons, notNeeded);
+        return result;
+    }
+
+    /**
+     * Find all occurrences of the comparison object in the supplied list and null the list's reference to it.
+     * 
+     * @param comparisons the collection in which null references are to be placed
+     * @param comparisonToNull the comparison that is to be found and nulled in the collection
+     */
+    protected void nullReference( List<Comparison> comparisons,
+                                  Comparison comparisonToNull ) {
+        if (comparisonToNull != null) {
+            for (int i = 0; i != comparisons.size(); ++i) {
+                if (comparisons.get(i) == comparisonToNull) comparisons.set(i, null);
+            }
+        }
+    }
+
+    /**
+     * Find all references in the supplied list that match those supplied and set them to null.
+     * 
+     * @param comparisons the collection in which null references are to be placed
+     * @param comparisonsToNull the comparisons that are to be found and nulled in the collection
+     */
+    protected void nullReference( List<Comparison> comparisons,
+                                  Iterable<Comparison> comparisonsToNull ) {
+        for (Comparison comparisonToNull : comparisonsToNull) {
+            nullReference(comparisons, comparisonToNull);
+        }
+    }
+
+    /**
+     * Compare the values used in the two comparisons
+     * 
+     * @param context the query context; may not be null
+     * @param comparison1 the first comparison object; may not be null
+     * @param comparison2 the second comparison object; may not be null
+     * @return 0 if the values are the same, less than 0 if the first comparison's value is less than the second's, or greater
+     *         than 0 if the first comparison's value is greater than the second's
+     */
+    protected int compareStaticOperands( QueryContext context,
+                                         Comparison comparison1,
+                                         Comparison comparison2 ) {
+        Object value1 = getValue(context, comparison1.getOperand2());
+        Object value2 = getValue(context, comparison2.getOperand2());
+        return ValueComparators.OBJECT_COMPARATOR.compare(value1, value2);
+    }
+
+    /**
+     * Get the comparison with the smallest (or largest) value.
+     * 
+     * @param context the query context; may not be null
+     * @param comparison1 the first comparison object; may not be null
+     * @param comparison2 the second comparison object; may not be null
+     * @param smallest true if the comparison with the smallest value should be returned, or false otherwise
+     * @return the comparison with the smallest (or largest) value
+     */
+    protected Comparison getComparison( QueryContext context,
+                                        Comparison comparison1,
+                                        Comparison comparison2,
+                                        boolean smallest ) {
+        int diff = compareStaticOperands(context, comparison1, comparison2);
+        if (diff == 0) {
+            // They are the same ...
+            return comparison1;
+        }
+        if (!smallest) diff = -1 * diff;
+        return diff < 1 ? comparison1 : comparison2;
+    }
+
+    /**
+     * Get the value associated with the static operand of the comparison. If the operand is a {@link BindVariableName variable
+     * name}, the variable value is returned.
+     * 
+     * @param context the query context; may not be null
+     * @param operand the static operand; may not be null
+     * @return the value of the static operand
+     */
+    protected Object getValue( QueryContext context,
+                               StaticOperand operand ) {
+        if (operand instanceof Literal) {
+            Literal literal = (Literal)operand;
+            return literal.getValue();
+        }
+        BindVariableName variable = (BindVariableName)operand;
+        return context.getVariables().get(variable.getVariableName());
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -69,6 +69,7 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
+        ruleStack.addFirst(RewriteAsRangeCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
             ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -39,7 +39,6 @@
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.Column;
-import org.jboss.dna.graph.query.model.Command;
 import org.jboss.dna.graph.query.model.Constraint;
 import org.jboss.dna.graph.query.model.JoinCondition;
 import org.jboss.dna.graph.query.model.JoinType;
@@ -189,9 +188,11 @@
         /** For LIMIT nodes, the offset value. Value is an {@link Integer} object. */
         LIMIT_OFFSET,
 
-        /** For ACCESS nodes, the {@link Command} that is to be executed by the source */
-        // ACCESS_COMMAND,
-        BOGUS; // remove this
+        /**
+         * For ACESS nodes, this signifies that the node will never return results. Value is a {@link Boolean} object, though the
+         * mere presence of this property signifies that it is no longer needed.
+         */
+        ACCESS_NO_RESULTS
     }
 
     private Type type;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -161,11 +161,16 @@
         ProcessingComponent component = null;
         switch (node.getType()) {
             case ACCESS:
-                // Create the component to handle the ACCESS node ...
-                assert node.getChildCount() == 1;
-                component = createAccessComponent(originalQuery, context, node, columns, analyzer);
-                // // Don't do anything special with an access node at the moment ...
-                // component = createComponent(context, node.getFirstChild(), columns, analyzer);
+                // If the ACCESS node will not have results ...
+                if (node.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class)) {
+                    component = new NoResultsComponent(context, columns);
+                } else {
+                    // Create the component to handle the ACCESS node ...
+                    assert node.getChildCount() == 1;
+                    component = createAccessComponent(originalQuery, context, node, columns, analyzer);
+                    // // Don't do anything special with an access node at the moment ...
+                    // component = createComponent(context, node.getFirstChild(), columns, analyzer);
+                }
                 break;
             case DUP_REMOVE:
                 // Create the component under the DUP_REMOVE ...

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -206,7 +206,7 @@
         // Execute the rule ...
         PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
 
-        System.out.println(result);
+        // System.out.println(result);
 
         assertThat(result, is(sameInstance(project)));
         assertChildren(project, select3);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-17 18:46:21 UTC (rev 1323)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -131,8 +131,8 @@
 
         // Execute the rule ...
         PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
-        System.out.println(project);
-        System.out.println(result);
+        // System.out.println(project);
+        // System.out.println(result);
         assertThat(result.isSameAs(project), is(true));
         assertChildren(project, select1);
         assertChildren(select1, select2);

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java	2009-11-17 18:47:00 UTC (rev 1324)
@@ -0,0 +1,417 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.optimize;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.LinkedList;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Between;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class RewriteAsRangeCriteriaTest extends AbstractQueryTest {
+
+    private RewriteAsRangeCriteria rule;
+    private LinkedList<OptimizerRule> rules;
+    private QueryContext context;
+    private boolean print = true;
+
+    @Before
+    public void beforeEach() {
+        rule = RewriteAsRangeCriteria.INSTANCE;
+        rules = new LinkedList<OptimizerRule>();
+        rules.add(rule);
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+    }
+
+    protected void print( PlanNode node ) {
+        if (print) System.out.println(node);
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt; 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt; 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1]
+     *  Project [t1]
+     *    Select [t1] &lt;SELECT_CRITERIA=t1.c1 BETWEEN 1 EXCLUSIVE AND 3 EXCLUSIVE&gt;
+     *      Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *        Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceComparisonsSpecifyingExclusiveRangeWithBetweenConstraint() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN, new Literal(1L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertChildren(access, project);
+        PlanNode newSelect = project.getFirstChild();
+        assertThat(newSelect.getType(), is(Type.SELECT));
+        assertThat(newSelect.getSelectors(), is(access.getSelectors()));
+        assertThat(newSelect.getParent(), is(sameInstance(project)));
+        Between between = newSelect.getProperty(Property.SELECT_CRITERIA, Between.class);
+        assertThat(between.getOperand(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand1()));
+        assertThat(between.getLowerBound(), is(select3.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand2()));
+        assertThat(between.getUpperBound(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand2()));
+        assertThat(between.isLowerBoundIncluded(), is(false));
+        assertThat(between.isUpperBoundIncluded(), is(false));
+        assertChildren(newSelect, select1);
+        assertChildren(select1, source);
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt;= 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt;= 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1]
+     *  Project [t1]
+     *    Select [t1] &lt;SELECT_CRITERIA=t1.c1 BETWEEN 1 AND 3&gt;
+     *      Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *        Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceComparisonsSpecifyingInclusiveRangeWithBetweenConstraint() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN_OR_EQUAL_TO, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(1L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertChildren(access, project);
+        PlanNode newSelect = project.getFirstChild();
+        assertThat(newSelect.getType(), is(Type.SELECT));
+        assertThat(newSelect.getSelectors(), is(access.getSelectors()));
+        assertThat(newSelect.getParent(), is(sameInstance(project)));
+        Between between = newSelect.getProperty(Property.SELECT_CRITERIA, Between.class);
+        assertThat(between.getOperand(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand1()));
+        assertThat(between.getLowerBound(), is(select3.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand2()));
+        assertThat(between.getUpperBound(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand2()));
+        assertThat(between.isLowerBoundIncluded(), is(true));
+        assertThat(between.isUpperBoundIncluded(), is(true));
+        assertChildren(newSelect, select1);
+        assertChildren(select1, source);
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt; 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt; 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1] &lt;ACCESS_NO_RESULTS=true&gt;
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt; 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt; 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceComparisonsSpecifyingExclusiveRangeWithNotBetweenConstraint() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN, new Literal(1L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertChildren(access, project);
+        assertThat(access.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class), is(true));
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt;= 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt;= 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1] &lt;ACCESS_NO_RESULTS=true&gt;
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt;= 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt;= 1&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceComparisonsSpecifyingInclusiveRangeWithNotBetweenConstraint() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN_OR_EQUAL_TO, new Literal(1L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertChildren(access, project);
+        assertThat(access.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class), is(true));
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt;= 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt;= 3&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1]
+     *  Project [t1]
+     *    Select [t1] &lt;SELECT_CRITERIA=t1.c1 = 3&gt;
+     *      Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *        Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceComparisonsSpecifyingInclusiveRangeWithOverlappingBoundaryEqualityComparison() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN_OR_EQUAL_TO, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(3L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertThat(access.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class), is(nullValue()));
+        assertChildren(access, project);
+        PlanNode newSelect = project.getFirstChild();
+        assertThat(newSelect.getType(), is(Type.SELECT));
+        assertThat(newSelect.getSelectors(), is(access.getSelectors()));
+        assertThat(newSelect.getParent(), is(sameInstance(project)));
+        Comparison equality = newSelect.getProperty(Property.SELECT_CRITERIA, Comparison.class);
+        assertThat(equality.getOperand1(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand1()));
+        assertThat(equality.getOperator(), is(Operator.EQUAL_TO));
+        assertThat(equality.getOperand2(), is(select2.getProperty(Property.SELECT_CRITERIA, Comparison.class).getOperand2()));
+        assertChildren(newSelect, select1);
+        assertChildren(select1, source);
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     * Access [t1]
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt; 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt; 3&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     * Access [t1] &lt;ACCESS_NO_RESULTS=true&gt;
+     *   Project [t1]
+     *     Select [t1] &lt;SELECT_CRITERIA=t1.c2 = 100&gt;
+     *       Select [t1] &lt;SELECT_CRITERIA=t1.c1 &lt; 3&gt;
+     *         Select [t1] &lt;SELECT_CRITERIA=t1.c1 &gt; 3&gt;
+     *           Source [t1] &lt;SOURCE_NAME=t1&gt;
+     * </pre>
+     */
+    @Test
+    public void shouldMarkAsHavingNoResultsWhenComparisonsSpecifyRangeWithNonOverlappingBoundary() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
+                                                                     Operator.EQUAL_TO, new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.LESS_THAN, new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+                                                                     Operator.GREATER_THAN, new Literal(3L)));
+
+        // Execute the rule ...
+        print(access);
+        PlanNode result = executeRules(access);
+        print(result);
+
+        // Compare results ...
+        assertThat(result, is(sameInstance(access)));
+        assertChildren(access, project);
+        assertThat(access.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class), is(true));
+    }
+
+    protected PlanNode executeRules( PlanNode node ) {
+        while (!rules.isEmpty()) {
+            OptimizerRule rule = rules.poll();
+            assert rule != null;
+            node = rule.execute(context, node, rules);
+        }
+        return node;
+    }
+
+    protected Name name( String name ) {
+        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Tue Nov 17 16:09:49 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Nov 2009 16:09:49 -0500
Subject: [dna-commits] DNA SVN: r1325 - in
	trunk/extensions/dna-connector-filesystem/src/main:
	resources/org/jboss/dna/connector/filesystem and 1 other directory.
Message-ID: <200911172109.nAHL9nVU017478@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-17 16:09:49 -0500 (Tue, 17 Nov 2009)
New Revision: 1325

Added:
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java
Modified:
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
   trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties
Log:
DNA-553 Enhanced the file system connector so that it is much easier to define how non-standard properties can be made to appear on nt:folder, nt:file, and nt:resource nodes.  By default, the file system connector behaves as it has in the past: it only uses the standard properties defined by JCR for files, folders and content nodes.

There is a new CustomPropertiesFactory interface that defines several methods for getting and setting custom properties.  Simply implement this interface, and call FileSystemSource.setCustomPropertiesFactory(...) with an instance of your implementation.  (Or, optionally subclass FileSystemSource, set the factory in the subclass' constructor, and inherit all other functionality).

The connector uses this factory whenever it reads 'nt:folder', 'nt:file', or 'nt:resource' nodes, and includes in these nodes the properties returned by the factory. The factory methods take several arguments, including the ExecutionContext (which can be used to obtain the PropertyFactory, value factories used to create property values, and even the MIME type detector), the java.io.File object, the Location (which includes the path), and other information.  Hopefully this is sufficient to construct the necessary custom properties.

Added: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java	                        (rev 0)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java	2009-11-17 21:09:49 UTC (rev 1325)
@@ -0,0 +1,151 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.connector.filesystem;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * A simple interface that allows an implementer to define additional properties for "nt:folder", "nt:file", and "nt:resource"
+ * nodes created by the file system connector.
+ * <p>
+ * To use, supply the implementation to a {@link FileSystemSource} object (or register the factory in a subclass of
+ * FileSystemSource). Implementations should be immutable because they are shared between all the connections.
+ * </p>
+ */
+ at Immutable
+public interface CustomPropertiesFactory extends Serializable {
+
+    /**
+     * Construct the custom properties that should be created for the supplied directory that is to be treated as an "nt:folder".
+     * The resulting properties should not include the standard {@link JcrLexicon#PRIMARY_TYPE} or {@link JcrLexicon#CREATED}
+     * properties, which are set automatically and will override any returned Property with the same name.
+     * 
+     * @param context the execution context; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param directory the file system object; never null and {@link File#isDirectory()} will always return true
+     * @return the custom properties; never null but possibly empty
+     */
+    Collection<Property> getDirectoryProperties( ExecutionContext context,
+                                                 Location location,
+                                                 File directory );
+
+    /**
+     * Construct the custom properties that should be created for the supplied file that is to be treated as an "nt:resource",
+     * which is the node that contains the content-oriented properties and that is a child of a "nt:file" node. The resulting
+     * properties should not include the standard {@link JcrLexicon#PRIMARY_TYPE}, {@link JcrLexicon#LAST_MODIFIED}, or
+     * {@link JcrLexicon#DATA} properties, which are set automatically and will override any returned Property with the same name.
+     * 
+     * @param context the execution context; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param file the file system object; never null and {@link File#isFile()} will always return true
+     * @param mimeType the mime type for the file, as determined by the {@link ExecutionContext#getMimeTypeDetector() MIME type
+     *        detector}, or null if the MIME type could not be determined
+     * @return the custom properties; never null but possibly empty
+     */
+    Collection<Property> getResourceProperties( ExecutionContext context,
+                                                Location location,
+                                                File file,
+                                                String mimeType );
+
+    /**
+     * Construct the custom properties that should be created for the supplied file that is to be treated as an "nt:file". The
+     * resulting properties should not include the standard {@link JcrLexicon#PRIMARY_TYPE} or {@link JcrLexicon#CREATED}
+     * properties, which are set automatically and will override any returned Property with the same name.
+     * <p>
+     * Although the connector does not automatically determine the MIME type for the "nt:file" nodes, an implementation can
+     * determine the MIME type by using the context's {@link ExecutionContext#getMimeTypeDetector() MIME type detector}. Note,
+     * however, that this may be an expensive operation, so it should be used only when needed.
+     * </p>
+     * 
+     * @param context the execution context; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param file the file system object; never null and {@link File#isFile()} will always return true
+     * @return the custom properties; never null but possibly empty
+     */
+    Collection<Property> getFileProperties( ExecutionContext context,
+                                            Location location,
+                                            File file );
+
+    /**
+     * Record the supplied properties as being set on the designated "nt:folder" node.
+     * 
+     * @param context the execution context; never null
+     * @param sourceName the name of the repository source; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isDirectory()} will always
+     *        return true
+     * @param properties the properties that are to be set
+     * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
+     */
+    void recordDirectoryProperties( ExecutionContext context,
+                                    String sourceName,
+                                    Location location,
+                                    File file,
+                                    Map<Name, Property> properties ) throws RepositorySourceException;
+
+    /**
+     * Record the supplied properties as being set on the designated "nt:file" node.
+     * 
+     * @param context the execution context; never null
+     * @param sourceName the name of the repository source; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isFile()} will always return
+     *        true
+     * @param properties the properties that are to be set
+     * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
+     */
+    void recordFileProperties( ExecutionContext context,
+                               String sourceName,
+                               Location location,
+                               File file,
+                               Map<Name, Property> properties ) throws RepositorySourceException;
+
+    /**
+     * Record the supplied properties as being set on the designated "nt:resource" node.
+     * 
+     * @param context the execution context; never null
+     * @param sourceName the name of the repository source; never null
+     * @param location the Location of the node, which always contains a {@link Location#getPath() path}; never null
+     * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isFile()} will always return
+     *        true
+     * @param properties the properties that are to be set
+     * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
+     */
+    void recordResourceProperties( ExecutionContext context,
+                                   String sourceName,
+                                   Location location,
+                                   File file,
+                                   Map<Name, Property> properties ) throws RepositorySourceException;
+
+}


Property changes on: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java	2009-11-17 18:47:00 UTC (rev 1324)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java	2009-11-17 21:09:49 UTC (rev 1325)
@@ -52,6 +52,7 @@
     private final int maxPathLength;
     private final String workspaceRootPath;
     private final boolean updatesAllowed;
+    private final CustomPropertiesFactory customPropertiesFactory;
 
     FileSystemConnection( String sourceName,
                           String defaultWorkspaceName,
@@ -62,11 +63,13 @@
                           String workspaceRootPath,
                           int maxPathLength,
                           FilenameFilter filenameFilter,
-                          boolean updatesAllowed ) {
+                          boolean updatesAllowed,
+                          CustomPropertiesFactory customPropertiesFactory ) {
         assert sourceName != null;
         assert sourceName.trim().length() != 0;
         assert availableWorkspaces != null;
         assert rootNodeUuid != null;
+        assert customPropertiesFactory != null;
         this.sourceName = sourceName;
         this.defaultWorkspaceName = defaultWorkspaceName;
         this.availableWorkspaces = availableWorkspaces;
@@ -77,6 +80,7 @@
         this.maxPathLength = maxPathLength;
         this.filenameFilter = filenameFilter;
         this.updatesAllowed = updatesAllowed;
+        this.customPropertiesFactory = customPropertiesFactory;
     }
 
     /**
@@ -126,7 +130,8 @@
                          Request request ) throws RepositorySourceException {
         RequestProcessor proc = new FileSystemRequestProcessor(sourceName, defaultWorkspaceName, availableWorkspaces,
                                                                creatingWorkspacesAllowed, rootNodeUuid, workspaceRootPath,
-                                                               maxPathLength, context, filenameFilter, updatesAllowed);
+                                                               maxPathLength, context, filenameFilter, updatesAllowed,
+                                                               customPropertiesFactory);
         try {
             proc.process(request);
         } finally {

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java	2009-11-17 18:47:00 UTC (rev 1324)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java	2009-11-17 21:09:49 UTC (rev 1325)
@@ -51,6 +51,7 @@
     public static I18n sourceIsReadOnly;
     public static I18n pathIsReadOnly;
     public static I18n unableToCreateWorkspaces;
+    public static I18n errorSerializingCustomPropertiesFactory;
 
     // Writable messages
     public static I18n parentIsReadOnly;

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java	2009-11-17 18:47:00 UTC (rev 1324)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java	2009-11-17 21:09:49 UTC (rev 1325)
@@ -30,8 +30,6 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -42,7 +40,6 @@
 import java.util.UUID;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.FileUtil;
-import org.jboss.dna.graph.DnaIntLexicon;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrLexicon;
@@ -91,31 +88,6 @@
 
     private static final String DEFAULT_MIME_TYPE = "application/octet";
 
-    /**
-     * Only certain properties are tolerated when writing content (dna:resource or jcr:resource) nodes. These properties are
-     * implicitly stored (primary type, data) or silently ignored (encoded, mimetype, last modified). The silently ignored
-     * properties must be accepted to stay compatible with the JCR specification.
-     */
-    private static final Set<Name> ALLOWABLE_PROPERTIES_FOR_CONTENT = Collections.unmodifiableSet(new HashSet<Name>(
-                                                                                                                    Arrays.asList(new Name[] {
-                                                                                                                        JcrLexicon.PRIMARY_TYPE,
-                                                                                                                        JcrLexicon.DATA,
-                                                                                                                        JcrLexicon.ENCODED,
-                                                                                                                        JcrLexicon.MIMETYPE,
-                                                                                                                        JcrLexicon.LAST_MODIFIED,
-                                                                                                                        JcrLexicon.UUID,
-                                                                                                                        DnaIntLexicon.NODE_DEFINITON})));
-    /**
-     * Only certain properties are tolerated when writing files (nt:file) or folders (nt:folder) nodes. These properties are
-     * implicitly stored in the file or folder (primary type, created).
-     */
-    private static final Set<Name> ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER = Collections.unmodifiableSet(new HashSet<Name>(
-                                                                                                                           Arrays.asList(new Name[] {
-                                                                                                                               JcrLexicon.PRIMARY_TYPE,
-                                                                                                                               JcrLexicon.CREATED,
-                                                                                                                               JcrLexicon.UUID,
-                                                                                                                               DnaIntLexicon.NODE_DEFINITON})));
-
     private final String defaultNamespaceUri;
     private final Map<String, File> availableWorkspaces;
     private final boolean creatingWorkspacesAllowed;
@@ -126,6 +98,7 @@
     private final boolean updatesAllowed;
     private final MimeTypeDetector mimeTypeDetector;
     private final UUID rootNodeUuid;
+    private final CustomPropertiesFactory customPropertiesFactory;
 
     /**
      * @param sourceName
@@ -141,6 +114,8 @@
      * @param filenameFilter the filename filter to use to restrict the allowable nodes, or null if all files/directories are to
      *        be exposed by this connector
      * @param updatesAllowed true if this connector supports updating the file system, or false if the connector is readonly
+     * @param customPropertiesFactory the factory that should be used to create custom properties for "nt:folder", "nt:file", and
+     *        "nt:resource" nodes
      */
     protected FileSystemRequestProcessor( String sourceName,
                                           String defaultWorkspaceName,
@@ -151,11 +126,13 @@
                                           int maxPathLength,
                                           ExecutionContext context,
                                           FilenameFilter filenameFilter,
-                                          boolean updatesAllowed ) {
+                                          boolean updatesAllowed,
+                                          CustomPropertiesFactory customPropertiesFactory ) {
         super(sourceName, context, null);
         assert defaultWorkspaceName != null;
         assert availableWorkspaces != null;
         assert rootNodeUuid != null;
+        assert customPropertiesFactory != null;
         this.availableWorkspaces = availableWorkspaces;
         this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
         this.defaultNamespaceUri = getExecutionContext().getNamespaceRegistry().getDefaultNamespaceUri();
@@ -165,6 +142,7 @@
         this.defaultWorkspaceName = defaultWorkspaceName;
         this.updatesAllowed = updatesAllowed;
         this.mimeTypeDetector = context.getMimeTypeDetector();
+        this.customPropertiesFactory = customPropertiesFactory;
 
         if (workspaceRootPath != null) {
             this.workspaceRootPath = new File(workspaceRootPath);
@@ -279,24 +257,17 @@
             return;
         }
         // Generate the properties for this File object ...
-        DateTimeFactory dateFactory = getExecutionContext().getValueFactories().getDateFactory();
+        final ExecutionContext context = getExecutionContext();
+        final DateTimeFactory dateFactory = context.getValueFactories().getDateFactory();
         // Note that we don't have 'created' timestamps, just last modified, so we'll have to use them
         if (file.isDirectory()) {
             // Add properties for the directory ...
+            request.addProperties(customPropertiesFactory.getDirectoryProperties(context, location, file));
             request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER));
             request.addProperty(factory.create(JcrLexicon.CREATED, dateFactory.create(file.lastModified())));
-
         } else {
             // It is a file, but ...
             if (path.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
-                // The request is to get properties of the "jcr:content" child node ...
-                // ... use the dna:resource node type. This is the same as nt:resource, but is not referenceable
-                // since we cannot assume that we control all access to this file and can track its movements
-                request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.RESOURCE));
-                request.addProperty(factory.create(JcrLexicon.LAST_MODIFIED, dateFactory.create(file.lastModified())));
-                // Don't really know the encoding, either ...
-                // request.addProperty(factory.create(JcrLexicon.ENCODED, stringFactory.create("UTF-8")));
-
                 // Discover the mime type ...
                 String mimeType = null;
                 InputStream contents = null;
@@ -319,11 +290,25 @@
                     }
                 }
 
+                // First add any custom properties ...
+                request.addProperties(customPropertiesFactory.getResourceProperties(context, location, file, mimeType));
+
+                // The request is to get properties of the "jcr:content" child node ...
+                // ... use the dna:resource node type. This is the same as nt:resource, but is not referenceable
+                // since we cannot assume that we control all access to this file and can track its movements
+                request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.RESOURCE));
+                request.addProperty(factory.create(JcrLexicon.LAST_MODIFIED, dateFactory.create(file.lastModified())));
+                // Don't really know the encoding, either ...
+                // request.addProperty(factory.create(JcrLexicon.ENCODED, stringFactory.create("UTF-8")));
+
                 // Now put the file's content into the "jcr:data" property ...
-                BinaryFactory binaryFactory = getExecutionContext().getValueFactories().getBinaryFactory();
+                BinaryFactory binaryFactory = context.getValueFactories().getBinaryFactory();
                 request.addProperty(factory.create(JcrLexicon.DATA, binaryFactory.create(file)));
 
             } else {
+                // First add any custom properties ...
+                request.addProperties(customPropertiesFactory.getFileProperties(context, location, file));
+
                 // The request is to get properties for the node representing the file
                 request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE));
                 request.addProperty(factory.create(JcrLexicon.CREATED, dateFactory.create(file.lastModified())));
@@ -367,8 +352,9 @@
         Property primaryTypeProp = properties.get(JcrLexicon.PRIMARY_TYPE);
         Name primaryType = primaryTypeProp == null ? null : nameFactory().create(primaryTypeProp.getFirstValue());
 
+        Path newPath = pathFactory().create(parentPath, request.named());
+        Location actualLocation = Location.create(newPath);
         if (JcrNtLexicon.FILE.equals(primaryType)) {
-            ensureValidProperties(request.properties(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
 
             // The FILE node is represented by the existence of the file
             if (!parent.canWrite()) {
@@ -410,8 +396,12 @@
                                                                                          ioe.getMessage()), ioe));
                 return;
             }
+            customPropertiesFactory.recordFileProperties(getExecutionContext(),
+                                                         getSourceName(),
+                                                         actualLocation,
+                                                         newFile,
+                                                         properties);
         } else if (JcrNtLexicon.RESOURCE.equals(primaryType) || DnaLexicon.RESOURCE.equals(primaryType)) {
-            ensureValidProperties(request.properties(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
             if (!JcrLexicon.CONTENT.equals(request.named())) {
                 I18n msg = FileSystemI18n.invalidNameForResource;
                 String nodeName = request.named().getString(registry);
@@ -526,9 +516,13 @@
                     }
                 }
             }
+            customPropertiesFactory.recordResourceProperties(getExecutionContext(),
+                                                             getSourceName(),
+                                                             actualLocation,
+                                                             newFile,
+                                                             properties);
 
         } else if (JcrNtLexicon.FOLDER.equals(primaryType) || primaryType == null) {
-            ensureValidProperties(request.properties(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
             ensureValidPathLength(newFile);
 
             if (!newFile.mkdir()) {
@@ -541,6 +535,11 @@
                                                                         primaryType == null ? "null" : primaryType.getString(registry))));
                 return;
             }
+            customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
+                                                              getSourceName(),
+                                                              actualLocation,
+                                                              newFile,
+                                                              properties);
         } else {
             // Set error and return
             I18n msg = FileSystemI18n.unsupportedPrimaryType;
@@ -551,8 +550,7 @@
             return;
         }
 
-        Path newPath = pathFactory().create(parentPath, request.named());
-        request.setActualLocationOfNode(Location.create(newPath));
+        request.setActualLocationOfNode(actualLocation);
     }
 
     /**
@@ -564,8 +562,9 @@
     public void process( UpdatePropertiesRequest request ) {
         if (!updatesAllowed(request)) return;
 
+        Path path = request.on().getPath();
         File workspace = getWorkspaceDirectory(request.inWorkspace());
-        File target = getExistingFileFor(workspace, request.on().getPath(), request.on(), request);
+        File target = getExistingFileFor(workspace, path, request.on(), request);
 
         if (!target.exists()) {
             // getExistingFile fills in the PathNotFoundException for non-existent files
@@ -573,13 +572,31 @@
             return;
         }
 
+        Location location = request.on();
         if (target.isFile()) {
-            ensureValidProperties(request.properties().values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
+            if (path.endsWith(JcrLexicon.CONTENT)) {
+                customPropertiesFactory.recordResourceProperties(getExecutionContext(),
+                                                                 getSourceName(),
+                                                                 location,
+                                                                 target,
+                                                                 request.properties());
+            } else {
+                customPropertiesFactory.recordFileProperties(getExecutionContext(),
+                                                             getSourceName(),
+                                                             location,
+                                                             target,
+                                                             request.properties());
+            }
         } else {
-            ensureValidProperties(request.properties().values(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
+            assert target.isDirectory();
+            customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
+                                                              getSourceName(),
+                                                              location,
+                                                              target,
+                                                              request.properties());
         }
 
-        request.setActualLocationOfNode(request.on());
+        request.setActualLocationOfNode(location);
     }
 
     /**
@@ -996,32 +1013,6 @@
         return getExecutionContext().getValueFactories().getUuidFactory();
     }
 
-    /**
-     * Checks that the collection of {@code properties} only contains properties with allowable names.
-     * 
-     * @param properties
-     * @param validPropertyNames
-     * @throws RepositorySourceException if {@code properties} contains a
-     * @see #ALLOWABLE_PROPERTIES_FOR_CONTENT
-     * @see #ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER
-     */
-    protected void ensureValidProperties( Collection<Property> properties,
-                                          Set<Name> validPropertyNames ) {
-        List<String> invalidNames = new LinkedList<String>();
-        NamespaceRegistry registry = getExecutionContext().getNamespaceRegistry();
-
-        for (Property property : properties) {
-            if (!validPropertyNames.contains(property.getName())) {
-                invalidNames.add(property.getName().getString(registry));
-            }
-        }
-
-        if (!invalidNames.isEmpty()) {
-            throw new RepositorySourceException(this.getSourceName(),
-                                                FileSystemI18n.invalidPropertyNames.text(invalidNames.toString()));
-        }
-    }
-
     protected void ensureValidPathLength( File root ) {
         ensureValidPathLength(root, 0);
     }

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java	2009-11-17 18:47:00 UTC (rev 1324)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java	2009-11-17 21:09:49 UTC (rev 1325)
@@ -24,16 +24,28 @@
 
 package org.jboss.dna.connector.filesystem;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
+import javax.naming.BinaryRefAddr;
 import javax.naming.Context;
 import javax.naming.RefAddr;
 import javax.naming.Reference;
@@ -45,12 +57,19 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.DnaIntLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.cache.CachePolicy;
 import org.jboss.dna.graph.connector.RepositoryConnection;
 import org.jboss.dna.graph.connector.RepositoryContext;
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Property;
 
 /**
  * The {@link RepositorySource} for the connector that exposes an area of the local file system as content in a repository. This
@@ -61,6 +80,12 @@
 public class FileSystemSource implements RepositorySource, ObjectFactory {
 
     /**
+     * An immutable {@link CustomPropertiesFactory} implementation that is used by default when none is provided. Note that this
+     * implementation does restrict the properties that can be placed on file, folder and resource nodes.
+     */
+    protected static CustomPropertiesFactory DEFAULT_PROPERTIES_FACTORY = new StandardPropertiesFactory();
+
+    /**
      * The first serialized version of this source. Version {@value} .
      */
     private static final long serialVersionUID = 1L;
@@ -79,7 +104,7 @@
     protected static final String ALLOW_CREATING_WORKSPACES = "allowCreatingWorkspaces";
     protected static final String MAX_PATH_LENGTH = "maxPathLength";
     protected static final String EXCLUSION_PATTERN = "exclusionPattern";
-    protected static final String ALLOW_UPDATES = "allowUpdates";
+    protected static final String CUSTOM_PROPERTY_FACTORY = "customPropertyFactory";
 
     /**
      * This source supports events.
@@ -125,6 +150,7 @@
                                                                                                   SUPPORTS_REFERENCES);
     private transient CachePolicy cachePolicy;
     private transient Map<String, File> availableWorkspaces;
+    private volatile CustomPropertiesFactory customPropertiesFactory;
 
     /**
      * 
@@ -404,6 +430,24 @@
     }
 
     /**
+     * Get the factory that is used to create custom properties on "nt:folder", "nt:file", and "nt:resource" nodes.
+     * 
+     * @return the factory, or null if no custom properties are to be created
+     */
+    public synchronized CustomPropertiesFactory getCustomPropertiesFactory() {
+        return customPropertiesFactory;
+    }
+
+    /**
+     * Set the factory that is used to create custom properties on "nt:folder", "nt:file", and "nt:resource" nodes.
+     * 
+     * @param customPropertiesFactory the factory reference, or null if no custom properties will be created
+     */
+    public synchronized void setCustomPropertiesFactory( CustomPropertiesFactory customPropertiesFactory ) {
+        this.customPropertiesFactory = customPropertiesFactory;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.connector.RepositorySource#initialize(org.jboss.dna.graph.connector.RepositoryContext)
@@ -435,6 +479,18 @@
         if (workspaceNames != null && workspaceNames.length != 0) {
             ref.add(new StringRefAddr(PREDEFINED_WORKSPACE_NAMES, StringUtil.combineLines(workspaceNames)));
         }
+        if (getCustomPropertiesFactory() != null) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            CustomPropertiesFactory factory = getCustomPropertiesFactory();
+            try {
+                ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(factory);
+                ref.add(new BinaryRefAddr(CUSTOM_PROPERTY_FACTORY, baos.toByteArray()));
+            } catch (IOException e) {
+                I18n msg = FileSystemI18n.errorSerializingCustomPropertiesFactory;
+                throw new RepositorySourceException(getName(), msg.text(factory.getClass().getName(), getName()), e);
+            }
+        }
         return ref;
     }
 
@@ -446,7 +502,7 @@
                                      Context nameCtx,
                                      Hashtable<?, ?> environment ) throws Exception {
         if (obj instanceof Reference) {
-            Map<String, String> values = new HashMap<String, String>();
+            Map<String, Object> values = new HashMap<String, Object>();
             Reference ref = (Reference)obj;
             Enumeration<?> en = ref.getAll();
             while (en.hasMoreElements()) {
@@ -455,17 +511,28 @@
                     String key = subref.getType();
                     Object value = subref.getContent();
                     if (value != null) values.put(key, value.toString());
+                } else if (subref instanceof BinaryRefAddr) {
+                    String key = subref.getType();
+                    Object value = subref.getContent();
+                    if (value instanceof byte[]) {
+                        // Deserialize ...
+                        ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value);
+                        ObjectInputStream ois = new ObjectInputStream(bais);
+                        value = ois.readObject();
+                        values.put(key, value);
+                    }
                 }
             }
-            String sourceName = values.get(SOURCE_NAME);
-            String cacheTtlInMillis = values.get(CACHE_TIME_TO_LIVE_IN_MILLISECONDS);
-            String retryLimit = values.get(RETRY_LIMIT);
-            String defaultWorkspace = values.get(DEFAULT_WORKSPACE);
-            String createWorkspaces = values.get(ALLOW_CREATING_WORKSPACES);
-            String exclusionPattern = values.get(DEFAULT_EXCLUSION_PATTERN);
-            String maxPathLength = values.get(DEFAULT_MAX_PATH_LENGTH);
+            String sourceName = (String)values.get(SOURCE_NAME);
+            String cacheTtlInMillis = (String)values.get(CACHE_TIME_TO_LIVE_IN_MILLISECONDS);
+            String retryLimit = (String)values.get(RETRY_LIMIT);
+            String defaultWorkspace = (String)values.get(DEFAULT_WORKSPACE);
+            String createWorkspaces = (String)values.get(ALLOW_CREATING_WORKSPACES);
+            String exclusionPattern = (String)values.get(EXCLUSION_PATTERN);
+            String maxPathLength = (String)values.get(DEFAULT_MAX_PATH_LENGTH);
+            Object customPropertiesFactory = values.get(CUSTOM_PROPERTY_FACTORY);
 
-            String combinedWorkspaceNames = values.get(PREDEFINED_WORKSPACE_NAMES);
+            String combinedWorkspaceNames = (String)values.get(PREDEFINED_WORKSPACE_NAMES);
             String[] workspaceNames = null;
             if (combinedWorkspaceNames != null) {
                 List<String> paths = StringUtil.splitLines(combinedWorkspaceNames);
@@ -482,6 +549,7 @@
             if (workspaceNames != null && workspaceNames.length != 0) source.setPredefinedWorkspaceNames(workspaceNames);
             if (exclusionPattern != null) source.setExclusionPattern(exclusionPattern);
             if (maxPathLength != null) source.setMaxPathLength(Integer.valueOf(maxPathLength));
+            if (customPropertiesFactory != null) source.setCustomPropertiesFactory((CustomPropertiesFactory)customPropertiesFactory);
             return source;
         }
         return null;
@@ -562,9 +630,10 @@
             };
         }
 
+        CustomPropertiesFactory propFactory = customPropertiesFactory != null ? customPropertiesFactory : DEFAULT_PROPERTIES_FACTORY;
         return new FileSystemConnection(name, defaultWorkspaceName, availableWorkspaces, isCreatingWorkspacesAllowed(),
                                         cachePolicy, rootNodeUuid, workspaceRootPath, maxPathLength, filenameFilter,
-                                        getSupportsUpdates());
+                                        getSupportsUpdates(), propFactory);
     }
 
     /**
@@ -576,6 +645,127 @@
         this.availableWorkspaces = null;
     }
 
+    protected static class StandardPropertiesFactory implements CustomPropertiesFactory {
+        private static final long serialVersionUID = 1L;
+        private final Collection<Property> empty = Collections.emptyList();
+
+        /**
+         * Only certain properties are tolerated when writing content (dna:resource or jcr:resource) nodes. These properties are
+         * implicitly stored (primary type, data) or silently ignored (encoded, mimetype, last modified). The silently ignored
+         * properties must be accepted to stay compatible with the JCR specification.
+         */
+        private final Set<Name> ALLOWABLE_PROPERTIES_FOR_CONTENT = Collections.unmodifiableSet(new HashSet<Name>(
+                                                                                                                 Arrays.asList(new Name[] {
+                                                                                                                     JcrLexicon.PRIMARY_TYPE,
+                                                                                                                     JcrLexicon.DATA,
+                                                                                                                     JcrLexicon.ENCODED,
+                                                                                                                     JcrLexicon.MIMETYPE,
+                                                                                                                     JcrLexicon.LAST_MODIFIED,
+                                                                                                                     JcrLexicon.UUID,
+                                                                                                                     DnaIntLexicon.NODE_DEFINITON})));
+        /**
+         * Only certain properties are tolerated when writing files (nt:file) or folders (nt:folder) nodes. These properties are
+         * implicitly stored in the file or folder (primary type, created).
+         */
+        private final Set<Name> ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER = Collections.unmodifiableSet(new HashSet<Name>(
+                                                                                                                        Arrays.asList(new Name[] {
+                                                                                                                            JcrLexicon.PRIMARY_TYPE,
+                                                                                                                            JcrLexicon.CREATED,
+                                                                                                                            JcrLexicon.UUID,
+                                                                                                                            DnaIntLexicon.NODE_DEFINITON})));
+
+        public Collection<Property> getDirectoryProperties( ExecutionContext context,
+                                                            Location location,
+                                                            File directory ) {
+            return empty;
+        }
+
+        public Collection<Property> getFileProperties( ExecutionContext context,
+                                                       Location location,
+                                                       File file ) {
+            return empty;
+        }
+
+        public Collection<Property> getResourceProperties( ExecutionContext context,
+                                                           Location location,
+                                                           File file,
+                                                           String mimeType ) {
+            return empty;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordDirectoryProperties(org.jboss.dna.graph.ExecutionContext,
+         *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
+         */
+        public void recordDirectoryProperties( ExecutionContext context,
+                                               String sourceName,
+                                               Location location,
+                                               File file,
+                                               Map<Name, Property> properties ) throws RepositorySourceException {
+            ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordFileProperties(org.jboss.dna.graph.ExecutionContext,
+         *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
+         */
+        public void recordFileProperties( ExecutionContext context,
+                                          String sourceName,
+                                          Location location,
+                                          File file,
+                                          Map<Name, Property> properties ) throws RepositorySourceException {
+            ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordResourceProperties(org.jboss.dna.graph.ExecutionContext,
+         *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
+         */
+        public void recordResourceProperties( ExecutionContext context,
+                                              String sourceName,
+                                              Location location,
+                                              File file,
+                                              Map<Name, Property> properties ) throws RepositorySourceException {
+            ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
+        }
+
+        /**
+         * Checks that the collection of {@code properties} only contains properties with allowable names.
+         * 
+         * @param context
+         * @param sourceName
+         * @param properties
+         * @param validPropertyNames
+         * @throws RepositorySourceException if {@code properties} contains a
+         * @see #ALLOWABLE_PROPERTIES_FOR_CONTENT
+         * @see #ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER
+         */
+        protected void ensureValidProperties( ExecutionContext context,
+                                              String sourceName,
+                                              Collection<Property> properties,
+                                              Set<Name> validPropertyNames ) {
+            List<String> invalidNames = new LinkedList<String>();
+            NamespaceRegistry registry = context.getNamespaceRegistry();
+
+            for (Property property : properties) {
+                if (!validPropertyNames.contains(property.getName())) {
+                    invalidNames.add(property.getName().getString(registry));
+                }
+            }
+
+            if (!invalidNames.isEmpty()) {
+                throw new RepositorySourceException(sourceName, FileSystemI18n.invalidPropertyNames.text(invalidNames.toString()));
+            }
+        }
+
+    }
+
     @Immutable
     /*package*/class FileSystemCachePolicy implements CachePolicy {
         private static final long serialVersionUID = 1L;

Modified: trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties	2009-11-17 18:47:00 UTC (rev 1324)
+++ trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties	2009-11-17 21:09:49 UTC (rev 1325)
@@ -39,6 +39,7 @@
 onlyTheDefaultNamespaceIsAllowed = {0} requires node names use the default namespace: {1}
 sourceIsReadOnly = The source "{0}" does not allow updates
 pathIsReadOnly = The path "{0}" in workspace "{1}" in {2} cannot be written to.  See java.io.File\#canWrite().
+errorSerializingCustomPropertiesFactory = Error serializing a {0} instance owned by the {1} FileSystemSource
 
 # Writable tests
 parentIsReadOnly = The parent node at path "{0}" in workspace "{1}" in {2} cannot be written to.  See java.io.File\#canWrite().


From dna-commits at lists.jboss.org  Tue Nov 17 18:21:38 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Nov 2009 18:21:38 -0500
Subject: [dna-commits] DNA SVN: r1326 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse.
Message-ID: <200911172321.nAHNLcWi008573@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-17 18:21:38 -0500 (Tue, 17 Nov 2009)
New Revision: 1326

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
Log:
DNA-467 Documented the SQL and full-text search grammars in the JavaDoc for each parser.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java	2009-11-17 21:09:49 UTC (rev 1325)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java	2009-11-17 23:21:38 UTC (rev 1326)
@@ -38,6 +38,24 @@
 /**
  * A {@link QueryParser} implementation that parses a full-text search expression. This grammar is based on the full-text search
  * grammar as defined by the JCR 2.0 specification.
+ * <p>
+ * </p>
+ * <h3>Grammar</h3>
+ * <p>
+ * The grammar for the full-text expression is taken from the JCR 2.0 specification, and is as follows:
+ * </p>
+ * 
+ * <pre>
+ * FulltextSearch ::= Disjunct {Space 'OR' Space Disjunct}
+ * Disjunct ::= Term {Space Term}
+ * Term ::= ['-'] SimpleTerm
+ * SimpleTerm ::= Word | '&quot;' Word {Space Word} '&quot;'
+ * Word ::= NonSpaceChar {NonSpaceChar}
+ * Space ::= SpaceChar {SpaceChar}
+ * NonSpaceChar ::= Char - SpaceChar /* Any Char except SpaceChar &#42;/
+ * SpaceChar ::= ' '
+ * Char ::= /* Any character &#42;/
+ * </pre>
  */
 public class FullTextSearchParser {
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-17 21:09:49 UTC (rev 1325)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-17 23:21:38 UTC (rev 1326)
@@ -97,13 +97,342 @@
 /**
  * A {@link QueryParser} implementation that parses a subset of SQL select and set queries.
  * <p>
- * This grammar is based on the SQL grammar as defined by the JCR 2.0 specification, with some useful additions:
+ * This grammar is equivalent to the SQL grammar as defined by the JCR 2.0 specification, with some useful additions:
  * <ul>
- * <li>(UNION|INTERSECT|EXCEPT) [ALL]</li>
- * <li>SELECT DISTINCT</li>
- * <li>LIMIT clauses</li>
+ * <li>"<code>... (UNION|INTERSECT|EXCEPT) [ALL] ...</code>" to combine and merge results from multiple queries</li>
+ * <li>"<code>SELECT DISTINCT ...</code>" to remove duplicates</li>
+ * <li>"<code>LIMIT count [OFFSET number]</code>" clauses to control the number of results returned as well as the number of rows
+ * that should be skipped</li>
+ * <li>Support for additional join types, including "<code>FULL OUTER JOIN</code>" and "<code>CROSS JOIN</code>"</li>
+ * <li>Additional dynamic operands "<code>DEPTH([&lt;selectorName>])</code>" and "<code>PATH([&lt;selectorName>])</code>" that
+ * enables placing constraints on the node depth and path, respectively, and which can be used in a manner similar to "
+ * <code>NAME([&lt;selectorName>])</code>" and "<code>LOCALNAME([&lt;selectorName>])</code>. Note in each of these cases, the
+ * selector name is optional if there is only one selector in the query. on the node depth</li>
+ * <li>Support for the IN clause and NOT IN clause to more easily supply a list of valid discrete static operands: "
+ * <code>&lt;dynamicOperand> [NOT] IN (&lt;staticOperand> {, &lt;staticOperand>})</code>"</li>
+ * <li>Support for the BETWEEN clause: "<code>&lt;dynamicOperand> [NOT] BETWEEN &lt;lowerBoundStaticOperand> [EXCLUSIVE] AND
+ * &lt;upperBoundStaticOperand> [EXCLUSIVE]</code>"</i>
  * </ul>
  * </p>
+ * <h3>SQL grammar</h3>
+ * <p>
+ * This section defines the complete grammar for the SQL dialect supported by this parser.
+ * </p>
+ * <h4>Queries</h4>
+ * 
+ * <pre>
+ * QueryCommand ::= Query | SetQuery
+ * 
+ * SetQuery ::= Query ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query
+ *                  { ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query }
+ * 
+ * Query ::= 'SELECT' ['DISINCT'] columns
+ *           'FROM' Source
+ *           ['WHERE' Constraint]
+ *           ['ORDER BY' orderings]
+ *           [Limit]
+ * </pre>
+ * 
+ * <h4>Sources</h4>
+ * 
+ * <pre>
+ * Source ::= Selector | Join
+ * 
+ * Selector ::= nodeTypeName ['AS' selectorName]
+ * 
+ * nodeTypeName ::= Name
+ * </pre>
+ * 
+ * <h4>Joins</h4>
+ * 
+ * <pre>
+ * Join ::= left [JoinType] 'JOIN' right 'ON' JoinCondition
+ *          // If JoinType is omitted INNER is assumed.
+ *          
+ * left ::= Source
+ * right ::= Source
+ * 
+ * JoinType ::= Inner | LeftOuter | RightOuter | FullOuter | Cross
+ * 
+ * Inner ::= 'INNER' ['JOIN']
+ * 
+ * LeftOuter ::= 'LEFT JOIN' | 'OUTER JOIN' | 'LEFT OUTER JOIN'
+ * 
+ * RightOuter ::= 'RIGHT OUTER' ['JOIN']
+ * 
+ * RightOuter ::= 'FULL OUTER' ['JOIN']
+ * 
+ * RightOuter ::= 'CROSS' ['JOIN']
+ * 
+ * JoinCondition ::= EquiJoinCondition | SameNodeJoinCondition | ChildNodeJoinCondition | DescendantNodeJoinCondition
+ * </pre>
+ * 
+ * <h5>Equi-join conditions</h5>
+ * 
+ * <pre>
+ * EquiJoinCondition ::= selector1Name'.'property1Name '=' selector2Name'.'property2Name
+ * 
+ * selector1Name ::= selectorName
+ * selector2Name ::= selectorName
+ * property1Name ::= propertyName
+ * property2Name ::= propertyName
+ * </pre>
+ * 
+ * <h5>Same-node join condition</h5>
+ * 
+ * <pre>
+ * SameNodeJoinCondition ::= 'ISSAMENODE(' selector1Name ',' selector2Name [',' selector2Path] ')'
+ * 
+ * selector2Path ::= Path
+ * </pre>
+ * 
+ * <h5>Child-node join condition</h5>
+ * 
+ * <pre>
+ * ChildNodeJoinCondition ::= 'ISCHILDNODE(' childSelectorName ',' parentSelectorName ')'
+ * 
+ * childSelectorName ::= selectorName
+ * parentSelectorName ::= selectorName
+ * </pre>
+ * 
+ * <h5>Descendant-node join condition</h5>
+ * 
+ * <pre>
+ * DescendantNodeJoinCondition ::= 'ISDESCENDANTNODE(' descendantSelectorName ',' ancestorSelectorName ')'
+ * descendantSelectorName ::= selectorName
+ * ancestorSelectorName ::= selectorName
+ * </pre>
+ * 
+ * <h4>Constraints</h4>
+ * 
+ * <pre>
+ * Constraint ::= ConstraintItem | '(' ConstraintItem ')'
+ * 
+ * ConstraintItem ::= And | Or | Not | Comparison | Between | PropertyExistence | SetConstraint | FullTextSearch | 
+ *                    SameNode | ChildNode | DescendantNode
+ * </pre>
+ * 
+ * <h5>And constraint</h5>
+ * 
+ * <pre>
+ * And ::= constraint1 'AND' constraint2
+ * 
+ * constraint1 ::= Constraint
+ * constraint2 ::= Constraint
+ * </pre>
+ * 
+ * <h5>Or constraint</h5>
+ * 
+ * <pre>
+ * Or ::= constraint1 'OR' constraint2
+ * </pre>
+ * 
+ * <h5>Not constraint</h5>
+ * 
+ * <pre>
+ * Not ::= 'NOT' Constraint
+ * </pre>
+ * 
+ * <h5>Comparison constraint</h5>
+ * 
+ * <pre>
+ * Comparison ::= DynamicOperand Operator StaticOperand
+ * 
+ * Operator ::= '=' | '!=' | '<' | '<=' | '>' | '>=' | 'LIKE'
+ * </pre>
+ * 
+ * <h5>Between constraint</h5>
+ * 
+ * <pre>
+ * Between ::= DynamicOperand ['NOT'] 'BETWEEN' lowerBound ['EXCLUSIVE'] 'AND' upperBound ['EXCLUSIVE']
+ * 
+ * lowerBound ::= StaticOperand
+ * upperBound ::= StaticOperand
+ * </pre>
+ * 
+ * <h5>Property existence constraint</h5>
+ * 
+ * <pre>
+ * PropertyExistence ::= selectorName'.'propertyName 'IS' ['NOT'] 'NULL' | 
+ *                       propertyName 'IS' ['NOT'] 'NULL' &#47;* If only one selector exists in this query *&#47;
+ * 
+ * </pre>
+ * 
+ * <h5>Set constraint</h5>
+ * 
+ * <pre>
+ * SetConstraint ::= selectorName'.'propertyName ['NOT'] 'IN' | 
+ *                       propertyName ['NOT'] 'IN' &#47;* If only one selector exists in this query *&#47;
+ *                       '(' firstStaticOperand {',' additionalStaticOperand } ')'
+ * firstStaticOperand ::= StaticOperand
+ * additionalStaticOperand ::= StaticOperand
+ * </pre>
+ * 
+ * <h5>Full-text search constraint</h5>
+ * 
+ * <pre>
+ * FullTextSearch ::= 'CONTAINS(' ([selectorName'.']propertyName | selectorName'.*') 
+ *                            ',' ''' fullTextSearchExpression''' ')'
+ *                    &#47;* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * fullTextSearchExpression ::= &#47;* a full-text search expression, see {@link FullTextSearchParser} *&#47;
+ * </pre>
+ * 
+ * <h5>Same-node constraint</h5>
+ * 
+ * <pre>
+ * SameNode ::= 'ISSAMENODE(' [selectorName ','] Path ')' 
+ *                    &#47;* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * </pre>
+ * 
+ * <h5>Child-node constraint</h5>
+ * 
+ * <pre>
+ * ChildNode ::= 'ISCHILDNODE(' [selectorName ','] Path ')' 
+ *                    &#47;* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * </pre>
+ * 
+ * <h5>Descendant-node constraint</h5>
+ * 
+ * <pre>
+ * DescendantNode ::= 'ISDESCENDANTNODE(' [selectorName ','] Path ')' 
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * </pre>
+ * 
+ * <h5>Paths and names</h5>
+ * 
+ * <pre>
+ * 
+ * Name ::= '[' quotedName ']' | '[' simpleName ']' | simpleName
+ * 
+ * quotedName ::= /* A JCR Name (see the JCR specification) *&#47;
+ * simpleName ::= /* A JCR Name that contains only SQL-legal characters (namely letters, digits, and underscore) *&#47;
+ *
+ * Path ::= '[' quotedPath ']' | '[' simplePath ']' | simplePath
+ *
+ * quotedPath ::= /* A JCR Path that contains non-SQL-legal characters *&#47;
+ * simplePath ::= /* A JCR Path (rather Name) that contains only SQL-legal characters (namely letters, digits, and underscore) *&#47;
+ * </pre>
+ * 
+ * <h4>Static operands</h4>
+ * 
+ * <pre>
+ * StaticOperand ::= Literal | BindVariableValue
+ * </pre>
+ * 
+ * <h5>Literal</h5>
+ * 
+ * <pre>
+ * Literal ::= CastLiteral | UncastLiteral
+ * 
+ * CastLiteral ::= 'CAST(' UncastLiteral ' AS ' PropertyType ')'
+ * 
+ * PropertyType ::= 'STRING' | 'BINARY' | 'DATE' | 'LONG' | 'DOUBLE' | 'DECIMAL' | 'BOOLEAN' | 'NAME' | 'PATH' | 
+ *                  'REFERENCE' | 'WEAKREFERENCE' | 'URI'
+ *                  /* 'WEAKREFERENCE' is not currently supported in JCR 1.0 *&#47;
+ *                  
+ * UncastLiteral ::= UnquotedLiteral | ''' UnquotedLiteral ''' | '"' UnquotedLiteral '"'
+ * 
+ * UnquotedLiteral ::= /* String form of a JCR Value, as defined in the JCR specification *&#47;
+ * </pre>
+ * 
+ * <h5>Bind variables</h5>
+ * 
+ * <pre>
+ * BindVariableValue ::= '$'bindVariableName
+ * 
+ * bindVariableName ::= /* A string that conforms to the JCR Name syntax, though the prefix does not need to be
+ *                         a registered namespace prefix. *&#47;
+ * </pre>
+ * 
+ * <h4>Dynamic operands</h4>
+ * 
+ * <pre>
+ * DynamicOperand ::= PropertyValue | Length | NodeName | NodeLocalName | NodePath | NodeDepth | 
+ *                    FullTextSearchScore | LowerCase | UpperCase
+ * </pre>
+ * <h5>Property value</h5>
+ * <pre>
+ * PropertyValue ::= [selectorName'.'] propertyName
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * </pre>
+ * <h5>Property length</h5>
+ * <pre>
+ * Length ::= 'LENGTH(' PropertyValue ')'
+ * </pre>
+ * <h5>Node name</h5>
+ * <pre>
+ * NodeName ::= 'NAME(' [selectorName] ')'
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       is optional *&#47;
+ * </pre>
+ * <h5>Node local name</h5>
+ * <pre>
+ * NodeLocalName ::= 'LOCALNAME(' [selectorName] ')'
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       is optional *&#47;
+ * </pre>
+ * <h5>Node path</h5>
+ * <pre>
+ * NodePath ::= 'PATH(' [selectorName] ')'
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       is optional *&#47;
+ * </pre>
+ * <h5>Node depth</h5>
+ * <pre>
+ * NodeDepth ::= 'DEPTH(' [selectorName] ')'
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       is optional *&#47;
+ * </pre>
+ * <h5>Full-text search score</h5>
+ * <pre>
+ * FullTextSearchScore ::= 'SCORE(' [selectorName] ')'
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       is optional *&#47;
+ * </pre>
+ * <h5>Lowercase</h5>
+ * <pre>
+ * LowerCase ::= 'LOWER(' DynamicOperand ')'
+ * </pre>
+ * <h5>Uppercase</h5>
+ * <pre>
+ * UpperCase ::= 'UPPER(' DynamicOperand ')'
+ * </pre>
+ * 
+ * <h4>Ordering</h4>
+ * 
+ * <pre>
+ * orderings ::= Ordering {',' Ordering}
+ * 
+ * Ordering ::= DynamicOperand [Order]
+ * 
+ * Order ::= 'ASC' | 'DESC'
+ * </pre>
+ * 
+ * <h4>Columns</h4>
+ * 
+ * <pre>
+ * columns ::= (Column ',' {Column}) | '*'
+ * 
+ * Column ::= ([selectorName'.']propertyName ['AS' columnName]) | (selectorName'.*')
+ *                    /* If only one selector exists in this query, explicit specification of the selectorName
+ *                       preceding the propertyName is optional *&#47;
+ * selectorName ::= Name
+ * propertyName ::= Name
+ * columnName ::= Name
+ * </pre>
+ * 
+ * <h4>Limit</h4>
+ * 
+ * <pre>
+ * Limit ::= 'LIMIT' count [ 'OFFSET' offset ]
+ * count ::= /* Positive integer value *&#47;
+ * offset ::= /* Non-negative integer value *&#47;
+ * </pre>
  */
 public class SqlQueryParser implements QueryParser {
 
@@ -255,11 +584,11 @@
             } else if (tokens.canConsume("OUTER", "JOIN") || tokens.canConsume("LEFT", "JOIN")
                        || tokens.canConsume("LEFT", "OUTER", "JOIN")) {
                 joinType = JoinType.LEFT_OUTER;
-            } else if (tokens.canConsume("RIGHT", "OUTER", "JOIN")) {
+            } else if (tokens.canConsume("RIGHT", "OUTER", "JOIN") || tokens.canConsume("RIGHT", "OUTER")) {
                 joinType = JoinType.RIGHT_OUTER;
-            } else if (tokens.canConsume("FULL", "OUTER", "JOIN")) {
+            } else if (tokens.canConsume("FULL", "OUTER", "JOIN") || tokens.canConsume("FULL", "OUTER")) {
                 joinType = JoinType.FULL_OUTER;
-            } else if (tokens.canConsume("CROSS", "JOIN")) {
+            } else if (tokens.canConsume("CROSS", "JOIN") || tokens.canConsume("CROSS")) {
                 joinType = JoinType.CROSS;
             }
             if (joinType == null) break;


From dna-commits at lists.jboss.org  Tue Nov 17 18:30:25 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Nov 2009 18:30:25 -0500
Subject: [dna-commits] DNA SVN: r1327 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse.
Message-ID: <200911172330.nAHNUPq2010716@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-17 18:30:25 -0500 (Tue, 17 Nov 2009)
New Revision: 1327

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
Log:
DNA-467 Factored out a parse method to allow subclasses to parse tokenized query commands.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-17 23:21:38 UTC (rev 1326)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-17 23:30:25 UTC (rev 1327)
@@ -482,6 +482,11 @@
         Tokenizer tokenizer = new SqlTokenizer(false);
         TokenStream tokens = new TokenStream(query, tokenizer, false);
         tokens.start();
+        return parseQueryCommand(tokens, context);
+    }
+
+    protected QueryCommand parseQueryCommand( TokenStream tokens,
+                                              ExecutionContext context ) {
         QueryCommand command = null;
         if (tokens.matches("SELECT")) {
             command = parseQuery(tokens, context);


From dna-commits at lists.jboss.org  Wed Nov 18 14:37:57 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Nov 2009 14:37:57 -0500
Subject: [dna-commits] DNA SVN: r1328 - in
	trunk/dna-search/src/main/java/org/jboss/dna/search: query
	and 1 other directory.
Message-ID: <200911181937.nAIJbvAg015797@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-18 14:37:57 -0500 (Wed, 18 Nov 2009)
New Revision: 1328

Added:
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareLengthQuery.java
Modified:
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
Log:
DNA-467 Added query to constrain the length of a field.

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-17 23:30:25 UTC (rev 1327)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-18 19:37:57 UTC (rev 1328)
@@ -87,6 +87,7 @@
 import org.jboss.dna.graph.query.QueryEngine;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Length;
 import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
@@ -106,6 +107,7 @@
 import org.jboss.dna.graph.query.process.QueryProcessor;
 import org.jboss.dna.graph.request.ChangeRequest;
 import org.jboss.dna.search.IndexRules.Rule;
+import org.jboss.dna.search.query.CompareLengthQuery;
 import org.jboss.dna.search.query.CompareNameQuery;
 import org.jboss.dna.search.query.ComparePathQuery;
 import org.jboss.dna.search.query.CompareStringQuery;
@@ -958,6 +960,36 @@
             return query;
         }
 
+        protected Query findNodesWith( Length propertyLength,
+                                       Operator operator,
+                                       Object value ) {
+            assert propertyLength != null;
+            assert value != null;
+            PropertyValue propertyValue = propertyLength.getPropertyValue();
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            ValueFactories factories = context.getValueFactories();
+            int length = factories.getLongFactory().create(value).intValue();
+            switch (operator) {
+                case EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldEqualTo(length, field, factories);
+                case NOT_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldNotEqualTo(length, field, factories);
+                case GREATER_THAN:
+                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThan(length, field, factories);
+                case GREATER_THAN_OR_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(length, field, factories);
+                case LESS_THAN:
+                    return CompareLengthQuery.createQueryForNodesWithFieldLessThan(length, field, factories);
+                case LESS_THAN_OR_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldLessThanOrEqualTo(length, field, factories);
+                case LIKE:
+                    // This is not allowed ...
+                    assert false;
+                    break;
+            }
+            return null;
+        }
+
         protected Query findNodesWith( PropertyValue propertyValue,
                                        Operator operator,
                                        Object value,
@@ -1111,7 +1143,6 @@
                     return null;
             }
             return null;
-
         }
 
         protected Query findNodesWithNumericRange( PropertyValue propertyValue,

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-17 23:30:25 UTC (rev 1327)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-18 19:37:57 UTC (rev 1328)
@@ -449,10 +449,7 @@
             } else if (left instanceof PropertyValue) {
                 return session.findNodesWith((PropertyValue)left, operator, value, caseSensitive);
             } else if (left instanceof Length) {
-                Length length = (Length)left;
-                PropertyValue nested = length.getPropertyValue();
-
-                return null;
+                return session.findNodesWith((Length)left, operator, right);
             } else if (left instanceof LowerCase) {
                 LowerCase lowercase = (LowerCase)left;
                 return createQuery(lowercase.getOperand(), operator, right, false);

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareLengthQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareLengthQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareLengthQuery.java	2009-11-18 19:37:57 UTC (rev 1328)
@@ -0,0 +1,254 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Weight;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.model.Length;
+
+/**
+ * A Lucene {@link Query} implementation that is used to apply a {@link Length} constraint against a string field. This query
+ * implementation works by using the {@link Query#weight(Searcher) weight} and
+ * {@link Weight#scorer(IndexReader, boolean, boolean) scorer} of the wrapped query to score (and return) only those documents
+ * with string fields that satisfy the constraint.
+ */
+public class CompareLengthQuery extends CompareQuery<Integer> {
+
+    private static final long serialVersionUID = 1L;
+    protected static final Evaluator<Integer> EQUAL_TO = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue == length;
+        }
+
+        @Override
+        public String toString() {
+            return " = ";
+        }
+    };
+    protected static final Evaluator<Integer> NOT_EQUAL_TO = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue == length;
+        }
+
+        @Override
+        public String toString() {
+            return " != ";
+        }
+    };
+    protected static final Evaluator<Integer> IS_LESS_THAN = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue < length;
+        }
+
+        @Override
+        public String toString() {
+            return " < ";
+        }
+    };
+    protected static final Evaluator<Integer> IS_LESS_THAN_OR_EQUAL_TO = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue < length;
+        }
+
+        @Override
+        public String toString() {
+            return " <= ";
+        }
+    };
+    protected static final Evaluator<Integer> IS_GREATER_THAN = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue < length;
+        }
+
+        @Override
+        public String toString() {
+            return " > ";
+        }
+    };
+    protected static final Evaluator<Integer> IS_GREATER_THAN_OR_EQUAL_TO = new Evaluator<Integer>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( Integer nodeValue,
+                                            Integer length ) {
+            return nodeValue < length;
+        }
+
+        @Override
+        public String toString() {
+            return " >= ";
+        }
+    };
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is equal to the supplied constraint
+     * value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldEqualTo( Integer constraintValue,
+                                                                          String fieldName,
+                                                                          ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_GREATER_THAN);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is not equal to the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldNotEqualTo( Integer constraintValue,
+                                                                             String fieldName,
+                                                                             ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_GREATER_THAN);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is greater than the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldGreaterThan( Integer constraintValue,
+                                                                              String fieldName,
+                                                                              ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_GREATER_THAN);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is greater than or equal to the
+     * supplied constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldGreaterThanOrEqualTo( Integer constraintValue,
+                                                                                       String fieldName,
+                                                                                       ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_GREATER_THAN_OR_EQUAL_TO);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is less than the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldLessThan( Integer constraintValue,
+                                                                           String fieldName,
+                                                                           ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_LESS_THAN);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores documents with a field length that is less than or equal to the
+     * supplied constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @return the query; never null
+     */
+    public static CompareLengthQuery createQueryForNodesWithFieldLessThanOrEqualTo( Integer constraintValue,
+                                                                                    String fieldName,
+                                                                                    ValueFactories factories ) {
+        return new CompareLengthQuery(fieldName, constraintValue, factories.getStringFactory(), IS_LESS_THAN_OR_EQUAL_TO);
+    }
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param constraintValue the constraint value; may not be null
+     * @param stringFactory the string factory that can be used during the scoring; may not be null
+     * @param evaluator the {@link CompareQuery.Evaluator} implementation that returns whether the node path satisfies the
+     *        constraint; may not be null
+     */
+    protected CompareLengthQuery( String fieldName,
+                                  Integer constraintValue,
+                                  ValueFactory<String> stringFactory,
+                                  Evaluator<Integer> evaluator ) {
+        super(fieldName, constraintValue, null, stringFactory, evaluator);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.query.CompareQuery#readFromDocument(org.apache.lucene.index.IndexReader, int)
+     */
+    @Override
+    protected Integer readFromDocument( IndexReader reader,
+                                        int docId ) throws IOException {
+        // This implementation reads the length of the field ...
+        Document doc = reader.document(docId, fieldSelector);
+        String valueString = doc.get(fieldName);
+        String value = stringFactory.create(valueString);
+        return value != null ? value.length() : 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#clone()
+     */
+    @Override
+    public Object clone() {
+        return new CompareLengthQuery(fieldName, constraintValue, stringFactory, evaluator);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareLengthQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Wed Nov 18 14:39:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Nov 2009 14:39:06 -0500
Subject: [dna-commits] DNA SVN: r1329 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/search and 7
	other directories.
Message-ID: <200911181939.nAIJd66E016019@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-18 14:39:05 -0500 (Wed, 18 Nov 2009)
New Revision: 1329

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngineException.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchException.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchProvider.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfiguration.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfigurations.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneException.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java
Removed:
   trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
   trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
Log:
DNA-467 refactored the search engine components, moving the general-purpose classes (i.e., those that don't depend on Lucene) into 'dna-graph'.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -160,6 +160,16 @@
     public static I18n expectingLiteralAndUnableToParseAsDouble;
     public static I18n expectingLiteralAndUnableToParseAsDate;
 
+    /* Search */
+    public static I18n errorWhileIndexingContentAtPath;
+    public static I18n errorWhileRemovingContentAtPath;
+    public static I18n errorWhileUpdatingContent;
+    public static I18n errorWhileCommittingIndexChanges;
+    public static I18n errorWhileOptimizingIndexes;
+    public static I18n errorWhilePerformingSearch;
+    public static I18n errorWhilePerformingQuery;
+    public static I18n errorWhileRemovingIndexesForWorkspace;
+
     static {
         try {
             I18n.initialize(GraphI18n.class);

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,807 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.search;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import net.jcip.annotations.GuardedBy;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.search.SearchProvider.Session;
+
+/**
+ * A component that acts as a search engine for the content within a single {@link RepositorySource}. This engine manages a set of
+ * indexes and provides search functionality for each of the workspaces within the source, and provides various methods to
+ * (re)index the content contained with source's workspaces and keep the indexes up-to-date via changes.
+ */
+ at ThreadSafe
+public class SearchEngine {
+
+    /**
+     * The default maximum number of changes that can be made to an index before the indexes are automatically optimized is * * *
+     * * {@value}
+     */
+    public static final int DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION = 0;
+
+    protected final ExecutionContext context;
+    private final String sourceName;
+    private final RepositoryConnectionFactory connectionFactory;
+    protected final SearchProvider indexLayout;
+    private final int maxChangesBeforeAutomaticOptimization;
+    @GuardedBy( "workspacesLock" )
+    private final Map<String, Workspace> workspacesByName = new HashMap<String, Workspace>();
+    private final ReadWriteLock workspacesLock = new ReentrantReadWriteLock();
+
+    /**
+     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
+     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
+     * {@link SearchProvider search provider}.
+     * 
+     * @param context the execution context for indexing and optimization operations
+     * @param sourceName the name of the {@link RepositorySource}
+     * @param connectionFactory the connection factory
+     * @param indexLayout the specification of the Lucene index layout
+     * @param maxChangesBeforeAutomaticOptimization the number of changes that can be made to the index before the indexes are
+     *        automatically optimized; may be 0 or a negative number if no automatic optimization should be done
+     * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
+     */
+    public SearchEngine( ExecutionContext context,
+                         String sourceName,
+                         RepositoryConnectionFactory connectionFactory,
+                         SearchProvider indexLayout,
+                         int maxChangesBeforeAutomaticOptimization ) {
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(sourceName, "sourceName");
+        CheckArg.isNotNull(connectionFactory, "connectionFactory");
+        this.sourceName = sourceName;
+        this.connectionFactory = connectionFactory;
+        this.indexLayout = indexLayout;
+        this.context = context;
+        this.maxChangesBeforeAutomaticOptimization = maxChangesBeforeAutomaticOptimization < 0 ? 0 : maxChangesBeforeAutomaticOptimization;
+    }
+
+    /**
+     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
+     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
+     * {@link SearchProvider search provider} that defines where each workspace's indexes should be placed.
+     * 
+     * @param context the execution context for indexing and optimization operations
+     * @param sourceName the name of the {@link RepositorySource}
+     * @param connectionFactory the connection factory
+     * @param indexLayout the specification of the Lucene index layout
+     * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
+     */
+    public SearchEngine( ExecutionContext context,
+                         String sourceName,
+                         RepositoryConnectionFactory connectionFactory,
+                         SearchProvider indexLayout ) {
+        this(context, sourceName, connectionFactory, indexLayout, DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION);
+    }
+
+    /**
+     * Get the name of the RepositorySource that this engine is to use.
+     * 
+     * @return the source name; never null
+     */
+    public String getSourceName() {
+        return sourceName;
+    }
+
+    /**
+     * Get the context in which all indexing operations execute.
+     * 
+     * @return the execution context; never null
+     */
+    public ExecutionContext getContext() {
+        return context;
+    }
+
+    /**
+     * @return maxChangesBeforeAutomaticOptimization
+     */
+    public int getMaxChangesBeforeAutomaticOptimization() {
+        return maxChangesBeforeAutomaticOptimization;
+    }
+
+    /**
+     * Utility to create a Graph for the source.
+     * 
+     * @return the graph instance; never null
+     */
+    final Graph graph() {
+        return Graph.create(sourceName, connectionFactory, context);
+    }
+
+    /**
+     * Utility to obtain the root path.
+     * 
+     * @return the root path; never null
+     */
+    final Path rootPath() {
+        return context.getValueFactories().getPathFactory().createRootPath();
+    }
+
+    /**
+     * Utility to obtain a readable string representation of the supplied path.
+     * 
+     * @param path the path
+     * @return the readable string representation; may be null if path is null
+     */
+    final String readable( Path path ) {
+        return context.getValueFactories().getStringFactory().create(path);
+    }
+
+    /**
+     * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
+     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
+     * the workspace and source.
+     * <p>
+     * This method operates synchronously and returns when the requested indexing is completed.
+     * </p>
+     * 
+     * @param workspaceName the name of the workspace
+     * @param startingPoint the location that represents the content to be indexed; must have a path
+     * @param depthPerRead the depth of each subgraph read operation
+     * @throws IllegalArgumentException if the workspace name or location are null
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    public void index( String workspaceName,
+                       Location startingPoint,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        CheckArg.isNotNull(startingPoint, "startingPoint");
+        assert startingPoint.hasPath();
+
+        Workspace workspace = getWorkspace(workspaceName);
+        if (startingPoint.getPath().isRoot()) {
+            // More efficient to just start over with a new index ...
+            workspace.execute(true, addContent(startingPoint, depthPerRead));
+        } else {
+            // Have to first remove the content below the starting point, then add it again ...
+            workspace.execute(false, removeContent(startingPoint), addContent(startingPoint, depthPerRead));
+        }
+    }
+
+    /**
+     * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
+     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
+     * the workspace and source.
+     * <p>
+     * This method operates synchronously and returns when the requested indexing is completed.
+     * </p>
+     * 
+     * @param workspaceName the name of the workspace
+     * @param startingPoint the path that represents the content to be indexed
+     * @param depthPerRead the depth of each subgraph read operation
+     * @throws IllegalArgumentException if the workspace name or path are null
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    public void index( String workspaceName,
+                       Path startingPoint,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        CheckArg.isNotNull(startingPoint, "startingPoint");
+        index(workspaceName, Location.create(startingPoint), depthPerRead);
+    }
+
+    /**
+     * Index all of the content in the named workspace within the {@link #getSourceName() source}. This method operates
+     * synchronously and returns when the requested indexing is completed.
+     * 
+     * @param workspaceName the name of the workspace
+     * @param depthPerRead the depth of each subgraph read operation
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    public void index( String workspaceName,
+                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        index(workspaceName, rootPath(), depthPerRead);
+    }
+
+    /**
+     * Index (or re-index) all of the content in all of the workspaces within the source. This method operates synchronously and
+     * returns when the requested indexing is completed.
+     * 
+     * @param depthPerRead the depth of each subgraph read operation
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
+     */
+    public void index( int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+        Path rootPath = rootPath();
+        for (String workspaceName : graph().getWorkspaces()) {
+            index(workspaceName, rootPath, depthPerRead);
+        }
+    }
+
+    /**
+     * Update the indexes with the supplied set of changes to the content.
+     * 
+     * @param changes the set of changes to the content
+     * @throws IllegalArgumentException if the path is null
+     * @throws RepositorySourceException if there is a problem accessing the content
+     * @throws SearchEngineException if there is a problem updating the indexes
+     */
+    public void index( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
+        // First break up all the changes into different collections, one collection per workspace ...
+        Map<String, Collection<ChangeRequest>> changesByWorkspace = new HashMap<String, Collection<ChangeRequest>>();
+        for (ChangeRequest request : changes) {
+            String workspaceName = request.changedWorkspace();
+            Collection<ChangeRequest> changesForWorkspace = changesByWorkspace.get(workspaceName);
+            if (changesForWorkspace == null) {
+                changesForWorkspace = new LinkedList<ChangeRequest>();
+                changesByWorkspace.put(workspaceName, changesForWorkspace);
+            }
+            changesForWorkspace.add(request);
+        }
+        // Now update the indexes for each workspace (serially). This minimizes the time that each workspace
+        // locks its indexes for writing.
+        for (Map.Entry<String, Collection<ChangeRequest>> entry : changesByWorkspace.entrySet()) {
+            String workspaceName = entry.getKey();
+            Collection<ChangeRequest> changesForWorkspace = entry.getValue();
+            getWorkspace(workspaceName).execute(false, updateContent(changesForWorkspace));
+        }
+    }
+
+    /**
+     * Invoke the engine's garbage collection on all indexes used by all workspaces in the source. This method reclaims space and
+     * optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
+     * 
+     * @throws SearchEngineException if there is a problem during optimization
+     */
+    public void optimize() throws SearchEngineException {
+        for (String workspaceName : graph().getWorkspaces()) {
+            getWorkspace(workspaceName).execute(false, optimizeContent());
+        }
+    }
+
+    /**
+     * Invoke the engine's garbage collection for the indexes associated with the specified workspace. This method reclaims space
+     * and optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
+     * 
+     * @param workspaceName the name of the workspace
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem during optimization
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    public void optimize( String workspaceName ) throws SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        getWorkspace(workspaceName).execute(false, optimizeContent());
+    }
+
+    /**
+     * Perform a full-text search of the content in the named workspace, given the maximum number of results and the offset
+     * defining the first result the caller is interested in.
+     * 
+     * @param context the execution context in which the search is to take place; may not be null
+     * @param workspaceName the name of the workspace
+     * @param fullTextSearch the full-text search to be performed; may not be null
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @return the activity that will perform the work
+     * @throws IllegalArgumentException if the execution context or workspace name are null
+     * @throws SearchEngineException if there is a problem during optimization
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    public List<Location> fullTextSearch( ExecutionContext context,
+                                          String workspaceName,
+                                          String fullTextSearch,
+                                          int maxResults,
+                                          int offset ) {
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        Search searchActivity = searchContent(context, fullTextSearch, maxResults, offset);
+        getWorkspace(workspaceName).execute(false, searchActivity);
+        return searchActivity.getResults();
+    }
+
+    /**
+     * Perform a query of the content in the named workspace, given the Abstract Query Model representation of the query.
+     * 
+     * @param context the execution context in which the search is to take place; may not be null
+     * @param workspaceName the name of the workspace
+     * @param query the query that is to be executed, in the form of the Abstract Query Model
+     * @param schemata the definition of the tables and views that can be used in the query; may not be null
+     * @return the query results; never null
+     * @throws IllegalArgumentException if the context, query, or schemata references are null
+     */
+    public QueryResults query( ExecutionContext context,
+                               String workspaceName,
+                               QueryCommand query,
+                               Schemata schemata ) {
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        CheckArg.isNotNull(query, "query");
+        CheckArg.isNotNull(schemata, "schemata");
+        QueryContext queryContext = new QueryContext(context, schemata);
+        Query queryActivity = queryContent(queryContext, query);
+        getWorkspace(workspaceName).execute(false, queryActivity);
+        return queryActivity.getResults();
+    }
+
+    /**
+     * Remove the supplied index from the search engine. This is typically done when the workspace has been deleted from the
+     * source, or when
+     * 
+     * @param workspaceName the name of the workspace
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem removing the workspace
+     */
+    public void removeWorkspace( String workspaceName ) throws SearchEngineException {
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        try {
+            workspacesLock.writeLock().lock();
+            // Check whether another thread got in and created the engine while we waited ...
+            Workspace workspace = workspacesByName.remove(workspaceName);
+            if (workspace != null) {
+                indexLayout.destroyIndexes(context, getSourceName(), workspaceName);
+            }
+        } catch (IOException e) {
+            String message = GraphI18n.errorWhileRemovingIndexesForWorkspace.text(sourceName, workspaceName, e.getMessage());
+            throw new SearchEngineException(message, e);
+        } finally {
+            workspacesLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Remove from the search engine all workspace-related indexes, thereby cleaning up any resources used by this search engine.
+     * 
+     * @throws SearchEngineException if there is a problem removing any of the workspace
+     */
+    public void removeWorkspaces() throws SearchEngineException {
+        try {
+            workspacesLock.writeLock().lock();
+            for (String workspaceName : new HashSet<String>(workspacesByName.keySet())) {
+                removeWorkspace(workspaceName);
+            }
+        } finally {
+            workspacesLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Get the search engine for the workspace with the supplied name.
+     * 
+     * @param workspaceName the name of the workspace
+     * @return the workspace's search engine
+     * @throws InvalidWorkspaceException if the workspace does not exist
+     */
+    protected Workspace getWorkspace( String workspaceName ) {
+        Workspace workspace = null;
+        try {
+            workspacesLock.readLock().lock();
+            workspace = workspacesByName.get(workspaceName);
+        } finally {
+            workspacesLock.readLock().unlock();
+        }
+
+        if (workspace == null) {
+            // Verify the workspace does exist ...
+            if (!graph().getWorkspaces().contains(workspaceName)) {
+                String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, getSourceName());
+                throw new InvalidWorkspaceException(msg);
+            }
+            try {
+                workspacesLock.writeLock().lock();
+                // Check whether another thread got in and created the engine while we waited ...
+                workspace = workspacesByName.get(workspaceName);
+                if (workspace == null) {
+                    // Create the engine and register it ...
+                    workspace = new Workspace(workspaceName);
+                    workspacesByName.put(workspaceName, workspace);
+                }
+            } finally {
+                workspacesLock.writeLock().unlock();
+            }
+        }
+        return workspace;
+    }
+
+    protected class Workspace {
+        private final String sourceName;
+        private final String workspaceName;
+        protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
+
+        protected Workspace( String workspaceName ) {
+            this.workspaceName = workspaceName;
+            this.sourceName = getSourceName();
+        }
+
+        /**
+         * Get the workspace name.
+         * 
+         * @return the workspace name; never null
+         */
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        /**
+         * Execute the supplied activities against the indexes.
+         * 
+         * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+         * @param activities the activities to execute
+         * @throws SearchEngineException if there is a problem performing the activities
+         */
+        protected final void execute( boolean overwrite,
+                                      Activity... activities ) throws SearchEngineException {
+            // Determine if the activities are readonly ...
+            boolean readOnly = true;
+            for (Activity activity : activities) {
+                if (!(activity instanceof ReadOnlyActivity)) {
+                    readOnly = false;
+                    break;
+                }
+            }
+
+            // Create a session ...
+            Session session = indexLayout.createSession(context, sourceName, workspaceName, overwrite, readOnly);
+            assert session != null;
+
+            // Execute the various activities ...
+            Throwable error = null;
+            try {
+                int numChanges = 0;
+                for (Activity activity : activities) {
+                    try {
+                        numChanges += activity.execute(session);
+                    } catch (RuntimeException e) {
+                        error = e;
+                        throw e;
+                    }
+                }
+                if (numChanges > 0) {
+                    numChanges = this.modifiedNodesSinceLastOptimize.addAndGet(numChanges);
+                    // Determine if there have been enough changes made to run the optimizer ...
+                    int maxChanges = getMaxChangesBeforeAutomaticOptimization();
+                    if (maxChanges > 0 && numChanges >= maxChanges) {
+                        Activity optimizer = optimizeContent();
+                        try {
+                            optimizer.execute(session);
+                        } catch (RuntimeException e) {
+                            error = e;
+                            throw e;
+                        }
+                    }
+                }
+            } finally {
+                try {
+                    if (error == null) {
+                        session.commit();
+                    } else {
+                        session.rollback();
+                    }
+                } catch (RuntimeException e2) {
+                    // We don't want to lose the existing error, if there is one ...
+                    if (error == null) {
+                        I18n msg = GraphI18n.errorWhileCommittingIndexChanges;
+                        throw new SearchEngineException(msg.text(workspaceName, sourceName, e2.getMessage()), e2);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create an activity that will optimize the indexes.
+     * 
+     * @return the activity that will perform the work
+     */
+    protected Activity optimizeContent() {
+        return new Activity() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#execute(org.jboss.dna.graph.search.SearchProvider.Session)
+             */
+            public int execute( Session session ) {
+                session.optimize();
+                return 0; // no lines changed
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return GraphI18n.errorWhileOptimizingIndexes.text(sourceName, workspaceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will read from the source the content at the supplied location and add the content to the search
+     * index.
+     * 
+     * @param location the location of the content to read; may not be null
+     * @param depthPerRead the depth of each read operation; always positive
+     * @return the activity that will perform the work
+     */
+    protected Activity addContent( final Location location,
+                                   final int depthPerRead ) {
+        return new Activity() {
+            public int execute( Session session ) {
+
+                // Create a queue that we'll use to walk the content ...
+                LinkedList<Location> locationsToRead = new LinkedList<Location>();
+                locationsToRead.add(location);
+                int count = 0;
+
+                // Now read and index the content ...
+                Graph graph = graph();
+                graph.useWorkspace(session.getWorkspaceName());
+                while (!locationsToRead.isEmpty()) {
+                    Location location = locationsToRead.poll();
+                    if (location == null) continue;
+                    Subgraph subgraph = graph.getSubgraphOfDepth(depthPerRead).at(location);
+                    // Index all of the nodes within this subgraph ...
+                    for (SubgraphNode node : subgraph) {
+                        // Index the node ...
+                        session.index(node);
+                        ++count;
+
+                        // Process the children ...
+                        for (Location child : node.getChildren()) {
+                            if (!subgraph.includes(child)) {
+                                // Record this location as needing to be read ...
+                                locationsToRead.add(child);
+                            }
+                        }
+                    }
+                }
+                return count;
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                String path = readable(location.getPath());
+                return GraphI18n.errorWhileIndexingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will remove from the indexes all documents that represent content at or below the specified
+     * location.
+     * 
+     * @param location the location of the content to removed; may not be null
+     * @return the activity that will perform the work
+     */
+    protected Activity removeContent( final Location location ) {
+        return new Activity() {
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#execute(org.jboss.dna.graph.search.SearchProvider.Session)
+             */
+            public int execute( Session session ) {
+                // Delete the content at/below the path ...
+                return session.deleteBelow(location.getPath());
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                String path = readable(location.getPath());
+                return GraphI18n.errorWhileRemovingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will update the indexes with changes that were already made to the content.
+     * 
+     * @param changes the changes that have been made to the content; may not be null
+     * @return the activity that will perform the work
+     */
+    protected Activity updateContent( final Iterable<ChangeRequest> changes ) {
+        return new Activity() {
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#execute(org.jboss.dna.graph.search.SearchProvider.Session)
+             */
+            public int execute( Session session ) {
+                return session.apply(changes);
+            }
+
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return GraphI18n.errorWhileUpdatingContent.text(workspaceName, sourceName, error.getMessage());
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will perform a full-text search given the supplied query.
+     * 
+     * @param context the context in which the search is to be performed; may not be null
+     * @param fullTextSearch the full-text search to be performed; may not be null
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @return the activity that will perform the work; never null
+     */
+    protected Search searchContent( final ExecutionContext context,
+                                    final String fullTextSearch,
+                                    final int maxResults,
+                                    final int offset ) {
+        final List<Location> results = new ArrayList<Location>(maxResults);
+        return new Search() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#execute(org.jboss.dna.graph.search.SearchProvider.Session)
+             */
+            public int execute( Session session ) {
+                session.search(context, fullTextSearch, maxResults, offset, results);
+                return 0;
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
+             *      java.lang.String)
+             */
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return GraphI18n.errorWhilePerformingSearch.text(fullTextSearch, workspaceName, sourceName, error.getMessage());
+            }
+
+            public List<Location> getResults() {
+                return results;
+            }
+        };
+    }
+
+    /**
+     * Create an activity that will perform a query against the index.
+     * 
+     * @param context the context in which the search is to be performed; may not be null
+     * @param query the query to be performed; may not be null
+     * @return the activity that will perform the query; never null
+     */
+    protected Query queryContent( final QueryContext context,
+                                  final QueryCommand query ) {
+        return new Query() {
+            private QueryResults results = null;
+
+            public int execute( Session session ) throws SearchException {
+                results = session.query(context, query);
+                return 0;
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
+             *      java.lang.String)
+             */
+            public String messageFor( Throwable error,
+                                      String sourceName,
+                                      String workspaceName ) {
+                return GraphI18n.errorWhilePerformingQuery.text(query, workspaceName, sourceName, error.getMessage());
+            }
+
+            public QueryResults getResults() {
+                return results;
+            }
+        };
+    }
+
+    /**
+     * Interface for activities that will be executed against a workspace. These activities don't have to commit or roll back the
+     * writer, nor do they have to translate the exceptions, since this is done by the
+     * {@link Workspace#execute(boolean, Activity...)} method.
+     */
+    protected interface Activity {
+
+        /**
+         * Perform the activity by using the index writer.
+         * 
+         * @param indexSession the index session that should be used by the activity; never null
+         * @return the number of changes that were made by this activity
+         */
+        int execute( Session indexSession );
+
+        /**
+         * Translate an exception obtained during {@link #execute(Session) execution} into a single message.
+         * 
+         * @param t the exception
+         * @param sourceName the name of the source
+         * @param workspaceName the name of the workspace
+         * @return the error message
+         */
+        String messageFor( Throwable t,
+                           String sourceName,
+                           String workspaceName );
+    }
+
+    /**
+     * A read-only activity.
+     */
+    protected interface ReadOnlyActivity extends Activity {
+    }
+
+    /**
+     * A search activity.
+     */
+    protected interface Search extends ReadOnlyActivity {
+        /**
+         * Get the results of the search.
+         * 
+         * @return the list of {@link Location} objects for each node satisfying the results; never null
+         */
+        List<Location> getResults();
+    }
+
+    /**
+     * A query activity.
+     */
+    protected interface Query extends ReadOnlyActivity {
+        /**
+         * Get the results of the query.
+         * 
+         * @return the results of a query; never null
+         */
+        QueryResults getResults();
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngineException.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngineException.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngineException.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,67 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.search;
+
+/**
+ * An exception that represents a problem within a search engine.
+ */
+public class SearchEngineException extends RuntimeException {
+
+    /**
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 
+     */
+    public SearchEngineException() {
+    }
+
+    /**
+     * @param message
+     */
+    public SearchEngineException( String message ) {
+        super(message);
+
+    }
+
+    /**
+     * @param cause
+     */
+    public SearchEngineException( Throwable cause ) {
+        super(cause);
+
+    }
+
+    /**
+     * @param message
+     * @param cause
+     */
+    public SearchEngineException( String message,
+                                  Throwable cause ) {
+        super(message, cause);
+
+    }
+
+}

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchException.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchException.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,97 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.search;
+
+/**
+ * An exception signalling an error during a search.
+ */
+public class SearchException extends RuntimeException {
+
+    /**
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final String expression;
+
+    /**
+     * Create an exception with the search expression.
+     * 
+     * @param expression the search expression
+     */
+    public SearchException( String expression ) {
+        super();
+        this.expression = expression;
+    }
+
+    /**
+     * Create an exception with the search expression and a message.
+     * 
+     * @param expression the search expression
+     * @param message the exception message
+     */
+    public SearchException( String expression,
+                            String message ) {
+        super(message);
+        assert expression != null;
+        this.expression = expression;
+    }
+
+    /**
+     * Construct a system failure exception with another exception that is the cause of the failure.
+     * 
+     * @param expression the search expression
+     * @param cause the original cause of the failure
+     */
+    public SearchException( String expression,
+                            Throwable cause ) {
+        super(cause);
+        assert expression != null;
+        this.expression = expression;
+    }
+
+    /**
+     * Construct a system failure exception with a single message and another exception that is the cause of the failure.
+     * 
+     * @param expression the search expression
+     * @param message the message describing the failure
+     * @param cause the original cause of the failure
+     */
+    public SearchException( String expression,
+                            String message,
+                            Throwable cause ) {
+        super(message, cause);
+        assert expression != null;
+        this.expression = expression;
+    }
+
+    /**
+     * Get the search expression.
+     * 
+     * @return the search expression; never null
+     */
+    public String getSearchExpression() {
+        return expression;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchException.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchProvider.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchProvider.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchProvider.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,178 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.search;
+
+import java.io.IOException;
+import java.util.List;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * The representation of a single layout of one or more Lucene indexes.
+ */
+ at ThreadSafe
+public interface SearchProvider {
+
+    /**
+     * Create a new session to the indexes.
+     * 
+     * @param context the execution context for which this session is to be established; may not be null
+     * @param sourceName the name of the source; may not be null
+     * @param workspaceName the name of the workspace; may not be null
+     * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+     * @param readOnly true if the resulting session can be optimized for use in read-only situations, or false if the session
+     *        needs to allow calling the write methods
+     * @return the session to the indexes; never null
+     */
+    Session createSession( ExecutionContext context,
+                           String sourceName,
+                           String workspaceName,
+                           boolean overwrite,
+                           boolean readOnly );
+
+    /**
+     * Destroy the indexes for the workspace with the supplied name.
+     * 
+     * @param context the execution context in which the destruction should be performed; may not be null
+     * @param sourceName the name of the source; may not be null
+     * @param workspaceName the name of the workspace; may not be null
+     * @return true if the indexes for the workspace were destroyed, or false if there was no such workspace index
+     * @throws IOException if there is a problem destroying the indexes
+     */
+    boolean destroyIndexes( ExecutionContext context,
+                            String sourceName,
+                            String workspaceName ) throws IOException;
+
+    /**
+     * A stateful session that is used to interact with the search provider to search a particular source and workspace.
+     */
+    public interface Session {
+
+        /**
+         * Get the name of the {@link RepositorySource repository source} for which this session exists. A session instance will
+         * always return the same name.
+         * 
+         * @return the source name; never null
+         */
+        String getSourceName();
+
+        /**
+         * Get the name of the workspace for which this session exists. A session instance will always return the same name.
+         * 
+         * @return the workspace name; never null
+         */
+        String getWorkspaceName();
+
+        /**
+         * Get the execution context in which this session is operating.
+         * 
+         * @return the execution context; never null
+         */
+        ExecutionContext getContext();
+
+        /**
+         * Return whether this session made changes to the indexed state.
+         * 
+         * @return true if change were made, or false otherwise
+         */
+        boolean hasChanges();
+
+        /**
+         * Perform a full-text search given the supplied query.
+         * 
+         * @param context the context in which the search should be executed; may not be null
+         * @param fullTextString the full-text query; never null or blank
+         * @param maxResults the maximum number of results that are to be returned; always positive
+         * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+         * @param results the list where the results should be accumulated; never null
+         */
+        void search( ExecutionContext context,
+                     String fullTextString,
+                     int maxResults,
+                     int offset,
+                     List<Location> results );
+
+        /**
+         * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model,
+         * with the {@link Schemata} that defines the tables and views that are available to the query, and the set of index
+         * readers (and writers) that should be used.
+         * 
+         * @param queryContext the context in which the query should be executed; may not be null
+         * @param query the query; never null
+         * @return the results of the query; never null
+         */
+        QueryResults query( QueryContext queryContext,
+                            QueryCommand query );
+
+        /**
+         * Index the node. Changes are recorded only when {@link #commit()} is called.
+         * 
+         * @param node the node to be indexed; never null
+         */
+        void index( Node node );
+
+        /**
+         * Update the indexes to reflect the supplied changes to the graph content.
+         * 
+         * @param changes the set of changes to the content
+         * @return the (approximate) number of nodes that were affected by the changes
+         * @throws SearchEngineException if there is a problem executing the query
+         */
+        int apply( Iterable<ChangeRequest> changes );
+
+        /**
+         * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path.
+         * 
+         * @param path the path identifying the graph content that is to be removed; never null
+         * @return the (approximate) number of nodes that were affected by the changes
+         */
+        int deleteBelow( Path path );
+
+        /**
+         * Optimize the indexes, if required.
+         */
+        void optimize();
+
+        /**
+         * Close this session by committing all of the changes. This session is no longer usable after this method is called.
+         */
+        void commit();
+
+        /**
+         * Close this session by rolling back all of the changes that have been made. This session is no longer usable after this
+         * method is called.
+         */
+        void rollback();
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchProvider.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-18 19:39:05 UTC (rev 1329)
@@ -147,3 +147,13 @@
 expectingLiteralAndUnableToParseAsLong = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a long
 expectingLiteralAndUnableToParseAsDouble = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a double
 expectingLiteralAndUnableToParseAsDate = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a date
+
+# Search
+errorWhileIndexingContentAtPath = Error while indexing the content at "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhileRemovingContentAtPath = Error while removing the content at/below "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhileUpdatingContent = Error while updating content in the "{0}" workspace of the "{1}" source: {2}
+errorWhileCommittingIndexChanges = Error while committing changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorWhileOptimizingIndexes = Error while optimizing the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorWhilePerformingSearch = Error while searching for "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhilePerformingQuery = Error while performing the query "{0}" against the content in the "{1}" workspace of the "{2}" source: {3}
+errorWhileRemovingIndexesForWorkspace = Error while removing the indexes for the "{0}" workspace of the "{1}" source: {2}

Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java (from rev 1328, trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,254 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Query;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.search.SearchProvider.Session;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SearchEngineTest {
+
+    private SearchEngine engine;
+    private ExecutionContext context;
+    private String sourceName;
+    private String workspaceName1;
+    private String workspaceName2;
+    private InMemoryRepositorySource source;
+    private RepositoryConnectionFactory connectionFactory;
+    private SearchProvider provider;
+    private SearchProvider.Session sessionWs1;
+    private SearchProvider.Session sessionWs2;
+    private SearchProvider.Session sessionDefault;
+    private Graph content;
+
+    @Before
+    public void beforeEach() throws Exception {
+        context = new ExecutionContext();
+        sourceName = "sourceA";
+        workspaceName1 = "workspace1";
+        workspaceName2 = "workspace2";
+
+        // Set up the source and graph instance ...
+        source = new InMemoryRepositorySource();
+        source.setName(sourceName);
+        content = Graph.create(source, context);
+
+        // Create the workspaces ...
+        content.createWorkspace().named(workspaceName1);
+        content.createWorkspace().named(workspaceName2);
+
+        // Set up the connection factory ...
+        connectionFactory = new RepositoryConnectionFactory() {
+            @SuppressWarnings( "synthetic-access" )
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                return source.getConnection();
+            }
+        };
+
+        // Set up the index layout ...
+        provider = mock(SearchProvider.class);
+        sessionWs1 = mockSession(provider, workspaceName1);
+        sessionWs2 = mockSession(provider, workspaceName2);
+        sessionDefault = mockSession(provider, "");
+
+        // Now set up the search engine ...
+        engine = new SearchEngine(context, sourceName, connectionFactory, provider);
+    }
+
+    protected Session mockSession( SearchProvider mockProvider,
+                                   String workspaceName ) {
+        Session session = mock(Session.class);
+        stub(mockProvider.createSession(context, sourceName, workspaceName, false, false)).toReturn(session);
+        stub(mockProvider.createSession(context, sourceName, workspaceName, false, true)).toReturn(session);
+        stub(mockProvider.createSession(context, sourceName, workspaceName, true, false)).toReturn(session);
+        stub(mockProvider.createSession(context, sourceName, workspaceName, true, true)).toReturn(session);
+        stub(session.getWorkspaceName()).toReturn(workspaceName);
+        stub(session.getSourceName()).toReturn(sourceName);
+        return session;
+    }
+
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected void loadContent() throws Exception {
+        // Load some content ...
+        content.useWorkspace(workspaceName1);
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
+        content.useWorkspace(workspaceName2);
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
+    }
+
+    @Test
+    public void shouldReturnSearchWorkspaceForExistingWorkspaceInSource() {
+        SearchEngine.Workspace workspace = engine.getWorkspace(workspaceName1);
+        assertThat(workspace, is(notNullValue()));
+        assertThat(workspace.modifiedNodesSinceLastOptimize.get(), is(0));
+        assertThat(workspace.getWorkspaceName(), is(workspaceName1));
+    }
+
+    @Test( expected = InvalidWorkspaceException.class )
+    public void shouldFailToReturnSearchWorkspaceForNonExistantWorkspaceInSource() {
+        engine.getWorkspace(workspaceName1 + "foobar");
+    }
+
+    @Test
+    public void shouldDoNothingDuringRemoveWorkspaceIfWorkspaceHasNotBeenLoaded() throws Exception {
+        engine.removeWorkspace(workspaceName1);
+        verifyZeroInteractions(provider);
+    }
+
+    @Test
+    public void shouldForwardRemoveWorkspaceToIndexLayout() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.removeWorkspace(workspaceName1);
+        verify(provider).destroyIndexes(context, sourceName, workspaceName1);
+        verifyNoMoreInteractions(provider);
+    }
+
+    @Test
+    public void shouldForwardRemoveWorkspaceToIndexLayoutForEachWorkspaceThatWasLoaded() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.removeWorkspaces();
+        verify(provider).destroyIndexes(context, sourceName, workspaceName1);
+        verifyZeroInteractions(provider);
+    }
+
+    @Test
+    public void shouldForwardRemoveWorkspaceToIndexLayoutForAllWorkspacesThatWereLoaded() throws Exception {
+        engine.getWorkspace(workspaceName1);
+        engine.getWorkspace(workspaceName2);
+        engine.removeWorkspaces();
+        verify(provider).destroyIndexes(context, sourceName, workspaceName1);
+        verify(provider).destroyIndexes(context, sourceName, workspaceName2);
+        verifyNoMoreInteractions(provider);
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailIfNullWorkspaceNamePassedToRemoveWorkspace() throws Exception {
+        engine.removeWorkspace(null);
+    }
+
+    @Test
+    public void shouldForwardOptimizeOfWorkspaceToIndexSession() throws Exception {
+        engine.optimize(workspaceName1);
+        verify(sessionWs1).optimize();
+        verify(sessionWs1).commit();
+        verifyNoMoreInteractions(sessionWs1);
+    }
+
+    @Test
+    public void shouldForwardOptimizeOfAllWorkspacesToEachIndexSession() throws Exception {
+        engine.optimize(); // will find all three workspaces
+        verify(sessionWs1).optimize();
+        verify(sessionWs1).commit();
+        verifyNoMoreInteractions(sessionWs1);
+        verify(sessionWs2).optimize();
+        verify(sessionWs2).commit();
+        verifyNoMoreInteractions(sessionWs2);
+        verify(sessionDefault).optimize();
+        verify(sessionDefault).commit();
+        verifyNoMoreInteractions(sessionDefault);
+    }
+
+    @Test
+    public void shouldForwardIndexOfWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, 3);
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardIndexOfSubgraphInWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, path("/Cars"), 3);
+        verify(sessionWs1).deleteBelow(path("/Cars"));
+        verify(sessionWs1, times(17)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardIndexEntireWorkspaceToIndexSession() throws Exception {
+        loadContent();
+        engine.index(workspaceName1, path("/"), 3);
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardIndexOfAllWorkspacesToEachIndexSession() throws Exception {
+        loadContent();
+        engine.index(3); // will find all three workspaces
+        verify(sessionWs1, times(18)).index((Node)anyObject());
+        verify(sessionWs1).commit();
+        verify(sessionWs2, times(24)).index((Node)anyObject());
+        verify(sessionWs2).commit();
+        verify(sessionDefault, times(1)).index((Node)anyObject());
+        verify(sessionDefault).commit();
+    }
+
+    @SuppressWarnings( "unchecked" )
+    @Test
+    public void shouldForwardSearchToIndexSession() throws Exception {
+        String query = "term1 term2";
+        engine.fullTextSearch(context, workspaceName1, query, 3, 0);
+        verify(sessionWs1).search(eq(context), eq(query), eq(3), eq(0), (List<Location>)anyObject());
+        verify(sessionWs1).commit();
+    }
+
+    @Test
+    public void shouldForwardQueryToIndexSession() throws Exception {
+        Query query = mock(Query.class);
+        Schemata schemata = mock(Schemata.class);
+        engine.query(context, workspaceName1, query, schemata);
+        verify(sessionWs1).query(eq(new QueryContext(context, schemata)), eq(query));
+        verify(sessionWs1).commit();
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,59 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.store.Directory;
-
-/**
- * Interface used to obtain the Lucene {@link Directory} instance that should be used for a workspace given the name of the
- * workspace. There are several implementations (see {@link DirectoryConfigurations}), but custom implementations can always be
- * used.
- */
- at ThreadSafe
-public interface DirectoryConfiguration {
-    /**
-     * Get the {@link Directory} that should be used for the workspace with the supplied name.
-     * 
-     * @param workspaceName the workspace name
-     * @param indexName the name of the index to be created
-     * @return the directory; never null
-     * @throws IllegalArgumentException if the workspace name is null
-     * @throws SearchEngineException if there is a problem creating the directory
-     */
-    Directory getDirectory( String workspaceName,
-                            String indexName ) throws SearchEngineException;
-
-    /**
-     * Destroy the {@link Directory} that is used for the workspace with the supplied name.
-     * 
-     * @param workspaceName the workspace name
-     * @param indexName the name of the index to be created
-     * @return true if the directory existed and was destroyed, or false if the directory didn't exist
-     * @throws IllegalArgumentException if the workspace name is null
-     * @throws SearchEngineException if there is a problem creating the directory
-     */
-    boolean destroyDirectory( String workspaceName,
-                              String indexName ) throws SearchEngineException;
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,426 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.ConcurrentHashMap;
-import net.jcip.annotations.Immutable;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.store.LockFactory;
-import org.apache.lucene.store.RAMDirectory;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.text.NoOpEncoder;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.common.util.FileUtil;
-import org.jboss.dna.common.util.HashCode;
-
-/**
- * A family of {@link DirectoryConfiguration} implementations.
- */
-public class DirectoryConfigurations {
-
-    /**
-     * Return a new {@link DirectoryConfiguration} that creates in-memory directories.
-     * 
-     * @return the new directory configuration; never null
-     */
-    public static final DirectoryConfiguration inMemory() {
-        return new RamDirectoryFactory();
-    }
-
-    /**
-     * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
-     * workspace names.
-     * 
-     * @param parent the parent folder
-     * @return the new directory configuration; never null
-     * @throws IllegalArgumentException if the parent file is null
-     */
-    public static final DirectoryConfiguration using( File parent ) {
-        return new FileSystemDirectoryFromNameFactory(parent);
-    }
-
-    /**
-     * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
-     * workspace names.
-     * 
-     * @param parent the parent folder
-     * @param lockFactory the lock factory; may be null
-     * @return the new directory configuration; never null
-     * @throws IllegalArgumentException if the parent file is null
-     */
-    public static final DirectoryConfiguration using( File parent,
-                                                      LockFactory lockFactory ) {
-        return new FileSystemDirectoryFromNameFactory(parent, lockFactory);
-    }
-
-    /**
-     * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
-     * workspace names.
-     * 
-     * @param parent the parent folder
-     * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
-     * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
-     * @return the new directory configuration; never null
-     * @throws IllegalArgumentException if the parent file is null
-     */
-    public static final DirectoryConfiguration using( File parent,
-                                                      TextEncoder workspaceNameEncoder,
-                                                      TextEncoder indexNameEncoder ) {
-        return new FileSystemDirectoryFromNameFactory(parent, workspaceNameEncoder, indexNameEncoder);
-    }
-
-    /**
-     * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
-     * workspace names.
-     * 
-     * @param parent the parent folder
-     * @param lockFactory the lock factory; may be null
-     * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
-     * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
-     * @return the new directory configuration; never null
-     * @throws IllegalArgumentException if the parent file is null
-     */
-    public static final DirectoryConfiguration using( File parent,
-                                                      LockFactory lockFactory,
-                                                      TextEncoder workspaceNameEncoder,
-                                                      TextEncoder indexNameEncoder ) {
-        return new FileSystemDirectoryFromNameFactory(parent, lockFactory, workspaceNameEncoder, indexNameEncoder);
-    }
-
-    /**
-     * A {@link DirectoryConfiguration} implementation that creates {@link Directory} instances of the supplied type for each
-     * workspace and pools the results, ensuring that the same {@link Directory} instance is always returned for the same
-     * workspace name.
-     * 
-     * @param <DirectoryType> the concrete type of the directory
-     */
-    @ThreadSafe
-    protected static abstract class PoolingDirectoryFactory<DirectoryType extends Directory> implements DirectoryConfiguration {
-        private final ConcurrentHashMap<IndexId, DirectoryType> directories = new ConcurrentHashMap<IndexId, DirectoryType>();
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.DirectoryConfiguration#getDirectory(java.lang.String, java.lang.String)
-         */
-        public Directory getDirectory( String workspaceName,
-                                       String indexName ) throws SearchEngineException {
-            CheckArg.isNotNull(workspaceName, "workspaceName");
-            IndexId id = new IndexId(workspaceName, indexName);
-            DirectoryType result = directories.get(id);
-            if (result == null) {
-                DirectoryType newDirectory = createDirectory(workspaceName, indexName);
-                result = directories.putIfAbsent(id, newDirectory);
-                if (result == null) result = newDirectory;
-            }
-            return result;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.DirectoryConfiguration#destroyDirectory(java.lang.String, java.lang.String)
-         */
-        public boolean destroyDirectory( String workspaceName,
-                                         String indexName ) throws SearchEngineException {
-            CheckArg.isNotNull(workspaceName, "workspaceName");
-            IndexId id = new IndexId(workspaceName, indexName);
-            DirectoryType result = directories.remove(id);
-            return result != null ? doDestroy(result) : false;
-        }
-
-        /**
-         * Method implemented by subclasses to create a new Directory implementation.
-         * 
-         * @param workspaceName the name of the workspace for which the {@link Directory} is to be created; never null
-         * @param indexName the name of the index to be created
-         * @return the new directory; may not be null
-         * @throws SearchEngineException if there is a problem creating the directory
-         */
-        protected abstract DirectoryType createDirectory( String workspaceName,
-                                                          String indexName ) throws SearchEngineException;
-
-        protected abstract boolean doDestroy( DirectoryType directory ) throws SearchEngineException;
-    }
-
-    /**
-     * A {@link DirectoryConfiguration} implementation that creates {@link RAMDirectory} instances for each workspace and index
-     * name. Each factory instance maintains a pool of {@link RAMDirectory} instances, ensuring that the same {@link RAMDirectory}
-     * is always returned for the same workspace name.
-     */
-    @ThreadSafe
-    public static class RamDirectoryFactory extends PoolingDirectoryFactory<RAMDirectory> {
-        protected RamDirectoryFactory() {
-        }
-
-        @Override
-        protected RAMDirectory createDirectory( String workspaceName,
-                                                String indexName ) {
-            return new RAMDirectory();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.DirectoryConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
-         */
-        @Override
-        protected boolean doDestroy( RAMDirectory directory ) throws SearchEngineException {
-            return directory != null;
-        }
-    }
-
-    /**
-     * A {@link DirectoryConfiguration} implementation that creates {@link FSDirectory} instances for each workspace and index
-     * name. This factory is created with a parent directory under which all workspace and index directories are created.
-     * <p>
-     * This uses the supplied encoders to translate the workspace and index names into valid directory names. By default, no
-     * encoding is performed, meaning that the workspace and index names are used explicitly as directory names. This default
-     * behavior, then, means that not all values of workspace names or index names will work. If you want to be sure that all
-     * workspace names work, supply an encoder for the workspace names. (Index names are currently such that they will always be
-     * valid directory names, but you can always supply an encoder if you'd like.)
-     * </p>
-     */
-    public static class FileSystemDirectoryFromNameFactory extends PoolingDirectoryFactory<FSDirectory> {
-        private final File parentFile;
-        private final LockFactory lockFactory;
-        private final TextEncoder workspaceNameEncoder;
-        private final TextEncoder indexNameEncoder;
-
-        /**
-         * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
-         * allowable workspace names.
-         * 
-         * @param parent the parent folder
-         * @throws IllegalArgumentException if the parent file is null
-         */
-        protected FileSystemDirectoryFromNameFactory( File parent ) {
-            this(parent, null, null, null);
-        }
-
-        /**
-         * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
-         * allowable workspace names.
-         * 
-         * @param parent the parent folder
-         * @param lockFactory the lock factory; may be null
-         * @throws IllegalArgumentException if the parent file is null
-         */
-        protected FileSystemDirectoryFromNameFactory( File parent,
-                                                      LockFactory lockFactory ) {
-            this(parent, lockFactory, null, null);
-        }
-
-        /**
-         * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
-         * allowable workspace names.
-         * 
-         * @param parent the parent folder
-         * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
-         * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
-         * @throws IllegalArgumentException if the parent file is null
-         */
-        protected FileSystemDirectoryFromNameFactory( File parent,
-                                                      TextEncoder workspaceNameEncoder,
-                                                      TextEncoder indexNameEncoder ) {
-            this(parent, null, workspaceNameEncoder, indexNameEncoder);
-        }
-
-        /**
-         * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
-         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
-         * allowable workspace names.
-         * 
-         * @param parent the parent folder
-         * @param lockFactory the lock factory; may be null
-         * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
-         * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
-         * @throws IllegalArgumentException if the parent file is null
-         */
-        protected FileSystemDirectoryFromNameFactory( File parent,
-                                                      LockFactory lockFactory,
-                                                      TextEncoder workspaceNameEncoder,
-                                                      TextEncoder indexNameEncoder ) {
-            CheckArg.isNotNull(parent, "parent");
-            this.parentFile = parent;
-            this.lockFactory = lockFactory;
-            this.workspaceNameEncoder = workspaceNameEncoder != null ? workspaceNameEncoder : new NoOpEncoder();
-            this.indexNameEncoder = indexNameEncoder != null ? indexNameEncoder : new NoOpEncoder();
-        }
-
-        @Override
-        protected FSDirectory createDirectory( String workspaceName,
-                                               String indexName ) {
-            File workspaceFile = new File(parentFile, workspaceNameEncoder.encode(workspaceName));
-            if (!workspaceFile.exists()) {
-                workspaceFile.mkdirs();
-            } else {
-                if (!workspaceFile.isDirectory()) {
-                    I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
-                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
-                }
-                if (!workspaceFile.canRead()) {
-                    I18n msg = SearchI18n.locationForIndexesCannotBeRead;
-                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
-                }
-                if (!workspaceFile.canWrite()) {
-                    I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
-                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
-                }
-            }
-            File directory = workspaceFile;
-            if (indexName != null) {
-                File indexFile = new File(workspaceFile, indexNameEncoder.encode(indexName));
-                if (!indexFile.exists()) {
-                    indexFile.mkdirs();
-                } else {
-                    if (!indexFile.isDirectory()) {
-                        I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
-                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
-                    }
-                    if (!indexFile.canRead()) {
-                        I18n msg = SearchI18n.locationForIndexesCannotBeRead;
-                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
-                    }
-                    if (!indexFile.canWrite()) {
-                        I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
-                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
-                    }
-                }
-                directory = indexFile;
-            }
-            try {
-                return create(directory, lockFactory);
-            } catch (IOException e) {
-                throw new SearchEngineException(e);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.DirectoryConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
-         */
-        @Override
-        protected boolean doDestroy( FSDirectory directory ) throws SearchEngineException {
-                File file = directory.getFile();
-                if (file.exists()) {
-                    return FileUtil.delete(file);
-                }
-                return false;
-        }
-
-        /**
-         * Override this method to define which subclass of {@link FSDirectory} should be created.
-         * 
-         * @param directory the file system directory; never null
-         * @param lockFactory the lock factory; may be null
-         * @return the {@link FSDirectory} instance
-         * @throws IOException if there is a problem creating the FSDirectory instance
-         */
-        protected FSDirectory create( File directory,
-                                      LockFactory lockFactory ) throws IOException {
-            return FSDirectory.open(directory, lockFactory);
-        }
-    }
-
-    @Immutable
-    protected static final class IndexId {
-        private final String workspaceName;
-        private final String indexName;
-        private final int hc;
-
-        protected IndexId( String workspaceName,
-                           String indexName ) {
-            assert workspaceName != null;
-            this.workspaceName = workspaceName;
-            this.indexName = indexName;
-            this.hc = HashCode.compute(this.workspaceName, this.indexName);
-        }
-
-        /**
-         * @return indexName
-         */
-        public String getIndexName() {
-            return indexName;
-        }
-
-        /**
-         * @return workspaceName
-         */
-        public String getWorkspaceName() {
-            return workspaceName;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#hashCode()
-         */
-        @Override
-        public int hashCode() {
-            return hc;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#equals(java.lang.Object)
-         */
-        @Override
-        public boolean equals( Object obj ) {
-            if (obj == this) return true;
-            if (obj instanceof IndexId) {
-                IndexId that = (IndexId)obj;
-                if (this.hashCode() != that.hashCode()) return false;
-                if (!this.workspaceName.equals(that.workspaceName)) return false;
-                if (!this.indexName.equals(that.indexName)) return false;
-                return true;
-            }
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#toString()
-         */
-        @Override
-        public String toString() {
-            return indexName != null ? workspaceName + "/" + this.indexName : this.workspaceName;
-        }
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,1580 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.document.NumericField;
-import org.apache.lucene.document.Field.Index;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.IndexWriter.MaxFieldLength;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.FieldCache;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.NumericRangeQuery;
-import org.apache.lucene.search.PrefixQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.WildcardQuery;
-import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
-import org.apache.lucene.search.regex.RegexQuery;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.Version;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.text.NoOpEncoder;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.util.Logger;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFactory;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryEngine;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.model.Length;
-import org.jboss.dna.graph.query.model.NodeDepth;
-import org.jboss.dna.graph.query.model.NodeLocalName;
-import org.jboss.dna.graph.query.model.NodeName;
-import org.jboss.dna.graph.query.model.NodePath;
-import org.jboss.dna.graph.query.model.Operator;
-import org.jboss.dna.graph.query.model.PropertyValue;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.model.Visitors;
-import org.jboss.dna.graph.query.optimize.Optimizer;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.plan.Planner;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.QueryProcessor;
-import org.jboss.dna.graph.request.ChangeRequest;
-import org.jboss.dna.search.IndexRules.Rule;
-import org.jboss.dna.search.query.CompareLengthQuery;
-import org.jboss.dna.search.query.CompareNameQuery;
-import org.jboss.dna.search.query.ComparePathQuery;
-import org.jboss.dna.search.query.CompareStringQuery;
-import org.jboss.dna.search.query.MatchNoneQuery;
-import org.jboss.dna.search.query.NotQuery;
-import org.jboss.dna.search.query.UuidsQuery;
-
-/**
- * A simple {@link IndexLayout} implementation that relies upon two separate indexes: one for the node content and a second one
- * for paths and UUIDs.
- */
- at ThreadSafe
-public abstract class DualIndexLayout implements IndexLayout {
-
-    protected static final long MIN_DATE = 0;
-    protected static final long MAX_DATE = Long.MAX_VALUE;
-    protected static final long MIN_LONG = Long.MIN_VALUE;
-    protected static final long MAX_LONG = Long.MAX_VALUE;
-    protected static final double MIN_DOUBLE = Double.MIN_VALUE;
-    protected static final double MAX_DOUBLE = Double.MAX_VALUE;
-    protected static final int MIN_DEPTH = 0;
-    protected static final int MAX_DEPTH = 100;
-
-    protected static final String PATHS_INDEX_NAME = "paths";
-    protected static final String CONTENT_INDEX_NAME = "content";
-
-    protected static final String UUID_FIELD = "uuid";
-    protected static final String FULL_TEXT_SUFFIX = "/fs"; // the slash character is not allowed in a property name unescaped
-
-    static class PathIndex {
-        public static final String PATH = "path";
-        public static final String LOCAL_NAME = "name";
-        public static final String SNS_INDEX = "sns";
-        public static final String UUID = UUID_FIELD;
-        public static final String DEPTH = "depth";
-    }
-
-    static class ContentIndex {
-        public static final String UUID = UUID_FIELD;
-        public static final String FULL_TEXT = "fts";
-    }
-
-    /**
-     * The number of results that should be returned when performing queries while deleting entire branches of content. The
-     * current value is {@value} .
-     */
-    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
-
-    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
-        }
-    };
-
-    /**
-     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
-     */
-    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
-        private static final long serialVersionUID = 1L;
-
-        public FieldSelectorResult accept( String fieldName ) {
-            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
-        }
-    };
-
-    /**
-     * Get the date formatter that can be reused safely within the current thread.
-     * 
-     * @return the date formatter; never null
-     */
-    protected DateFormat dateFormatter() {
-        return dateFormatter.get();
-    }
-
-    /**
-     * Get the text encoder that should be used to encode namespaces in the search index.
-     * 
-     * @return the namespace text encoder; never null
-     */
-    protected TextEncoder getNamespaceEncoder() {
-        return new NoOpEncoder();
-    }
-
-    /**
-     * Create a Lucene {@link Analyzer} analyzer that should be used for indexing and searching.
-     * 
-     * @return the analyzer; never null
-     */
-    protected Analyzer createAnalyzer() {
-        return new StandardAnalyzer(Version.LUCENE_CURRENT);
-    }
-
-    protected abstract class LuceneSession implements IndexSession {
-        protected final ExecutionContext context;
-        protected final String sourceName;
-        protected final String workspaceName;
-        protected final IndexRules rules;
-        private final QueryEngine queryEngine;
-        private final Analyzer analyzer;
-        private final Directory pathsIndexDirectory;
-        private final Directory contentIndexDirectory;
-        protected final boolean overwrite;
-        protected final boolean readOnly;
-        protected final ValueFactory<String> stringFactory;
-        protected final DateTimeFactory dateFactory;
-        protected final PathFactory pathFactory;
-        private int changeCount;
-        private IndexReader pathsReader;
-        private IndexWriter pathsWriter;
-        private IndexSearcher pathsSearcher;
-        private IndexReader contentReader;
-        private IndexWriter contentWriter;
-        private IndexSearcher contentSearcher;
-
-        protected LuceneSession( ExecutionContext context,
-                                 String sourceName,
-                                 String workspaceName,
-                                 IndexRules rules,
-                                 Directory pathsIndexDirectory,
-                                 Directory contentIndexDirectory,
-                                 boolean overwrite,
-                                 boolean readOnly ) {
-            this.context = context;
-            this.sourceName = sourceName;
-            this.workspaceName = workspaceName;
-            this.rules = rules;
-            this.overwrite = overwrite;
-            this.readOnly = readOnly;
-            this.pathsIndexDirectory = pathsIndexDirectory;
-            this.contentIndexDirectory = contentIndexDirectory;
-            this.analyzer = createAnalyzer();
-            this.stringFactory = context.getValueFactories().getStringFactory();
-            this.dateFactory = context.getValueFactories().getDateFactory();
-            this.pathFactory = context.getValueFactories().getPathFactory();
-            assert this.context != null;
-            assert this.sourceName != null;
-            assert this.workspaceName != null;
-            assert this.rules != null;
-            assert this.analyzer != null;
-            assert this.pathsIndexDirectory != null;
-            assert this.contentIndexDirectory != null;
-            assert this.stringFactory != null;
-            assert this.dateFactory != null;
-            // do this last ...
-            this.queryEngine = createQueryProcessor();
-            assert this.queryEngine != null;
-        }
-
-        /**
-         * Create the field name that will be used to store the full-text searchable property values.
-         * 
-         * @param propertyName the name of the property; may not be null
-         * @return the field name for the full-text searchable property values; never null
-         */
-        protected String fullTextFieldName( String propertyName ) {
-            return propertyName + FULL_TEXT_SUFFIX;
-        }
-
-        protected IndexReader getPathsReader() throws IOException {
-            if (pathsReader == null) {
-                pathsReader = IndexReader.open(pathsIndexDirectory, readOnly);
-            }
-            return pathsReader;
-        }
-
-        protected IndexReader getContentReader() throws IOException {
-            if (contentReader == null) {
-                contentReader = IndexReader.open(contentIndexDirectory, readOnly);
-            }
-            return contentReader;
-        }
-
-        protected IndexWriter getPathsWriter() throws IOException {
-            assert !readOnly;
-            if (pathsWriter == null) {
-                pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
-            }
-            return pathsWriter;
-        }
-
-        protected IndexWriter getContentWriter() throws IOException {
-            assert !readOnly;
-            if (contentWriter == null) {
-                contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
-            }
-            return contentWriter;
-        }
-
-        protected IndexSearcher getPathsSearcher() throws IOException {
-            if (pathsSearcher == null) {
-                pathsSearcher = new IndexSearcher(getPathsReader());
-            }
-            return pathsSearcher;
-        }
-
-        protected IndexSearcher getContentSearcher() throws IOException {
-            if (contentSearcher == null) {
-                contentSearcher = new IndexSearcher(getContentReader());
-            }
-            return contentSearcher;
-        }
-
-        protected boolean hasWriters() {
-            return pathsWriter != null || contentWriter != null;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#getContext()
-         */
-        public final ExecutionContext getContext() {
-            return context;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#getSourceName()
-         */
-        public final String getSourceName() {
-            return sourceName;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#getWorkspaceName()
-         */
-        public String getWorkspaceName() {
-            return workspaceName;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#hasChanges()
-         */
-        public boolean hasChanges() {
-            return changeCount > 0;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#index(org.jboss.dna.graph.Node)
-         */
-        public void index( Node node ) throws IOException {
-            assert !readOnly;
-            Location location = node.getLocation();
-            UUID uuid = location.getUuid();
-            if (uuid == null) uuid = UUID.randomUUID();
-            Path path = location.getPath();
-            String uuidStr = stringFactory.create(uuid);
-            String pathStr = pathAsString(path, stringFactory);
-            String nameStr = path.isRoot() ? "" : stringFactory.create(path.getLastSegment().getName());
-            int sns = path.isRoot() ? 1 : path.getLastSegment().getIndex();
-
-            Logger logger = Logger.getLogger(getClass());
-            if (logger.isTraceEnabled()) {
-                logger.trace("indexing {0}", pathStr);
-            }
-
-            // Create a separate document for the path, which makes it easier to handle moves since the path can
-            // be changed without changing any other content fields ...
-            Document doc = new Document();
-            doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-            doc.add(new Field(PathIndex.LOCAL_NAME, nameStr, Field.Store.YES, Field.Index.ANALYZED));
-            doc.add(new NumericField(PathIndex.LOCAL_NAME, Field.Store.YES, true).setIntValue(sns));
-            doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-            doc.add(new NumericField(PathIndex.DEPTH, Field.Store.YES, true).setIntValue(path.size()));
-            getPathsWriter().addDocument(doc);
-
-            // Create the document for the content (properties) ...
-            doc = new Document();
-            doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-            String stringValue = null;
-            StringBuilder fullTextSearchValue = null;
-            for (Property property : node.getProperties()) {
-                Name name = property.getName();
-                Rule rule = rules.getRule(name);
-                if (rule.isSkipped()) continue;
-                String nameString = stringFactory.create(name);
-                if (rule.isDate()) {
-                    for (Object value : property) {
-                        if (value == null) continue;
-                        DateTime dateValue = dateFactory.create(value);
-                        // Add a separate field for each property value ...
-                        doc.add(new NumericField(nameString, rule.getStoreOption(), true).setLongValue(dateValue.getMillisecondsInUtc()));
-                        // Dates are not added to the full-text search field (since this wouldn't make sense)
-                    }
-                    continue;
-                }
-                for (Object value : property) {
-                    if (value == null) continue;
-                    if (value instanceof Binary) {
-                        // don't include binary values as individual fields but do include them in the full-text search ...
-                        // TODO : add to full-text search ...
-                        continue;
-                    }
-                    stringValue = stringFactory.create(value);
-                    // Add a separate field for each property value ...
-                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-
-                    if (rule.isFullText()) {
-                        // Add this text to the full-text field ...
-                        if (fullTextSearchValue == null) {
-                            fullTextSearchValue = new StringBuilder();
-                        } else {
-                            fullTextSearchValue.append(' ');
-                        }
-                        fullTextSearchValue.append(stringValue);
-
-                        // Also create a full-text-searchable field ...
-                        String fullTextNameString = fullTextFieldName(nameString);
-                        doc.add(new Field(fullTextNameString, stringValue, Store.NO, Index.ANALYZED));
-                    }
-                }
-            }
-            // Add the full-text-search field ...
-            if (fullTextSearchValue != null) {
-                doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
-            }
-            getContentWriter().addDocument(doc);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#optimize()
-         */
-        public void optimize() throws IOException {
-            getContentWriter().optimize();
-            getPathsWriter().optimize();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#apply(java.lang.Iterable)
-         */
-        public int apply( Iterable<ChangeRequest> changes ) /*throws IOException*/{
-            for (ChangeRequest change : changes) {
-                if (change != null) continue;
-            }
-            return 0;
-        }
-
-        /**
-         * {@inheritDoc}
-         * <p>
-         * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes,
-         * we need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below
-         * a certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
-         * documents in the content index that represent those same nodes.
-         * </p>
-         * <p>
-         * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the
-         * number of results to a maximum number. We repeat batches as long as we find more results. This approach has the
-         * advantage that we'll never bring in a large number of results, and it allows us to delete the documents from the
-         * content node using a query.
-         * </p>
-         * 
-         * @see org.jboss.dna.search.IndexSession#deleteBelow(org.jboss.dna.graph.property.Path)
-         */
-        public int deleteBelow( Path path ) throws IOException {
-            assert !readOnly;
-            // Perform a query using the reader to find those nodes at/below the path ...
-            try {
-                IndexReader pathReader = getPathsReader();
-                IndexSearcher pathSearcher = new IndexSearcher(pathReader);
-                String pathStr = stringFactory.create(path) + "/";
-                PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
-                int numberDeleted = 0;
-                while (true) {
-                    // Execute the query and get the results ...
-                    TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
-                    int numResultsInBatch = results.scoreDocs.length;
-                    // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
-                    IndexReader contentReader = getContentReader();
-                    for (ScoreDoc result : results.scoreDocs) {
-                        int docId = result.doc;
-                        // Find the UUID of the node ...
-                        Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
-                        String uuid = doc.get(PathIndex.UUID);
-                        // Delete the document from the paths index ...
-                        pathReader.deleteDocument(docId);
-                        // Delete the corresponding document from the content index ...
-                        contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
-                    }
-                    numberDeleted += numResultsInBatch;
-                    if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
-                }
-                return numberDeleted;
-            } catch (FileNotFoundException e) {
-                // There are no index files yet, so nothing to delete ...
-                return 0;
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#search(org.jboss.dna.graph.ExecutionContext, java.lang.String, int, int,
-         *      java.util.List)
-         */
-        public void search( ExecutionContext context,
-                            String fullTextString,
-                            int maxResults,
-                            int offset,
-                            List<Location> results ) throws IOException, ParseException {
-            assert fullTextString != null;
-            assert fullTextString.length() > 0;
-            assert offset >= 0;
-            assert maxResults > 0;
-            assert results != null;
-
-            // Parse the full-text search and search against the 'fts' field ...
-            QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
-            Query query = parser.parse(fullTextString);
-            TopDocs docs = getContentSearcher().search(query, maxResults + offset);
-
-            // Collect the results ...
-            IndexReader contentReader = getContentReader();
-            IndexReader pathReader = getPathsReader();
-            IndexSearcher pathSearcher = getPathsSearcher();
-            ScoreDoc[] scoreDocs = docs.scoreDocs;
-            int numberOfResults = scoreDocs.length;
-            if (numberOfResults > offset) {
-                // There are enough results to satisfy the offset ...
-                PathFactory pathFactory = context.getValueFactories().getPathFactory();
-                for (int i = offset, num = scoreDocs.length; i != num; ++i) {
-                    ScoreDoc result = scoreDocs[i];
-                    int docId = result.doc;
-                    // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
-                    Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
-                    String uuid = doc.get(ContentIndex.UUID);
-                    // Find the path for this node (is there a better way to do this than one search per UUID?) ...
-                    TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
-                    if (pathDocs.scoreDocs.length < 1) {
-                        // No path record found ...
-                        continue;
-                    }
-                    Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
-                    String pathString = pathDoc.get(PathIndex.PATH);
-                    Path path = pathFactory.create(pathString);
-                    // Now add the location ...
-                    results.add(Location.create(path, UUID.fromString(uuid)));
-                }
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#query(org.jboss.dna.graph.query.QueryContext,
-         *      org.jboss.dna.graph.query.model.QueryCommand)
-         */
-        public QueryResults query( QueryContext queryContext,
-                                   QueryCommand query ) {
-            return queryEngine.execute(queryContext, query);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#commit()
-         */
-        public void commit() throws IOException {
-            IOException ioError = null;
-            RuntimeException runtimeError = null;
-            if (pathsReader != null) {
-                try {
-                    pathsReader.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    pathsReader = null;
-                }
-            }
-            if (contentReader != null) {
-                try {
-                    contentReader.close();
-                } catch (IOException e) {
-                    if (ioError == null) ioError = e;
-                } catch (RuntimeException e) {
-                    if (runtimeError == null) runtimeError = e;
-                } finally {
-                    contentReader = null;
-                }
-            }
-            if (pathsWriter != null) {
-                try {
-                    pathsWriter.commit();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    try {
-                        pathsWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        pathsWriter = null;
-                    }
-                }
-            }
-            if (contentWriter != null) {
-                try {
-                    contentWriter.commit();
-                } catch (IOException e) {
-                    if (ioError == null) ioError = e;
-                } catch (RuntimeException e) {
-                    if (runtimeError == null) runtimeError = e;
-                } finally {
-                    try {
-                        contentWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        contentWriter = null;
-                    }
-                }
-            }
-            if (ioError != null) throw ioError;
-            if (runtimeError != null) throw runtimeError;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexSession#rollback()
-         */
-        public void rollback() throws IOException {
-            IOException ioError = null;
-            RuntimeException runtimeError = null;
-            if (pathsReader != null) {
-                try {
-                    pathsReader.close();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    pathsReader = null;
-                }
-            }
-            if (contentReader != null) {
-                try {
-                    contentReader.close();
-                } catch (IOException e) {
-                    if (ioError == null) ioError = e;
-                } catch (RuntimeException e) {
-                    if (runtimeError == null) runtimeError = e;
-                } finally {
-                    contentReader = null;
-                }
-            }
-            if (pathsWriter != null) {
-                try {
-                    pathsWriter.rollback();
-                } catch (IOException e) {
-                    ioError = e;
-                } catch (RuntimeException e) {
-                    runtimeError = e;
-                } finally {
-                    try {
-                        pathsWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        pathsWriter = null;
-                    }
-                }
-            }
-            if (contentWriter != null) {
-                try {
-                    contentWriter.rollback();
-                } catch (IOException e) {
-                    if (ioError == null) ioError = e;
-                } catch (RuntimeException e) {
-                    if (runtimeError == null) runtimeError = e;
-                } finally {
-                    try {
-                        contentWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        contentWriter = null;
-                    }
-                }
-            }
-            if (ioError != null) throw ioError;
-            if (runtimeError != null) throw runtimeError;
-        }
-
-        protected QueryEngine createQueryProcessor() {
-            // Create the query engine ...
-            Planner planner = new CanonicalPlanner();
-            Optimizer optimizer = new RuleBasedOptimizer() {
-                /**
-                 * {@inheritDoc}
-                 * 
-                 * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
-                 *      org.jboss.dna.graph.query.plan.PlanHints)
-                 */
-                @Override
-                protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
-                                                  PlanHints hints ) {
-                    super.populateRuleStack(ruleStack, hints);
-                    // Add any custom rules here, either at the front of the stack or at the end
-                }
-            };
-            QueryProcessor processor = new QueryProcessor() {
-                /**
-                 * {@inheritDoc}
-                 * 
-                 * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
-                 *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
-                 *      org.jboss.dna.graph.query.QueryResults.Columns,
-                 *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-                 */
-                @Override
-                protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
-                                                                     QueryContext context,
-                                                                     PlanNode accessNode,
-                                                                     Columns resultColumns,
-                                                                     org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
-                    try {
-                        return LuceneSession.this.createAccessComponent(originalQuery,
-                                                                        context,
-                                                                        accessNode,
-                                                                        resultColumns,
-                                                                        analyzer);
-                    } catch (IOException e) {
-                        I18n msg = SearchI18n.errorWhilePerformingQuery;
-                        context.getProblems().addError(e,
-                                                       msg,
-                                                       Visitors.readable(originalQuery),
-                                                       getWorkspaceName(),
-                                                       getSourceName(),
-                                                       e.getMessage());
-                        return null;
-                    }
-                }
-            };
-
-            return new QueryEngine(planner, optimizer, processor);
-        }
-
-        protected abstract ProcessingComponent createAccessComponent( QueryCommand originalQuery,
-                                                                      QueryContext context,
-                                                                      PlanNode accessNode,
-                                                                      Columns resultColumns,
-                                                                      org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer )
-            throws IOException;
-
-        /**
-         * Get the set of UUIDs for the children of the node at the given path.
-         * 
-         * @param parentPath the path to the parent node; may not be null
-         * @return the UUIDs of the child nodes; never null but possibly empty
-         * @throws IOException if there is an error accessing the indexes
-         */
-        protected Set<UUID> getUuidsForChildrenOf( Path parentPath ) throws IOException {
-            // Find the path of the parent ...
-            String stringifiedPath = pathAsString(parentPath, stringFactory);
-            // Append a '/' to the parent path, so we'll only get decendants ...
-            stringifiedPath = stringifiedPath + '/';
-
-            // Create a query to find all the nodes below the parent path ...
-            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
-            // Include only the children ...
-            int childrenDepth = parentPath.size() + 1;
-            Query depthQuery = NumericRangeQuery.newIntRange(PathIndex.DEPTH, childrenDepth, childrenDepth, true, true);
-            // And combine ...
-            BooleanQuery combinedQuery = new BooleanQuery();
-            combinedQuery.add(query, Occur.MUST);
-            combinedQuery.add(depthQuery, Occur.MUST);
-            query = combinedQuery;
-
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return uuidCollector.getUuids();
-        }
-
-        /**
-         * Get the set of UUIDs for the nodes that are descendants of the node at the given path.
-         * 
-         * @param parentPath the path to the parent node; may not be null and <i>may not be the root node</i>
-         * @param includeParent true if the parent node should be included in the results, or false if only the descendants should
-         *        be included
-         * @return the UUIDs of the nodes; never null but possibly empty
-         * @throws IOException if there is an error accessing the indexes
-         */
-        protected Set<UUID> getUuidsForDescendantsOf( Path parentPath,
-                                                      boolean includeParent ) throws IOException {
-            assert !parentPath.isRoot();
-
-            // Find the path of the parent ...
-            String stringifiedPath = pathAsString(parentPath, stringFactory);
-            if (!includeParent) {
-                // Append a '/' to the parent path, and we'll only get decendants ...
-                stringifiedPath = stringifiedPath + '/';
-            }
-
-            // Create a prefix query ...
-            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
-
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return uuidCollector.getUuids();
-        }
-
-        /**
-         * Get the set containing the single UUID for the node at the given path.
-         * 
-         * @param path the path to the node; may not be null
-         * @return the UUID of the supplied node; or null if the node cannot be found
-         * @throws IOException if there is an error accessing the indexes
-         */
-        protected UUID getUuidFor( Path path ) throws IOException {
-            // Create a query to find all the nodes below the parent path ...
-            IndexSearcher searcher = getPathsSearcher();
-            String stringifiedPath = pathAsString(path, stringFactory);
-            TermQuery query = new TermQuery(new Term(PathIndex.PATH, stringifiedPath));
-
-            // Now execute and collect the UUIDs ...
-            TopDocs topDocs = searcher.search(query, 1);
-            if (topDocs.totalHits == 0) return null;
-            Document pathDoc = getPathsReader().document(topDocs.scoreDocs[0].doc);
-            String uuidString = pathDoc.get(PathIndex.UUID);
-            return UUID.fromString(uuidString);
-        }
-
-        /**
-         * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
-         * 
-         * @param uuids the UUIDs of the nodes that are to be found; may not be null
-         * @return the query; never null
-         */
-        protected Query findAllNodesWithUuids( Set<UUID> uuids ) {
-            if (uuids.isEmpty()) {
-                // There are no children, so return a null query ...
-                return new MatchNoneQuery();
-            }
-            if (uuids.size() == 1) {
-                UUID uuid = uuids.iterator().next();
-                if (uuid == null) return new MatchNoneQuery();
-                return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
-            }
-            if (uuids.size() < 50) {
-                // Create an OR boolean query for all the UUIDs, since this is probably more efficient ...
-                BooleanQuery query = new BooleanQuery();
-                for (UUID uuid : uuids) {
-                    Query uuidQuery = new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
-                    query.add(uuidQuery, Occur.SHOULD);
-                }
-                return query;
-            }
-            // Returna query that will always find all of the UUIDs ...
-            return new UuidsQuery(ContentIndex.UUID, uuids, getContext().getValueFactories().getUuidFactory());
-        }
-
-        protected Query findAllNodesBelow( Path ancestorPath ) throws IOException {
-            if (ancestorPath.isRoot()) {
-                return new MatchAllDocsQuery();
-            }
-            Set<UUID> uuids = getUuidsForDescendantsOf(ancestorPath, false);
-            return findAllNodesWithUuids(uuids);
-        }
-
-        /**
-         * Return a query that can be used to find all of the documents that represent nodes that are children of the node at the
-         * supplied path.
-         * 
-         * @param parentPath the path of the parent node.
-         * @return the query; never null
-         * @throws IOException if there is an error finding the UUIDs of the child nodes
-         */
-        protected Query findChildNodes( Path parentPath ) throws IOException {
-            if (parentPath.isRoot()) {
-                return new MatchAllDocsQuery();
-            }
-            Set<UUID> childUuids = getUuidsForChildrenOf(parentPath);
-            return findAllNodesWithUuids(childUuids);
-        }
-
-        /**
-         * Create a query that can be used to find the one document (or node) that exists at the exact path supplied. This method
-         * first queries the {@link PathIndex path index} to find the UUID of the node at the supplied path, and then returns a
-         * query that matches the UUID.
-         * 
-         * @param path the path of the node
-         * @return the query; never null
-         * @throws IOException if there is an error finding the UUID for the supplied path
-         */
-        protected Query findNodeAt( Path path ) throws IOException {
-            UUID uuid = getUuidFor(path);
-            if (uuid == null) return null;
-            return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
-        }
-
-        /**
-         * Create a query that can be used to find documents (or nodes) that have a field value that satisfies the supplied LIKE
-         * expression.
-         * 
-         * @param fieldName the name of the document field to search
-         * @param likeExpression the JCR like expression
-         * @return the query; never null
-         */
-        protected Query findNodesLike( String fieldName,
-                                       String likeExpression ) {
-            assert likeExpression != null;
-            assert likeExpression.length() > 0;
-
-            // '%' matches 0 or more characters
-            // '_' matches any single character
-            // '\x' matches 'x'
-            // all other characters match themselves
-
-            // Wildcard queries are a better match, but they can be slow and should not be used
-            // if the first character of the expression is a '%' or '_' ...
-            char firstChar = likeExpression.charAt(0);
-            if (firstChar != '%' && firstChar != '_') {
-                // Create a wildcard query ...
-                String expression = toWildcardExpression(likeExpression);
-                return new WildcardQuery(new Term(fieldName, expression));
-            }
-            // Create a regex query,
-            String regex = toRegularExpression(likeExpression);
-            RegexQuery query = new RegexQuery(new Term(fieldName, regex));
-            query.setRegexImplementation(new JavaUtilRegexCapabilities());
-            return query;
-        }
-
-        protected Query findNodesWith( Length propertyLength,
-                                       Operator operator,
-                                       Object value ) {
-            assert propertyLength != null;
-            assert value != null;
-            PropertyValue propertyValue = propertyLength.getPropertyValue();
-            String field = stringFactory.create(propertyValue.getPropertyName());
-            ValueFactories factories = context.getValueFactories();
-            int length = factories.getLongFactory().create(value).intValue();
-            switch (operator) {
-                case EQUAL_TO:
-                    return CompareLengthQuery.createQueryForNodesWithFieldEqualTo(length, field, factories);
-                case NOT_EQUAL_TO:
-                    return CompareLengthQuery.createQueryForNodesWithFieldNotEqualTo(length, field, factories);
-                case GREATER_THAN:
-                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThan(length, field, factories);
-                case GREATER_THAN_OR_EQUAL_TO:
-                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(length, field, factories);
-                case LESS_THAN:
-                    return CompareLengthQuery.createQueryForNodesWithFieldLessThan(length, field, factories);
-                case LESS_THAN_OR_EQUAL_TO:
-                    return CompareLengthQuery.createQueryForNodesWithFieldLessThanOrEqualTo(length, field, factories);
-                case LIKE:
-                    // This is not allowed ...
-                    assert false;
-                    break;
-            }
-            return null;
-        }
-
-        protected Query findNodesWith( PropertyValue propertyValue,
-                                       Operator operator,
-                                       Object value,
-                                       boolean caseSensitive ) {
-            String field = stringFactory.create(propertyValue.getPropertyName());
-            PropertyType valueType = PropertyType.discoverType(value);
-            ValueFactories factories = context.getValueFactories();
-            switch (valueType) {
-                case NAME:
-                case PATH:
-                case REFERENCE:
-                case URI:
-                case UUID:
-                case STRING:
-                    String stringValue = stringFactory.create(value);
-                    if (valueType == PropertyType.PATH) {
-                        stringValue = pathAsString(pathFactory.create(value), stringFactory);
-                    }
-                    if (!caseSensitive) stringValue = stringValue.toLowerCase();
-                    switch (operator) {
-                        case EQUAL_TO:
-                            return new TermQuery(new Term(field, stringValue));
-                        case NOT_EQUAL_TO:
-                            Query query = new TermQuery(new Term(field, stringValue));
-                            return new NotQuery(query);
-                        case GREATER_THAN:
-                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThan(stringValue,
-                                                                                              field,
-                                                                                              factories,
-                                                                                              caseSensitive);
-                        case GREATER_THAN_OR_EQUAL_TO:
-                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(stringValue,
-                                                                                                       field,
-                                                                                                       factories,
-                                                                                                       caseSensitive);
-                        case LESS_THAN:
-                            return CompareStringQuery.createQueryForNodesWithFieldLessThan(stringValue,
-                                                                                           field,
-                                                                                           factories,
-                                                                                           caseSensitive);
-                        case LESS_THAN_OR_EQUAL_TO:
-                            return CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(stringValue,
-                                                                                                    field,
-                                                                                                    factories,
-                                                                                                    caseSensitive);
-                        case LIKE:
-                            return findNodesLike(field, stringValue);
-                    }
-                    break;
-                case DATE:
-                    long date = factories.getLongFactory().create(value);
-                    switch (operator) {
-                        case EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, date, date, true, true);
-                        case NOT_EQUAL_TO:
-                            Query query = NumericRangeQuery.newLongRange(field, date, date, true, true);
-                            return new NotQuery(query);
-                        case GREATER_THAN:
-                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, false, true);
-                        case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, true, true);
-                        case LESS_THAN:
-                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, false);
-                        case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, true);
-                        case LIKE:
-                            // This is not allowed ...
-                            assert false;
-                            return null;
-                    }
-                    break;
-                case LONG:
-                    long longValue = factories.getLongFactory().create(value);
-                    switch (operator) {
-                        case EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
-                        case NOT_EQUAL_TO:
-                            Query query = NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
-                            return new NotQuery(query);
-                        case GREATER_THAN:
-                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, false, true);
-                        case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, true, true);
-                        case LESS_THAN:
-                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, false);
-                        case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, true);
-                        case LIKE:
-                            // This is not allowed ...
-                            assert false;
-                            return null;
-                    }
-                    break;
-                case DECIMAL:
-                case DOUBLE:
-                    double doubleValue = factories.getDoubleFactory().create(value);
-                    switch (operator) {
-                        case EQUAL_TO:
-                            return NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
-                        case NOT_EQUAL_TO:
-                            Query query = NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
-                            return new NotQuery(query);
-                        case GREATER_THAN:
-                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, false, true);
-                        case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, true, true);
-                        case LESS_THAN:
-                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, false);
-                        case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, true);
-                        case LIKE:
-                            // This is not allowed ...
-                            assert false;
-                            return null;
-                    }
-                    break;
-                case BOOLEAN:
-                    boolean booleanValue = factories.getBooleanFactory().create(value);
-                    stringValue = stringFactory.create(value);
-                    switch (operator) {
-                        case EQUAL_TO:
-                            return new TermQuery(new Term(field, stringValue));
-                        case NOT_EQUAL_TO:
-                            return new TermQuery(new Term(field, stringFactory.create(!booleanValue)));
-                        case GREATER_THAN:
-                            if (!booleanValue) {
-                                return new TermQuery(new Term(field, stringFactory.create(true)));
-                            }
-                            // Can't be greater than 'true', per JCR spec
-                            return new MatchNoneQuery();
-                        case GREATER_THAN_OR_EQUAL_TO:
-                            return new TermQuery(new Term(field, stringFactory.create(true)));
-                        case LESS_THAN:
-                            if (booleanValue) {
-                                return new TermQuery(new Term(field, stringFactory.create(false)));
-                            }
-                            // Can't be less than 'false', per JCR spec
-                            return new MatchNoneQuery();
-                        case LESS_THAN_OR_EQUAL_TO:
-                            return new TermQuery(new Term(field, stringFactory.create(false)));
-                        case LIKE:
-                            // This is not allowed ...
-                            assert false;
-                            return null;
-                    }
-                    break;
-                case OBJECT:
-                case BINARY:
-                    // This is not allowed ...
-                    assert false;
-                    return null;
-            }
-            return null;
-        }
-
-        protected Query findNodesWithNumericRange( PropertyValue propertyValue,
-                                                   Object lowerValue,
-                                                   Object upperValue,
-                                                   boolean includesLower,
-                                                   boolean includesUpper ) {
-            String field = stringFactory.create(propertyValue.getPropertyName());
-            return findNodesWithNumericRange(field, lowerValue, upperValue, includesLower, includesUpper);
-        }
-
-        protected Query findNodesWithNumericRange( NodeDepth depth,
-                                                   Object lowerValue,
-                                                   Object upperValue,
-                                                   boolean includesLower,
-                                                   boolean includesUpper ) {
-            return findNodesWithNumericRange(PathIndex.DEPTH, lowerValue, upperValue, includesLower, includesUpper);
-        }
-
-        protected Query findNodesWithNumericRange( String field,
-                                                   Object lowerValue,
-                                                   Object upperValue,
-                                                   boolean includesLower,
-                                                   boolean includesUpper ) {
-            PropertyType type = PropertyType.discoverType(lowerValue);
-            assert type == PropertyType.discoverType(upperValue);
-            ValueFactories factories = context.getValueFactories();
-            switch (type) {
-                case DATE:
-                    long lowerDate = factories.getLongFactory().create(lowerValue);
-                    long upperDate = factories.getLongFactory().create(upperValue);
-                    return NumericRangeQuery.newLongRange(field, lowerDate, upperDate, includesLower, includesUpper);
-                case LONG:
-                    long lowerLong = factories.getLongFactory().create(lowerValue);
-                    long upperLong = factories.getLongFactory().create(upperValue);
-                    return NumericRangeQuery.newLongRange(field, lowerLong, upperLong, includesLower, includesUpper);
-                case DECIMAL:
-                case DOUBLE:
-                    double lowerDouble = factories.getDoubleFactory().create(lowerValue);
-                    double upperDouble = factories.getDoubleFactory().create(upperValue);
-                    return NumericRangeQuery.newDoubleRange(field, lowerDouble, upperDouble, includesLower, includesUpper);
-                default:
-                    // This is not allowed ...
-                    assert false;
-                    return null;
-            }
-        }
-
-        protected Query findNodesWith( NodePath nodePath,
-                                       Operator operator,
-                                       Object value,
-                                       boolean caseSensitive ) throws IOException {
-            if (!caseSensitive) value = stringFactory.create(value).toLowerCase();
-            Path pathValue = operator != Operator.LIKE ? pathFactory.create(value) : null;
-            Query query = null;
-            switch (operator) {
-                case EQUAL_TO:
-                    return findNodeAt(pathValue);
-                case NOT_EQUAL_TO:
-                    return new NotQuery(findNodeAt(pathValue));
-                case LIKE:
-                    String likeExpression = stringFactory.create(value);
-                    return findNodesLike(PathIndex.PATH, likeExpression);
-                case GREATER_THAN:
-                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThan(pathValue,
-                                                                                    PathIndex.PATH,
-                                                                                    context.getValueFactories(),
-                                                                                    caseSensitive);
-                    break;
-                case GREATER_THAN_OR_EQUAL_TO:
-                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThanOrEqualTo(pathValue,
-                                                                                             PathIndex.PATH,
-                                                                                             context.getValueFactories(),
-                                                                                             caseSensitive);
-                    break;
-                case LESS_THAN:
-                    query = ComparePathQuery.createQueryForNodesWithPathLessThan(pathValue,
-                                                                                 PathIndex.PATH,
-                                                                                 context.getValueFactories(),
-                                                                                 caseSensitive);
-                    break;
-                case LESS_THAN_OR_EQUAL_TO:
-                    query = ComparePathQuery.createQueryForNodesWithPathLessThanOrEqualTo(pathValue,
-                                                                                          PathIndex.PATH,
-                                                                                          context.getValueFactories(),
-                                                                                          caseSensitive);
-                    break;
-            }
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
-        }
-
-        protected Query findNodesWith( NodeName nodeName,
-                                       Operator operator,
-                                       Object value,
-                                       boolean caseSensitive ) throws IOException {
-            String stringValue = stringFactory.create(value);
-            if (!caseSensitive) stringValue = stringValue.toLowerCase();
-            Path.Segment segment = operator != Operator.LIKE ? pathFactory.createSegment(stringValue) : null;
-            int snsIndex = operator != Operator.LIKE ? segment.getIndex() : 0;
-            Query query = null;
-            switch (operator) {
-                case EQUAL_TO:
-                    BooleanQuery booleanQuery = new BooleanQuery();
-                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
-                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
-                                     Occur.MUST);
-                    return booleanQuery;
-                case NOT_EQUAL_TO:
-                    booleanQuery = new BooleanQuery();
-                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
-                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
-                                     Occur.MUST);
-                    return new NotQuery(booleanQuery);
-                case GREATER_THAN:
-                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThan(segment,
-                                                                                    PathIndex.LOCAL_NAME,
-                                                                                    PathIndex.SNS_INDEX,
-                                                                                    context.getValueFactories(),
-                                                                                    caseSensitive);
-                    break;
-                case GREATER_THAN_OR_EQUAL_TO:
-                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThanOrEqualTo(segment,
-                                                                                             PathIndex.LOCAL_NAME,
-                                                                                             PathIndex.SNS_INDEX,
-                                                                                             context.getValueFactories(),
-                                                                                             caseSensitive);
-                    break;
-                case LESS_THAN:
-                    query = CompareNameQuery.createQueryForNodesWithNameLessThan(segment,
-                                                                                 PathIndex.LOCAL_NAME,
-                                                                                 PathIndex.SNS_INDEX,
-                                                                                 context.getValueFactories(),
-                                                                                 caseSensitive);
-                    break;
-                case LESS_THAN_OR_EQUAL_TO:
-                    query = CompareNameQuery.createQueryForNodesWithNameLessThanOrEqualTo(segment,
-                                                                                          PathIndex.LOCAL_NAME,
-                                                                                          PathIndex.SNS_INDEX,
-                                                                                          context.getValueFactories(),
-                                                                                          caseSensitive);
-                    break;
-                case LIKE:
-                    // See whether the like expression has brackets ...
-                    String likeExpression = stringValue;
-                    int openBracketIndex = likeExpression.indexOf('[');
-                    if (openBracketIndex != -1) {
-                        String localNameExpression = likeExpression.substring(0, openBracketIndex);
-                        String snsIndexExpression = likeExpression.substring(openBracketIndex);
-                        Query localNameQuery = createLocalNameQuery(localNameExpression);
-                        Query snsQuery = createSnsIndexQuery(snsIndexExpression);
-                        if (localNameQuery == null) {
-                            if (snsQuery == null) {
-                                query = new MatchNoneQuery();
-                            } else {
-                                // There is just an SNS part ...
-                                query = snsQuery;
-                            }
-                        } else {
-                            // There is a local name part ...
-                            if (snsQuery == null) {
-                                query = localNameQuery;
-                            } else {
-                                // There is both a local name part and a SNS part ...
-                                booleanQuery = new BooleanQuery();
-                                booleanQuery.add(localNameQuery, Occur.MUST);
-                                booleanQuery.add(snsQuery, Occur.MUST);
-                                query = booleanQuery;
-                            }
-                        }
-                    } else {
-                        // There is no SNS expression ...
-                        query = createLocalNameQuery(likeExpression);
-                    }
-                    assert query != null;
-                    break;
-            }
-
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
-        }
-
-        protected Query findNodesWith( NodeLocalName nodeName,
-                                       Operator operator,
-                                       Object value,
-                                       boolean caseSensitive ) throws IOException {
-            String nameValue = stringFactory.create(value);
-            Query query = null;
-            switch (operator) {
-                case LIKE:
-                    String likeExpression = stringFactory.create(value);
-                    return findNodesLike(PathIndex.LOCAL_NAME, likeExpression); // already is a query with UUIDs
-                case EQUAL_TO:
-                    query = new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue));
-                    break;
-                case NOT_EQUAL_TO:
-                    query = new NotQuery(new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue)));
-                    break;
-                case GREATER_THAN:
-                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThan(nameValue,
-                                                                                       PathIndex.LOCAL_NAME,
-                                                                                       context.getValueFactories(),
-                                                                                       caseSensitive);
-                    break;
-                case GREATER_THAN_OR_EQUAL_TO:
-                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(nameValue,
-                                                                                                PathIndex.LOCAL_NAME,
-                                                                                                context.getValueFactories(),
-                                                                                                caseSensitive);
-                    break;
-                case LESS_THAN:
-                    query = CompareStringQuery.createQueryForNodesWithFieldLessThan(nameValue,
-                                                                                    PathIndex.LOCAL_NAME,
-                                                                                    context.getValueFactories(),
-                                                                                    caseSensitive);
-                    break;
-                case LESS_THAN_OR_EQUAL_TO:
-                    query = CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(nameValue,
-                                                                                             PathIndex.LOCAL_NAME,
-                                                                                             context.getValueFactories(),
-                                                                                             caseSensitive);
-                    break;
-            }
-
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
-        }
-
-        protected Query findNodesWith( NodeDepth depthConstraint,
-                                       Operator operator,
-                                       Object value ) throws IOException {
-            int depth = context.getValueFactories().getLongFactory().create(value).intValue();
-            Query query = null;
-            switch (operator) {
-                case EQUAL_TO:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
-                    break;
-                case NOT_EQUAL_TO:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
-                    query = new NotQuery(query);
-                    break;
-                case GREATER_THAN:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, false, true);
-                    break;
-                case GREATER_THAN_OR_EQUAL_TO:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, true, true);
-                    break;
-                case LESS_THAN:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, false);
-                    break;
-                case LESS_THAN_OR_EQUAL_TO:
-                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, true);
-                    break;
-                case LIKE:
-                    // This is not allowed ...
-                    return null;
-            }
-
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
-            IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
-        }
-
-        protected Query createLocalNameQuery( String likeExpression ) {
-            if (likeExpression == null) return null;
-            likeExpression = likeExpression.trim();
-            if (likeExpression.length() == 0) return null;
-            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
-                // The local name is a like ...
-                return findNodesLike(PathIndex.LOCAL_NAME, likeExpression);
-            }
-            // The local name is an exact match ...
-            return new TermQuery(new Term(PathIndex.LOCAL_NAME, likeExpression));
-        }
-
-        protected Query createSnsIndexQuery( String likeExpression ) {
-            if (likeExpression == null) return null;
-            likeExpression = likeExpression.trim();
-            if (likeExpression.length() == 0) return null;
-
-            // Remove the leading '[' ...
-            assert likeExpression.charAt(0) == '[';
-            likeExpression = likeExpression.substring(1);
-
-            // Remove the trailing ']' if it exists ...
-            int closeBracketIndex = likeExpression.indexOf(']');
-            if (closeBracketIndex != -1) {
-                likeExpression = likeExpression.substring(0, closeBracketIndex);
-            }
-            // If SNS expression contains '?' or '*' ...
-            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
-                // There is a LIKE expression for the SNS ...
-                return findNodesLike(PathIndex.SNS_INDEX, likeExpression);
-            }
-            // This is not a LIKE expression but an exact value specification and should be a number ...
-            try {
-                // This SNS is just a number ...
-                int sns = Integer.parseInt(likeExpression);
-                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, sns, sns, true, false);
-            } catch (NumberFormatException e) {
-                // It's not a number but it's in the SNS field, so there will be no results ...
-                return new MatchNoneQuery();
-            }
-        }
-
-    }
-
-    /**
-     * Convert the JCR like expression to a Lucene wildcard expression. The JCR like expression uses '%' to match 0 or more
-     * characters, '_' to match any single character, '\x' to match the 'x' character, and all other characters to match
-     * themselves.
-     * 
-     * @param likeExpression the like expression; may not be null
-     * @return the expression that can be used with a WildcardQuery; never null
-     */
-    protected static String toWildcardExpression( String likeExpression ) {
-        assert likeExpression != null;
-        assert likeExpression.length() > 0;
-        return likeExpression.replace('%', '*').replace('_', '?').replaceAll("\\\\(.)", "$1");
-    }
-
-    /**
-     * Convert the JCR like expression to a regular expression. The JCR like expression uses '%' to match 0 or more characters,
-     * '_' to match any single character, '\x' to match the 'x' character, and all other characters to match themselves. Note that
-     * if any regex metacharacters appear in the like expression, they will be escaped within the resulting regular expression.
-     * 
-     * @param likeExpression the like expression; may not be null
-     * @return the expression that can be used with a WildcardQuery; never null
-     */
-    protected static String toRegularExpression( String likeExpression ) {
-        assert likeExpression != null;
-        assert likeExpression.length() > 0;
-        // Replace all '\x' with 'x' ...
-        String result = likeExpression.replaceAll("\\\\(.)", "$1");
-        // Escape characters used as metacharacters in regular expressions, including
-        // '[', '^', '\', '$', '.', '|', '?', '*', '+', '(', and ')'
-        result = result.replaceAll("([[^\\\\$.|?*+()])", "\\$1");
-        // Replace '%'->'[.]+' and '_'->'[.]
-        result = likeExpression.replace("%", "[.]+").replace("_", "[.]");
-        return result;
-    }
-
-    protected static String pathAsString( Path path,
-                                          ValueFactory<String> stringFactory ) {
-        assert path != null;
-        if (path.isRoot()) return "/";
-        String pathStr = stringFactory.create(path);
-        if (!pathStr.endsWith("]")) {
-            pathStr = pathStr + '[' + Path.DEFAULT_INDEX + ']';
-        }
-        return pathStr;
-    }
-
-    /**
-     * A {@link Collector} implementation that only captures the UUID of the documents returned by a query. Score information is
-     * not recorded. This is often used when querying the {@link PathIndex} to collect the UUIDs of a set of nodes satisfying some
-     * path constraint.
-     * 
-     * @see DualIndexLayout.LuceneSession#findChildNodes(Path)
-     */
-    protected static class UuidCollector extends Collector {
-        private final Set<UUID> uuids = new HashSet<UUID>();
-        private String[] uuidsByDocId;
-        private int baseDocId;
-
-        protected UuidCollector() {
-        }
-
-        /**
-         * Get the UUIDs that have been collected.
-         * 
-         * @return the set of UUIDs; never null
-         */
-        public Set<UUID> getUuids() {
-            return uuids;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
-         */
-        @Override
-        public boolean acceptsDocsOutOfOrder() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
-         */
-        @Override
-        public void setScorer( Scorer scorer ) {
-            // we don't care about scoring
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#collect(int)
-         */
-        @Override
-        public void collect( int doc ) {
-            int index = doc - baseDocId;
-            assert index >= 0;
-            String uuidString = uuidsByDocId[index];
-            assert uuidString != null;
-            uuids.add(UUID.fromString(uuidString));
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
-         */
-        @Override
-        public void setNextReader( IndexReader reader,
-                                   int docBase ) throws IOException {
-            this.uuidsByDocId = FieldCache.DEFAULT.getStrings(reader, UUID_FIELD);
-            this.baseDocId = docBase;
-        }
-    }
-}

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexLayout.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,1578 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.document.NumericField;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.WildcardQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
+import org.apache.lucene.search.regex.RegexQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Version;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.search.SearchException;
+import org.jboss.dna.graph.search.SearchProvider;
+import org.jboss.dna.search.IndexRules.Rule;
+import org.jboss.dna.search.query.CompareLengthQuery;
+import org.jboss.dna.search.query.CompareNameQuery;
+import org.jboss.dna.search.query.ComparePathQuery;
+import org.jboss.dna.search.query.CompareStringQuery;
+import org.jboss.dna.search.query.MatchNoneQuery;
+import org.jboss.dna.search.query.NotQuery;
+import org.jboss.dna.search.query.UuidsQuery;
+
+/**
+ * A simple {@link SearchProvider} implementation that relies upon two separate indexes: one for the node content and a second one
+ * for paths and UUIDs.
+ */
+ at ThreadSafe
+public class DualIndexSearchProvider implements SearchProvider {
+
+    /**
+     * The default set of {@link IndexRules} used by {@link DualIndexSearchProvider} instances when no rules are provided. These
+     * rules default to index and analyze all properties, and to index the {@link DnaLexicon#UUID dna:uuid} and
+     * {@link JcrLexicon#UUID jcr:uuid} properties to be indexed and stored only (not analyzed and not included in full-text
+     * search. The rules also treat {@link JcrLexicon#CREATED jcr:created} and {@link JcrLexicon#LAST_MODIFIED jcr:lastModified}
+     * properties as dates.
+     */
+    public static final IndexRules DEFAULT_RULES;
+
+    static {
+        IndexRules.Builder builder = IndexRules.createBuilder();
+        // Configure the default behavior ...
+        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
+        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+        builder.store(JcrLexicon.UUID, DnaLexicon.UUID);
+        // Configure the properties that we'll treat as dates ...
+        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+        DEFAULT_RULES = builder.build();
+    }
+
+    protected static final long MIN_DATE = 0;
+    protected static final long MAX_DATE = Long.MAX_VALUE;
+    protected static final long MIN_LONG = Long.MIN_VALUE;
+    protected static final long MAX_LONG = Long.MAX_VALUE;
+    protected static final double MIN_DOUBLE = Double.MIN_VALUE;
+    protected static final double MAX_DOUBLE = Double.MAX_VALUE;
+    protected static final int MIN_DEPTH = 0;
+    protected static final int MAX_DEPTH = 100;
+
+    protected static final String PATHS_INDEX_NAME = "paths";
+    protected static final String CONTENT_INDEX_NAME = "content";
+
+    protected static final String UUID_FIELD = "uuid";
+    protected static final String FULL_TEXT_SUFFIX = "/fs"; // the slash character is not allowed in a property name unescaped
+
+    static class PathIndex {
+        public static final String PATH = "path";
+        public static final String LOCAL_NAME = "name";
+        public static final String SNS_INDEX = "sns";
+        public static final String UUID = UUID_FIELD;
+        public static final String DEPTH = "depth";
+    }
+
+    static class ContentIndex {
+        public static final String UUID = UUID_FIELD;
+        public static final String FULL_TEXT = "fts";
+    }
+
+    /**
+     * The number of results that should be returned when performing queries while deleting entire branches of content. The
+     * current value is {@value} .
+     */
+    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
+
+    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+        }
+    };
+
+    /**
+     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
+     */
+    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
+        private static final long serialVersionUID = 1L;
+
+        public FieldSelectorResult accept( String fieldName ) {
+            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+        }
+    };
+
+    private final IndexRules rules;
+    private final LuceneConfiguration directoryConfiguration;
+
+    public DualIndexSearchProvider( LuceneConfiguration directoryConfiguration,
+                                    IndexRules rules ) {
+        assert directoryConfiguration != null;
+        assert rules != null;
+        this.rules = rules;
+        this.directoryConfiguration = directoryConfiguration;
+    }
+
+    public DualIndexSearchProvider( LuceneConfiguration directoryConfiguration ) {
+        this(directoryConfiguration, DEFAULT_RULES);
+    }
+
+    /**
+     * Get the date formatter that can be reused safely within the current thread.
+     * 
+     * @return the date formatter; never null
+     */
+    protected DateFormat dateFormatter() {
+        return dateFormatter.get();
+    }
+
+    /**
+     * Get the text encoder that should be used to encode namespaces in the search index.
+     * 
+     * @return the namespace text encoder; never null
+     */
+    protected TextEncoder getNamespaceEncoder() {
+        return new NoOpEncoder();
+    }
+
+    /**
+     * Create a Lucene {@link Analyzer} analyzer that should be used for indexing and searching.
+     * 
+     * @return the analyzer; never null
+     */
+    protected Analyzer createAnalyzer() {
+        return new StandardAnalyzer(Version.LUCENE_CURRENT);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider#createSession(org.jboss.dna.graph.ExecutionContext, java.lang.String,
+     *      java.lang.String, boolean, boolean)
+     */
+    public SearchProvider.Session createSession( ExecutionContext context,
+                                                 String sourceName,
+                                                 String workspaceName,
+                                                 boolean overwrite,
+                                                 boolean readOnly ) {
+        Directory pathIndexDirectory = directoryConfiguration.getDirectory(workspaceName, PATHS_INDEX_NAME);
+        Directory contentIndexDirectory = directoryConfiguration.getDirectory(workspaceName, CONTENT_INDEX_NAME);
+        assert pathIndexDirectory != null;
+        assert contentIndexDirectory != null;
+        Analyzer analyzer = createAnalyzer();
+        assert analyzer != null;
+        return new DualIndexSession(context, sourceName, workspaceName, rules, pathIndexDirectory, contentIndexDirectory,
+                                    analyzer, overwrite, readOnly);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider#destroyIndexes(org.jboss.dna.graph.ExecutionContext, java.lang.String,
+     *      java.lang.String)
+     */
+    public boolean destroyIndexes( ExecutionContext context,
+                                   String sourceName,
+                                   String workspaceName ) {
+        directoryConfiguration.destroyDirectory(workspaceName, PATHS_INDEX_NAME);
+        directoryConfiguration.destroyDirectory(workspaceName, CONTENT_INDEX_NAME);
+        return true;
+    }
+
+    protected class DualIndexSession extends LuceneSession {
+        private final Directory pathsIndexDirectory;
+        private final Directory contentIndexDirectory;
+        private IndexReader pathsReader;
+        private IndexWriter pathsWriter;
+        private IndexSearcher pathsSearcher;
+        private IndexReader contentReader;
+        private IndexWriter contentWriter;
+        private IndexSearcher contentSearcher;
+
+        protected DualIndexSession( ExecutionContext context,
+                                    String sourceName,
+                                    String workspaceName,
+                                    IndexRules rules,
+                                    Directory pathsIndexDirectory,
+                                    Directory contentIndexDirectory,
+                                    Analyzer analyzer,
+                                    boolean overwrite,
+                                    boolean readOnly ) {
+            super(context, sourceName, workspaceName, rules, analyzer, overwrite, readOnly);
+            this.pathsIndexDirectory = pathsIndexDirectory;
+            this.contentIndexDirectory = contentIndexDirectory;
+            assert this.pathsIndexDirectory != null;
+            assert this.contentIndexDirectory != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneSession#fullTextFieldName(java.lang.String)
+         */
+        @Override
+        protected String fullTextFieldName( String propertyName ) {
+            return propertyName + FULL_TEXT_SUFFIX;
+        }
+
+        protected IndexReader getPathsReader() throws IOException {
+            if (pathsReader == null) {
+                pathsReader = IndexReader.open(pathsIndexDirectory, readOnly);
+            }
+            return pathsReader;
+        }
+
+        protected IndexReader getContentReader() throws IOException {
+            if (contentReader == null) {
+                contentReader = IndexReader.open(contentIndexDirectory, readOnly);
+            }
+            return contentReader;
+        }
+
+        protected IndexWriter getPathsWriter() throws IOException {
+            assert !readOnly;
+            if (pathsWriter == null) {
+                pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+            }
+            return pathsWriter;
+        }
+
+        protected IndexWriter getContentWriter() throws IOException {
+            assert !readOnly;
+            if (contentWriter == null) {
+                contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+            }
+            return contentWriter;
+        }
+
+        protected IndexSearcher getPathsSearcher() throws IOException {
+            if (pathsSearcher == null) {
+                pathsSearcher = new IndexSearcher(getPathsReader());
+            }
+            return pathsSearcher;
+        }
+
+        @Override
+        public IndexSearcher getContentSearcher() throws IOException {
+            if (contentSearcher == null) {
+                contentSearcher = new IndexSearcher(getContentReader());
+            }
+            return contentSearcher;
+        }
+
+        protected boolean hasWriters() {
+            return pathsWriter != null || contentWriter != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#index(org.jboss.dna.graph.Node)
+         */
+        public void index( Node node ) {
+            assert !readOnly;
+            Location location = node.getLocation();
+            UUID uuid = location.getUuid();
+            if (uuid == null) uuid = UUID.randomUUID();
+            Path path = location.getPath();
+            String uuidStr = stringFactory.create(uuid);
+            String pathStr = pathAsString(path, stringFactory);
+            String nameStr = path.isRoot() ? "" : stringFactory.create(path.getLastSegment().getName());
+            int sns = path.isRoot() ? 1 : path.getLastSegment().getIndex();
+
+            Logger logger = Logger.getLogger(getClass());
+            if (logger.isTraceEnabled()) {
+                logger.trace("indexing {0}", pathStr);
+            }
+
+            try {
+
+                // Create a separate document for the path, which makes it easier to handle moves since the path can
+                // be changed without changing any other content fields ...
+                Document doc = new Document();
+                doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new Field(PathIndex.LOCAL_NAME, nameStr, Field.Store.YES, Field.Index.ANALYZED));
+                doc.add(new NumericField(PathIndex.LOCAL_NAME, Field.Store.YES, true).setIntValue(sns));
+                doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new NumericField(PathIndex.DEPTH, Field.Store.YES, true).setIntValue(path.size()));
+                getPathsWriter().addDocument(doc);
+
+                // Create the document for the content (properties) ...
+                doc = new Document();
+                doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                String stringValue = null;
+                StringBuilder fullTextSearchValue = null;
+                for (Property property : node.getProperties()) {
+                    Name name = property.getName();
+                    Rule rule = rules.getRule(name);
+                    if (!rule.isIncluded()) continue;
+                    String nameString = stringFactory.create(name);
+                    if (rule.isDate()) {
+                        for (Object value : property) {
+                            if (value == null) continue;
+                            DateTime dateValue = dateFactory.create(value);
+                            // Add a separate field for each property value ...
+                            doc.add(new NumericField(nameString, rule.getStoreOption(), true).setLongValue(dateValue.getMillisecondsInUtc()));
+                            // Dates are not added to the full-text search field (since this wouldn't make sense)
+                        }
+                        continue;
+                    }
+                    for (Object value : property) {
+                        if (value == null) continue;
+                        if (value instanceof Binary) {
+                            // don't include binary values as individual fields but do include them in the full-text search ...
+                            // TODO : add to full-text search ...
+                            continue;
+                        }
+                        stringValue = stringFactory.create(value);
+                        // Add a separate field for each property value ...
+                        doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+
+                        if (rule.isFullText()) {
+                            // Add this text to the full-text field ...
+                            if (fullTextSearchValue == null) {
+                                fullTextSearchValue = new StringBuilder();
+                            } else {
+                                fullTextSearchValue.append(' ');
+                            }
+                            fullTextSearchValue.append(stringValue);
+
+                            // Also create a full-text-searchable field ...
+                            String fullTextNameString = fullTextFieldName(nameString);
+                            doc.add(new Field(fullTextNameString, stringValue, Store.NO, Index.ANALYZED));
+                        }
+                    }
+                }
+                // Add the full-text-search field ...
+                if (fullTextSearchValue != null) {
+                    doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO,
+                                      Field.Index.ANALYZED));
+                }
+                getContentWriter().addDocument(doc);
+            } catch (IOException e) {
+                throw new LuceneException(e);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#optimize()
+         */
+        public void optimize() {
+            try {
+                getContentWriter().optimize();
+                getPathsWriter().optimize();
+            } catch (IOException e) {
+                throw new LuceneException(e);
+
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#apply(java.lang.Iterable)
+         */
+        public int apply( Iterable<ChangeRequest> changes ) {
+            for (ChangeRequest change : changes) {
+                if (change != null) continue;
+            }
+            return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes,
+         * we need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below
+         * a certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
+         * documents in the content index that represent those same nodes.
+         * </p>
+         * <p>
+         * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the
+         * number of results to a maximum number. We repeat batches as long as we find more results. This approach has the
+         * advantage that we'll never bring in a large number of results, and it allows us to delete the documents from the
+         * content node using a query.
+         * </p>
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#deleteBelow(org.jboss.dna.graph.property.Path)
+         */
+        public int deleteBelow( Path path ) {
+            assert !readOnly;
+            // Perform a query using the reader to find those nodes at/below the path ...
+            try {
+                IndexReader pathReader = getPathsReader();
+                IndexSearcher pathSearcher = new IndexSearcher(pathReader);
+                String pathStr = stringFactory.create(path) + "/";
+                PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
+                int numberDeleted = 0;
+                while (true) {
+                    // Execute the query and get the results ...
+                    TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
+                    int numResultsInBatch = results.scoreDocs.length;
+                    // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
+                    IndexReader contentReader = getContentReader();
+                    for (ScoreDoc result : results.scoreDocs) {
+                        int docId = result.doc;
+                        // Find the UUID of the node ...
+                        Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
+                        String uuid = doc.get(PathIndex.UUID);
+                        // Delete the document from the paths index ...
+                        pathReader.deleteDocument(docId);
+                        // Delete the corresponding document from the content index ...
+                        contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
+                    }
+                    numberDeleted += numResultsInBatch;
+                    if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
+                }
+                return numberDeleted;
+            } catch (FileNotFoundException e) {
+                // There are no index files yet, so nothing to delete ...
+                return 0;
+            } catch (IOException e) {
+                throw new LuceneException(e);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#search(org.jboss.dna.graph.ExecutionContext, java.lang.String,
+         *      int, int, java.util.List)
+         */
+        public void search( ExecutionContext context,
+                            String fullTextString,
+                            int maxResults,
+                            int offset,
+                            List<Location> results ) {
+            assert fullTextString != null;
+            assert fullTextString.length() > 0;
+            assert offset >= 0;
+            assert maxResults > 0;
+            assert results != null;
+
+            try {
+                // Parse the full-text search and search against the 'fts' field ...
+                QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+                Query query = parser.parse(fullTextString);
+                TopDocs docs = getContentSearcher().search(query, maxResults + offset);
+
+                // Collect the results ...
+                IndexReader contentReader = getContentReader();
+                IndexReader pathReader = getPathsReader();
+                IndexSearcher pathSearcher = getPathsSearcher();
+                ScoreDoc[] scoreDocs = docs.scoreDocs;
+                int numberOfResults = scoreDocs.length;
+                if (numberOfResults > offset) {
+                    // There are enough results to satisfy the offset ...
+                    PathFactory pathFactory = context.getValueFactories().getPathFactory();
+                    for (int i = offset, num = scoreDocs.length; i != num; ++i) {
+                        ScoreDoc result = scoreDocs[i];
+                        int docId = result.doc;
+                        // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
+                        Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
+                        String uuid = doc.get(ContentIndex.UUID);
+                        // Find the path for this node (is there a better way to do this than one search per UUID?) ...
+                        TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
+                        if (pathDocs.scoreDocs.length < 1) {
+                            // No path record found ...
+                            continue;
+                        }
+                        Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+                        String pathString = pathDoc.get(PathIndex.PATH);
+                        Path path = pathFactory.create(pathString);
+                        // Now add the location ...
+                        results.add(Location.create(path, UUID.fromString(uuid)));
+                    }
+                }
+            } catch (ParseException e) {
+                String msg = SearchI18n.errorWhilePerformingSearch.text(workspaceName, sourceName, fullTextString, e.getMessage());
+                throw new SearchException(fullTextString, msg, e);
+            } catch (IOException e) {
+                throw new LuceneException(e);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#query(org.jboss.dna.graph.query.QueryContext,
+         *      org.jboss.dna.graph.query.model.QueryCommand)
+         */
+        public QueryResults query( QueryContext queryContext,
+                                   QueryCommand query ) {
+            return queryEngine().execute(queryContext, query);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#commit()
+         */
+        public void commit() {
+            IOException ioError = null;
+            RuntimeException runtimeError = null;
+            if (pathsReader != null) {
+                try {
+                    pathsReader.close();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    pathsReader = null;
+                }
+            }
+            if (contentReader != null) {
+                try {
+                    contentReader.close();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    contentReader = null;
+                }
+            }
+            if (pathsWriter != null) {
+                try {
+                    pathsWriter.commit();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    try {
+                        pathsWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        pathsWriter = null;
+                    }
+                }
+            }
+            if (contentWriter != null) {
+                try {
+                    contentWriter.commit();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    try {
+                        contentWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        contentWriter = null;
+                    }
+                }
+            }
+            if (ioError != null) {
+                String msg = SearchI18n.errorWhileCommittingIndexChanges.text(workspaceName, sourceName, ioError.getMessage());
+                throw new LuceneException(msg, ioError);
+            }
+            if (runtimeError != null) throw runtimeError;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.search.SearchProvider.Session#rollback()
+         */
+        public void rollback() {
+            IOException ioError = null;
+            RuntimeException runtimeError = null;
+            if (pathsReader != null) {
+                try {
+                    pathsReader.close();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    pathsReader = null;
+                }
+            }
+            if (contentReader != null) {
+                try {
+                    contentReader.close();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    contentReader = null;
+                }
+            }
+            if (pathsWriter != null) {
+                try {
+                    pathsWriter.rollback();
+                } catch (IOException e) {
+                    ioError = e;
+                } catch (RuntimeException e) {
+                    runtimeError = e;
+                } finally {
+                    try {
+                        pathsWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        pathsWriter = null;
+                    }
+                }
+            }
+            if (contentWriter != null) {
+                try {
+                    contentWriter.rollback();
+                } catch (IOException e) {
+                    if (ioError == null) ioError = e;
+                } catch (RuntimeException e) {
+                    if (runtimeError == null) runtimeError = e;
+                } finally {
+                    try {
+                        contentWriter.close();
+                    } catch (IOException e) {
+                        ioError = e;
+                    } catch (RuntimeException e) {
+                        runtimeError = e;
+                    } finally {
+                        contentWriter = null;
+                    }
+                }
+            }
+            if (ioError != null) {
+                String msg = SearchI18n.errorWhileRollingBackIndexChanges.text(workspaceName, sourceName, ioError.getMessage());
+                throw new LuceneException(msg, ioError);
+            }
+            if (runtimeError != null) throw runtimeError;
+        }
+
+        protected QueryEngine createQueryProcessor() {
+            // Create the query engine ...
+            Planner planner = new CanonicalPlanner();
+            Optimizer optimizer = new RuleBasedOptimizer() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+                 *      org.jboss.dna.graph.query.plan.PlanHints)
+                 */
+                @Override
+                protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+                                                  PlanHints hints ) {
+                    super.populateRuleStack(ruleStack, hints);
+                    // Add any custom rules here, either at the front of the stack or at the end
+                }
+            };
+            QueryProcessor processor = new QueryProcessor() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+                 *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
+                 *      org.jboss.dna.graph.query.QueryResults.Columns,
+                 *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+                 */
+                @Override
+                protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                     QueryContext context,
+                                                                     PlanNode accessNode,
+                                                                     Columns resultColumns,
+                                                                     org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+                    return DualIndexSession.this.createAccessComponent(originalQuery,
+                                                                       context,
+                                                                       accessNode,
+                                                                       resultColumns,
+                                                                       analyzer);
+                }
+            };
+
+            return new QueryEngine(planner, optimizer, processor);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneSession#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+         *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
+         *      org.jboss.dna.graph.query.QueryResults.Columns, org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+         */
+        @Override
+        protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                             QueryContext context,
+                                                             PlanNode accessNode,
+                                                             Columns resultColumns,
+                                                             org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+            // Create a processing component for this access query ...
+            return new LuceneQueryComponent(this, originalQuery, context, resultColumns, accessNode, analyzer, sourceName,
+                                            workspaceName);
+        }
+
+        /**
+         * Get the set of UUIDs for the children of the node at the given path.
+         * 
+         * @param parentPath the path to the parent node; may not be null
+         * @return the UUIDs of the child nodes; never null but possibly empty
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected Set<UUID> getUuidsForChildrenOf( Path parentPath ) throws IOException {
+            // Find the path of the parent ...
+            String stringifiedPath = pathAsString(parentPath, stringFactory);
+            // Append a '/' to the parent path, so we'll only get decendants ...
+            stringifiedPath = stringifiedPath + '/';
+
+            // Create a query to find all the nodes below the parent path ...
+            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
+            // Include only the children ...
+            int childrenDepth = parentPath.size() + 1;
+            Query depthQuery = NumericRangeQuery.newIntRange(PathIndex.DEPTH, childrenDepth, childrenDepth, true, true);
+            // And combine ...
+            BooleanQuery combinedQuery = new BooleanQuery();
+            combinedQuery.add(query, Occur.MUST);
+            combinedQuery.add(depthQuery, Occur.MUST);
+            query = combinedQuery;
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return uuidCollector.getUuids();
+        }
+
+        /**
+         * Get the set of UUIDs for the nodes that are descendants of the node at the given path.
+         * 
+         * @param parentPath the path to the parent node; may not be null and <i>may not be the root node</i>
+         * @param includeParent true if the parent node should be included in the results, or false if only the descendants should
+         *        be included
+         * @return the UUIDs of the nodes; never null but possibly empty
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected Set<UUID> getUuidsForDescendantsOf( Path parentPath,
+                                                      boolean includeParent ) throws IOException {
+            assert !parentPath.isRoot();
+
+            // Find the path of the parent ...
+            String stringifiedPath = pathAsString(parentPath, stringFactory);
+            if (!includeParent) {
+                // Append a '/' to the parent path, and we'll only get decendants ...
+                stringifiedPath = stringifiedPath + '/';
+            }
+
+            // Create a prefix query ...
+            Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return uuidCollector.getUuids();
+        }
+
+        /**
+         * Get the set containing the single UUID for the node at the given path.
+         * 
+         * @param path the path to the node; may not be null
+         * @return the UUID of the supplied node; or null if the node cannot be found
+         * @throws IOException if there is an error accessing the indexes
+         */
+        protected UUID getUuidFor( Path path ) throws IOException {
+            // Create a query to find all the nodes below the parent path ...
+            IndexSearcher searcher = getPathsSearcher();
+            String stringifiedPath = pathAsString(path, stringFactory);
+            TermQuery query = new TermQuery(new Term(PathIndex.PATH, stringifiedPath));
+
+            // Now execute and collect the UUIDs ...
+            TopDocs topDocs = searcher.search(query, 1);
+            if (topDocs.totalHits == 0) return null;
+            Document pathDoc = getPathsReader().document(topDocs.scoreDocs[0].doc);
+            String uuidString = pathDoc.get(PathIndex.UUID);
+            return UUID.fromString(uuidString);
+        }
+
+        /**
+         * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
+         * 
+         * @param uuids the UUIDs of the nodes that are to be found; may not be null
+         * @return the query; never null
+         */
+        @Override
+        public Query findAllNodesWithUuids( Set<UUID> uuids ) {
+            if (uuids.isEmpty()) {
+                // There are no children, so return a null query ...
+                return new MatchNoneQuery();
+            }
+            if (uuids.size() == 1) {
+                UUID uuid = uuids.iterator().next();
+                if (uuid == null) return new MatchNoneQuery();
+                return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+            }
+            if (uuids.size() < 50) {
+                // Create an OR boolean query for all the UUIDs, since this is probably more efficient ...
+                BooleanQuery query = new BooleanQuery();
+                for (UUID uuid : uuids) {
+                    Query uuidQuery = new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+                    query.add(uuidQuery, Occur.SHOULD);
+                }
+                return query;
+            }
+            // Returna query that will always find all of the UUIDs ...
+            return new UuidsQuery(ContentIndex.UUID, uuids, getContext().getValueFactories().getUuidFactory());
+        }
+
+        @Override
+        public Query findAllNodesBelow( Path ancestorPath ) throws IOException {
+            if (ancestorPath.isRoot()) {
+                return new MatchAllDocsQuery();
+            }
+            Set<UUID> uuids = getUuidsForDescendantsOf(ancestorPath, false);
+            return findAllNodesWithUuids(uuids);
+        }
+
+        /**
+         * Return a query that can be used to find all of the documents that represent nodes that are children of the node at the
+         * supplied path.
+         * 
+         * @param parentPath the path of the parent node.
+         * @return the query; never null
+         * @throws IOException if there is an error finding the UUIDs of the child nodes
+         */
+        @Override
+        public Query findChildNodes( Path parentPath ) throws IOException {
+            if (parentPath.isRoot()) {
+                return new MatchAllDocsQuery();
+            }
+            Set<UUID> childUuids = getUuidsForChildrenOf(parentPath);
+            return findAllNodesWithUuids(childUuids);
+        }
+
+        /**
+         * Create a query that can be used to find the one document (or node) that exists at the exact path supplied. This method
+         * first queries the {@link PathIndex path index} to find the UUID of the node at the supplied path, and then returns a
+         * query that matches the UUID.
+         * 
+         * @param path the path of the node
+         * @return the query; never null
+         * @throws IOException if there is an error finding the UUID for the supplied path
+         */
+        @Override
+        public Query findNodeAt( Path path ) throws IOException {
+            UUID uuid = getUuidFor(path);
+            if (uuid == null) return null;
+            return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+        }
+
+        /**
+         * Create a query that can be used to find documents (or nodes) that have a field value that satisfies the supplied LIKE
+         * expression.
+         * 
+         * @param fieldName the name of the document field to search
+         * @param likeExpression the JCR like expression
+         * @return the query; never null
+         */
+        @Override
+        public Query findNodesLike( String fieldName,
+                                    String likeExpression ) {
+            assert likeExpression != null;
+            assert likeExpression.length() > 0;
+
+            // '%' matches 0 or more characters
+            // '_' matches any single character
+            // '\x' matches 'x'
+            // all other characters match themselves
+
+            // Wildcard queries are a better match, but they can be slow and should not be used
+            // if the first character of the expression is a '%' or '_' ...
+            char firstChar = likeExpression.charAt(0);
+            if (firstChar != '%' && firstChar != '_') {
+                // Create a wildcard query ...
+                String expression = toWildcardExpression(likeExpression);
+                return new WildcardQuery(new Term(fieldName, expression));
+            }
+            // Create a regex query,
+            String regex = toRegularExpression(likeExpression);
+            RegexQuery query = new RegexQuery(new Term(fieldName, regex));
+            query.setRegexImplementation(new JavaUtilRegexCapabilities());
+            return query;
+        }
+
+        @Override
+        public Query findNodesWith( Length propertyLength,
+                                    Operator operator,
+                                    Object value ) {
+            assert propertyLength != null;
+            assert value != null;
+            PropertyValue propertyValue = propertyLength.getPropertyValue();
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            ValueFactories factories = context.getValueFactories();
+            int length = factories.getLongFactory().create(value).intValue();
+            switch (operator) {
+                case EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldEqualTo(length, field, factories);
+                case NOT_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldNotEqualTo(length, field, factories);
+                case GREATER_THAN:
+                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThan(length, field, factories);
+                case GREATER_THAN_OR_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(length, field, factories);
+                case LESS_THAN:
+                    return CompareLengthQuery.createQueryForNodesWithFieldLessThan(length, field, factories);
+                case LESS_THAN_OR_EQUAL_TO:
+                    return CompareLengthQuery.createQueryForNodesWithFieldLessThanOrEqualTo(length, field, factories);
+                case LIKE:
+                    // This is not allowed ...
+                    assert false;
+                    break;
+            }
+            return null;
+        }
+
+        @Override
+        public Query findNodesWith( PropertyValue propertyValue,
+                                    Operator operator,
+                                    Object value,
+                                    boolean caseSensitive ) {
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            PropertyType valueType = PropertyType.discoverType(value);
+            ValueFactories factories = context.getValueFactories();
+            switch (valueType) {
+                case NAME:
+                case PATH:
+                case REFERENCE:
+                case URI:
+                case UUID:
+                case STRING:
+                    String stringValue = stringFactory.create(value);
+                    if (valueType == PropertyType.PATH) {
+                        stringValue = pathAsString(pathFactory.create(value), stringFactory);
+                    }
+                    if (!caseSensitive) stringValue = stringValue.toLowerCase();
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return new TermQuery(new Term(field, stringValue));
+                        case NOT_EQUAL_TO:
+                            Query query = new TermQuery(new Term(field, stringValue));
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThan(stringValue,
+                                                                                              field,
+                                                                                              factories,
+                                                                                              caseSensitive);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(stringValue,
+                                                                                                       field,
+                                                                                                       factories,
+                                                                                                       caseSensitive);
+                        case LESS_THAN:
+                            return CompareStringQuery.createQueryForNodesWithFieldLessThan(stringValue,
+                                                                                           field,
+                                                                                           factories,
+                                                                                           caseSensitive);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(stringValue,
+                                                                                                    field,
+                                                                                                    factories,
+                                                                                                    caseSensitive);
+                        case LIKE:
+                            return findNodesLike(field, stringValue);
+                    }
+                    break;
+                case DATE:
+                    long date = factories.getLongFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, date, date, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newLongRange(field, date, date, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case LONG:
+                    long longValue = factories.getLongFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case DECIMAL:
+                case DOUBLE:
+                    double doubleValue = factories.getDoubleFactory().create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case BOOLEAN:
+                    boolean booleanValue = factories.getBooleanFactory().create(value);
+                    stringValue = stringFactory.create(value);
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return new TermQuery(new Term(field, stringValue));
+                        case NOT_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(!booleanValue)));
+                        case GREATER_THAN:
+                            if (!booleanValue) {
+                                return new TermQuery(new Term(field, stringFactory.create(true)));
+                            }
+                            // Can't be greater than 'true', per JCR spec
+                            return new MatchNoneQuery();
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(true)));
+                        case LESS_THAN:
+                            if (booleanValue) {
+                                return new TermQuery(new Term(field, stringFactory.create(false)));
+                            }
+                            // Can't be less than 'false', per JCR spec
+                            return new MatchNoneQuery();
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return new TermQuery(new Term(field, stringFactory.create(false)));
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
+                case OBJECT:
+                case BINARY:
+                    // This is not allowed ...
+                    assert false;
+                    return null;
+            }
+            return null;
+        }
+
+        @Override
+        public Query findNodesWithNumericRange( PropertyValue propertyValue,
+                                                Object lowerValue,
+                                                Object upperValue,
+                                                boolean includesLower,
+                                                boolean includesUpper ) {
+            String field = stringFactory.create(propertyValue.getPropertyName());
+            return findNodesWithNumericRange(field, lowerValue, upperValue, includesLower, includesUpper);
+        }
+
+        @Override
+        public Query findNodesWithNumericRange( NodeDepth depth,
+                                                Object lowerValue,
+                                                Object upperValue,
+                                                boolean includesLower,
+                                                boolean includesUpper ) {
+            return findNodesWithNumericRange(PathIndex.DEPTH, lowerValue, upperValue, includesLower, includesUpper);
+        }
+
+        protected Query findNodesWithNumericRange( String field,
+                                                   Object lowerValue,
+                                                   Object upperValue,
+                                                   boolean includesLower,
+                                                   boolean includesUpper ) {
+            PropertyType type = PropertyType.discoverType(lowerValue);
+            assert type == PropertyType.discoverType(upperValue);
+            ValueFactories factories = context.getValueFactories();
+            switch (type) {
+                case DATE:
+                    long lowerDate = factories.getLongFactory().create(lowerValue);
+                    long upperDate = factories.getLongFactory().create(upperValue);
+                    return NumericRangeQuery.newLongRange(field, lowerDate, upperDate, includesLower, includesUpper);
+                case LONG:
+                    long lowerLong = factories.getLongFactory().create(lowerValue);
+                    long upperLong = factories.getLongFactory().create(upperValue);
+                    return NumericRangeQuery.newLongRange(field, lowerLong, upperLong, includesLower, includesUpper);
+                case DECIMAL:
+                case DOUBLE:
+                    double lowerDouble = factories.getDoubleFactory().create(lowerValue);
+                    double upperDouble = factories.getDoubleFactory().create(upperValue);
+                    return NumericRangeQuery.newDoubleRange(field, lowerDouble, upperDouble, includesLower, includesUpper);
+                default:
+                    // This is not allowed ...
+                    assert false;
+                    return null;
+            }
+        }
+
+        @Override
+        public Query findNodesWith( NodePath nodePath,
+                                    Operator operator,
+                                    Object value,
+                                    boolean caseSensitive ) throws IOException {
+            if (!caseSensitive) value = stringFactory.create(value).toLowerCase();
+            Path pathValue = operator != Operator.LIKE ? pathFactory.create(value) : null;
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    return findNodeAt(pathValue);
+                case NOT_EQUAL_TO:
+                    return new NotQuery(findNodeAt(pathValue));
+                case LIKE:
+                    String likeExpression = stringFactory.create(value);
+                    return findNodesLike(PathIndex.PATH, likeExpression);
+                case GREATER_THAN:
+                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThan(pathValue,
+                                                                                    PathIndex.PATH,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = ComparePathQuery.createQueryForNodesWithPathGreaterThanOrEqualTo(pathValue,
+                                                                                             PathIndex.PATH,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = ComparePathQuery.createQueryForNodesWithPathLessThan(pathValue,
+                                                                                 PathIndex.PATH,
+                                                                                 context.getValueFactories(),
+                                                                                 caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = ComparePathQuery.createQueryForNodesWithPathLessThanOrEqualTo(pathValue,
+                                                                                          PathIndex.PATH,
+                                                                                          context.getValueFactories(),
+                                                                                          caseSensitive);
+                    break;
+            }
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        @Override
+        public Query findNodesWith( NodeName nodeName,
+                                    Operator operator,
+                                    Object value,
+                                    boolean caseSensitive ) throws IOException {
+            String stringValue = stringFactory.create(value);
+            if (!caseSensitive) stringValue = stringValue.toLowerCase();
+            Path.Segment segment = operator != Operator.LIKE ? pathFactory.createSegment(stringValue) : null;
+            int snsIndex = operator != Operator.LIKE ? segment.getIndex() : 0;
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    BooleanQuery booleanQuery = new BooleanQuery();
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
+                                     Occur.MUST);
+                    return booleanQuery;
+                case NOT_EQUAL_TO:
+                    booleanQuery = new BooleanQuery();
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
+                                     Occur.MUST);
+                    return new NotQuery(booleanQuery);
+                case GREATER_THAN:
+                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThan(segment,
+                                                                                    PathIndex.LOCAL_NAME,
+                                                                                    PathIndex.SNS_INDEX,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = CompareNameQuery.createQueryForNodesWithNameGreaterThanOrEqualTo(segment,
+                                                                                             PathIndex.LOCAL_NAME,
+                                                                                             PathIndex.SNS_INDEX,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = CompareNameQuery.createQueryForNodesWithNameLessThan(segment,
+                                                                                 PathIndex.LOCAL_NAME,
+                                                                                 PathIndex.SNS_INDEX,
+                                                                                 context.getValueFactories(),
+                                                                                 caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = CompareNameQuery.createQueryForNodesWithNameLessThanOrEqualTo(segment,
+                                                                                          PathIndex.LOCAL_NAME,
+                                                                                          PathIndex.SNS_INDEX,
+                                                                                          context.getValueFactories(),
+                                                                                          caseSensitive);
+                    break;
+                case LIKE:
+                    // See whether the like expression has brackets ...
+                    String likeExpression = stringValue;
+                    int openBracketIndex = likeExpression.indexOf('[');
+                    if (openBracketIndex != -1) {
+                        String localNameExpression = likeExpression.substring(0, openBracketIndex);
+                        String snsIndexExpression = likeExpression.substring(openBracketIndex);
+                        Query localNameQuery = createLocalNameQuery(localNameExpression);
+                        Query snsQuery = createSnsIndexQuery(snsIndexExpression);
+                        if (localNameQuery == null) {
+                            if (snsQuery == null) {
+                                query = new MatchNoneQuery();
+                            } else {
+                                // There is just an SNS part ...
+                                query = snsQuery;
+                            }
+                        } else {
+                            // There is a local name part ...
+                            if (snsQuery == null) {
+                                query = localNameQuery;
+                            } else {
+                                // There is both a local name part and a SNS part ...
+                                booleanQuery = new BooleanQuery();
+                                booleanQuery.add(localNameQuery, Occur.MUST);
+                                booleanQuery.add(snsQuery, Occur.MUST);
+                                query = booleanQuery;
+                            }
+                        }
+                    } else {
+                        // There is no SNS expression ...
+                        query = createLocalNameQuery(likeExpression);
+                    }
+                    assert query != null;
+                    break;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        @Override
+        public Query findNodesWith( NodeLocalName nodeName,
+                                    Operator operator,
+                                    Object value,
+                                    boolean caseSensitive ) throws IOException {
+            String nameValue = stringFactory.create(value);
+            Query query = null;
+            switch (operator) {
+                case LIKE:
+                    String likeExpression = stringFactory.create(value);
+                    return findNodesLike(PathIndex.LOCAL_NAME, likeExpression); // already is a query with UUIDs
+                case EQUAL_TO:
+                    query = new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue));
+                    break;
+                case NOT_EQUAL_TO:
+                    query = new NotQuery(new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue)));
+                    break;
+                case GREATER_THAN:
+                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThan(nameValue,
+                                                                                       PathIndex.LOCAL_NAME,
+                                                                                       context.getValueFactories(),
+                                                                                       caseSensitive);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = CompareStringQuery.createQueryForNodesWithFieldGreaterThanOrEqualTo(nameValue,
+                                                                                                PathIndex.LOCAL_NAME,
+                                                                                                context.getValueFactories(),
+                                                                                                caseSensitive);
+                    break;
+                case LESS_THAN:
+                    query = CompareStringQuery.createQueryForNodesWithFieldLessThan(nameValue,
+                                                                                    PathIndex.LOCAL_NAME,
+                                                                                    context.getValueFactories(),
+                                                                                    caseSensitive);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = CompareStringQuery.createQueryForNodesWithFieldLessThanOrEqualTo(nameValue,
+                                                                                             PathIndex.LOCAL_NAME,
+                                                                                             context.getValueFactories(),
+                                                                                             caseSensitive);
+                    break;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        @Override
+        public Query findNodesWith( NodeDepth depthConstraint,
+                                    Operator operator,
+                                    Object value ) throws IOException {
+            int depth = context.getValueFactories().getLongFactory().create(value).intValue();
+            Query query = null;
+            switch (operator) {
+                case EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
+                    break;
+                case NOT_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, depth, true, true);
+                    query = new NotQuery(query);
+                    break;
+                case GREATER_THAN:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, false, true);
+                    break;
+                case GREATER_THAN_OR_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, depth, MAX_DEPTH, true, true);
+                    break;
+                case LESS_THAN:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, false);
+                    break;
+                case LESS_THAN_OR_EQUAL_TO:
+                    query = NumericRangeQuery.newIntRange(PathIndex.DEPTH, MIN_DEPTH, depth, true, true);
+                    break;
+                case LIKE:
+                    // This is not allowed ...
+                    return null;
+            }
+
+            // Now execute and collect the UUIDs ...
+            UuidCollector uuidCollector = new UuidCollector();
+            IndexSearcher searcher = getPathsSearcher();
+            searcher.search(query, uuidCollector);
+            return findAllNodesWithUuids(uuidCollector.getUuids());
+        }
+
+        protected Query createLocalNameQuery( String likeExpression ) {
+            if (likeExpression == null) return null;
+            likeExpression = likeExpression.trim();
+            if (likeExpression.length() == 0) return null;
+            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
+                // The local name is a like ...
+                return findNodesLike(PathIndex.LOCAL_NAME, likeExpression);
+            }
+            // The local name is an exact match ...
+            return new TermQuery(new Term(PathIndex.LOCAL_NAME, likeExpression));
+        }
+
+        protected Query createSnsIndexQuery( String likeExpression ) {
+            if (likeExpression == null) return null;
+            likeExpression = likeExpression.trim();
+            if (likeExpression.length() == 0) return null;
+
+            // Remove the leading '[' ...
+            assert likeExpression.charAt(0) == '[';
+            likeExpression = likeExpression.substring(1);
+
+            // Remove the trailing ']' if it exists ...
+            int closeBracketIndex = likeExpression.indexOf(']');
+            if (closeBracketIndex != -1) {
+                likeExpression = likeExpression.substring(0, closeBracketIndex);
+            }
+            // If SNS expression contains '?' or '*' ...
+            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
+                // There is a LIKE expression for the SNS ...
+                return findNodesLike(PathIndex.SNS_INDEX, likeExpression);
+            }
+            // This is not a LIKE expression but an exact value specification and should be a number ...
+            try {
+                // This SNS is just a number ...
+                int sns = Integer.parseInt(likeExpression);
+                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, sns, sns, true, false);
+            } catch (NumberFormatException e) {
+                // It's not a number but it's in the SNS field, so there will be no results ...
+                return new MatchNoneQuery();
+            }
+        }
+
+    }
+
+    /**
+     * A {@link Collector} implementation that only captures the UUID of the documents returned by a query. Score information is
+     * not recorded. This is often used when querying the {@link PathIndex} to collect the UUIDs of a set of nodes satisfying some
+     * path constraint.
+     * 
+     * @see DualIndexSearchProvider.DualIndexSession#findChildNodes(Path)
+     */
+    protected static class UuidCollector extends Collector {
+        private final Set<UUID> uuids = new HashSet<UUID>();
+        private String[] uuidsByDocId;
+        private int baseDocId;
+
+        protected UuidCollector() {
+        }
+
+        /**
+         * Get the UUIDs that have been collected.
+         * 
+         * @return the set of UUIDs; never null
+         */
+        public Set<UUID> getUuids() {
+            return uuids;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
+         */
+        @Override
+        public boolean acceptsDocsOutOfOrder() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
+         */
+        @Override
+        public void setScorer( Scorer scorer ) {
+            // we don't care about scoring
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#collect(int)
+         */
+        @Override
+        public void collect( int doc ) {
+            int index = doc - baseDocId;
+            assert index >= 0;
+            String uuidString = uuidsByDocId[index];
+            assert uuidString != null;
+            uuids.add(UUID.fromString(uuidString));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public void setNextReader( IndexReader reader,
+                                   int docBase ) throws IOException {
+            this.uuidsByDocId = FieldCache.DEFAULT.getStrings(reader, UUID_FIELD);
+            this.baseDocId = docBase;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexLayout.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,66 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.graph.ExecutionContext;
-
-/**
- * The representation of a single layout of one or more Lucene indexes.
- */
- at ThreadSafe
-public interface IndexLayout {
-
-    /**
-     * Create a new session to the indexes.
-     * 
-     * @param context the execution context for which this session is to be established; may not be null
-     * @param sourceName the name of the source; may not be null
-     * @param workspaceName the name of the workspace; may not be null
-     * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
-     * @param readOnly true if the resulting session can be optimized for use in read-only situations, or false if the session
-     *        needs to allow calling the write methods
-     * @return the session to the indexes; never null
-     */
-    IndexSession createSession( ExecutionContext context,
-                                String sourceName,
-                                String workspaceName,
-                                boolean overwrite,
-                                boolean readOnly );
-
-    /**
-     * Destroy the indexes for the workspace with the supplied name.
-     * 
-     * @param context the execution context in which the destruction should be performed; may not be null
-     * @param sourceName the name of the source; may not be null
-     * @param workspaceName the name of the workspace; may not be null
-     * @return true if the indexes for the workspace were destroyed, or false if there was no such workspace index
-     * @throws IOException if there is a problem destroying the indexes
-     */
-    boolean destroyIndexes( ExecutionContext context,
-                            String sourceName,
-                            String workspaceName ) throws IOException;
-
-}

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -56,10 +56,13 @@
      */
     @Immutable
     public static interface Rule {
+        /**
+         * Return whether this property should be included in the indexes.
+         * 
+         * @return true if it is to be included, or false otherwise
+         */
         boolean isIncluded();
 
-        boolean isSkipped();
-
         boolean isAnalyzed();
 
         boolean isAnalyzedWithoutNorms();
@@ -131,15 +134,6 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
          * @see org.jboss.dna.search.IndexRules.Rule#isStored()
          */
         public boolean isStored() {
@@ -243,15 +237,6 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
          * @see org.jboss.dna.search.IndexRules.Rule#isStored()
          */
         public boolean isStored() {

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexSession.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,162 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.List;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.search.IndexSearcher;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * A stateful session that maintains {@link IndexReader}, {@link IndexWriter} and {@link IndexSearcher} resources to the indexes
- * of a particular source and workspace.
- */
-public interface IndexSession {
-
-    /**
-     * Get the name of the {@link RepositorySource repository source} for which this session exists. A session instance will
-     * always return the same name.
-     * 
-     * @return the source name; never null
-     */
-    String getSourceName();
-
-    /**
-     * Get the name of the workspace for which this session exists. A session instance will always return the same name.
-     * 
-     * @return the workspace name; never null
-     */
-    String getWorkspaceName();
-
-    /**
-     * Get the execution context in which this session is operating.
-     * 
-     * @return the execution context; never null
-     */
-    ExecutionContext getContext();
-
-    /**
-     * Return whether this session made changes to the indexed state.
-     * 
-     * @return true if change were made, or false otherwise
-     */
-    boolean hasChanges();
-
-    /**
-     * Perform a full-text search given the supplied query.
-     * 
-     * @param context the context in which the search should be executed; may not be null
-     * @param fullTextString the full-text query; never null or blank
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @param results the list where the results should be accumulated; never null
-     * @throws IOException if there is a problem indexing or using the indexes
-     * @throws ParseException if there is a problem parsing the query
-     */
-    void search( ExecutionContext context,
-                 String fullTextString,
-                 int maxResults,
-                 int offset,
-                 List<Location> results ) throws IOException, ParseException;
-
-    /**
-     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
-     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
-     * writers) that should be used.
-     * 
-     * @param queryContext the context in which the query should be executed; may not be null
-     * @param query the query; never null
-     * @return the results of the query; never null
-     * @throws IOException if there is a problem indexing or using the indexes
-     * @throws ParseException if there is a problem parsing the query
-     */
-    QueryResults query( QueryContext queryContext,
-                        QueryCommand query ) throws IOException, ParseException;
-
-    /**
-     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
-     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
-     * {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param node the node to be indexed; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    void index( Node node ) throws IOException;
-
-    /**
-     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
-     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
-     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param changes the set of changes to the content
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int apply( Iterable<ChangeRequest> changes ) throws IOException;
-
-    /**
-     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
-     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
-     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param path the path identifying the graph content that is to be removed; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int deleteBelow( Path path ) throws IOException;
-
-    /**
-     * Optimize the indexes, if required.
-     * 
-     * @throws IOException if there is a problem optimizing
-     */
-    void optimize() throws IOException;
-
-    /**
-     * Close this session by committing all of the changes. This session is no longer usable after this method is called.
-     * 
-     * @throws IOException if there is a problem committing
-     */
-    void commit() throws IOException;
-
-    /**
-     * Close this session by rolling back all of the changes that have been made. This session is no longer usable after this
-     * method is called.
-     * 
-     * @throws IOException if there is a problem rolling back
-     */
-    void rollback() throws IOException;
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexLayout.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,708 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.PhraseQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.store.Directory;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactory;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.model.And;
-import org.jboss.dna.graph.query.model.Between;
-import org.jboss.dna.graph.query.model.BindVariableName;
-import org.jboss.dna.graph.query.model.ChildNode;
-import org.jboss.dna.graph.query.model.Comparison;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.DescendantNode;
-import org.jboss.dna.graph.query.model.DynamicOperand;
-import org.jboss.dna.graph.query.model.FullTextSearch;
-import org.jboss.dna.graph.query.model.FullTextSearchScore;
-import org.jboss.dna.graph.query.model.Length;
-import org.jboss.dna.graph.query.model.Literal;
-import org.jboss.dna.graph.query.model.LowerCase;
-import org.jboss.dna.graph.query.model.NodeDepth;
-import org.jboss.dna.graph.query.model.NodeLocalName;
-import org.jboss.dna.graph.query.model.NodeName;
-import org.jboss.dna.graph.query.model.NodePath;
-import org.jboss.dna.graph.query.model.Not;
-import org.jboss.dna.graph.query.model.Operator;
-import org.jboss.dna.graph.query.model.Or;
-import org.jboss.dna.graph.query.model.PropertyExistence;
-import org.jboss.dna.graph.query.model.PropertyValue;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.model.SameNode;
-import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.model.SetCriteria;
-import org.jboss.dna.graph.query.model.StaticOperand;
-import org.jboss.dna.graph.query.model.UpperCase;
-import org.jboss.dna.graph.query.model.Visitors;
-import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.AbstractAccessComponent;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.SelectComponent;
-import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
-
-/**
- * An {@link IndexLayout} implementation that stores all content within a set of two indexes: one for the node content and a
- * second one for paths and UUIDs.
- */
- at ThreadSafe
-public class KitchenSinkIndexLayout extends DualIndexLayout {
-
-    /**
-     * The default set of {@link IndexRules} used by {@link KitchenSinkIndexLayout} instances when no rules are provided.
-     */
-    public static final IndexRules DEFAULT_RULES;
-
-    static {
-        IndexRules.Builder builder = IndexRules.createBuilder();
-        // Configure the default behavior ...
-        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
-        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
-        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
-        // Configure the properties that we'll treat as dates ...
-        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
-        DEFAULT_RULES = builder.build();
-    }
-
-    private final IndexRules rules;
-    private final DirectoryConfiguration directoryConfiguration;
-
-    public KitchenSinkIndexLayout( DirectoryConfiguration directoryConfiguration ) {
-        this.rules = DEFAULT_RULES;
-        this.directoryConfiguration = directoryConfiguration;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexLayout#createSession(org.jboss.dna.graph.ExecutionContext, java.lang.String,
-     *      java.lang.String, boolean, boolean)
-     */
-    public IndexSession createSession( ExecutionContext context,
-                                       String sourceName,
-                                       String workspaceName,
-                                       boolean overwrite,
-                                       boolean readOnly ) {
-        Directory pathIndexDirectory = directoryConfiguration.getDirectory(workspaceName, PATHS_INDEX_NAME);
-        Directory contentIndexDirectory = directoryConfiguration.getDirectory(workspaceName, CONTENT_INDEX_NAME);
-        assert pathIndexDirectory != null;
-        assert contentIndexDirectory != null;
-        return new Session(context, sourceName, workspaceName, rules, pathIndexDirectory, contentIndexDirectory, overwrite,
-                           readOnly);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexLayout#destroyIndexes(org.jboss.dna.graph.ExecutionContext, java.lang.String,
-     *      java.lang.String)
-     */
-    public boolean destroyIndexes( ExecutionContext context,
-                                   String sourceName,
-                                   String workspaceName ) {
-        directoryConfiguration.destroyDirectory(workspaceName, PATHS_INDEX_NAME);
-        directoryConfiguration.destroyDirectory(workspaceName, CONTENT_INDEX_NAME);
-        return true;
-    }
-
-    protected class Session extends LuceneSession {
-
-        protected Session( ExecutionContext context,
-                           String sourceName,
-                           String workspaceName,
-                           IndexRules rules,
-                           Directory pathsIndexDirectory,
-                           Directory contentIndexDirectory,
-                           boolean overwrite,
-                           boolean readOnly ) {
-            super(context, sourceName, workspaceName, rules, pathsIndexDirectory, contentIndexDirectory, overwrite, readOnly);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.DualIndexLayout.LuceneSession#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
-         *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
-         *      org.jboss.dna.graph.query.QueryResults.Columns, org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-         */
-        @Override
-        protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
-                                                             QueryContext context,
-                                                             PlanNode accessNode,
-                                                             Columns resultColumns,
-                                                             Analyzer analyzer ) {
-            // Create a processing component for this access query ...
-            return new LuceneQueryComponent(this, originalQuery, context, resultColumns, accessNode, analyzer, sourceName,
-                                            workspaceName);
-        }
-
-    }
-
-    /**
-     * The {@link ProcessingComponent} implementation that executes a single atomic access query against the Lucene indexes.
-     */
-    protected static class LuceneQueryComponent extends AbstractAccessComponent {
-        private final QueryCommand originalQuery;
-        private final Session session;
-        private final String sourceName;
-        private final String workspaceName;
-
-        protected LuceneQueryComponent( Session session,
-                                        QueryCommand originalQuery,
-                                        QueryContext context,
-                                        Columns columns,
-                                        PlanNode accessNode,
-                                        Analyzer analyzer,
-                                        String sourceName,
-                                        String workspaceName ) {
-            super(context, columns, accessNode);
-            this.originalQuery = originalQuery;
-            this.session = session;
-            this.sourceName = sourceName;
-            this.workspaceName = workspaceName;
-        }
-
-        protected String fieldNameFor( Name name ) {
-            return session.stringFactory.create(name);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
-         */
-        @Override
-        public List<Object[]> execute() {
-
-            // Some kinds of constraints are not easily pushed down to Lucene as are of a Lucene Query, and
-            // instead are applied by filtering the results. For example, a FullTextSearchScore applies
-            // to the score of the tuple, which cannot be (easily?) applied as a Query.
-            //
-            // Therefore, each of the AND-ed constraints of the query are evaluated separately. After all,
-            // each of the tuples returned by the planned query must satisfy all of the AND-ed constraints.
-            // Or, to put it another way, if a tuple does not satisfy one of the AND-ed constraints, the
-            // tuple should not be included in the query results.
-            //
-            // Logically, any AND-ed criteria that cannot be pushed down to Lucene can of course be applied
-            // as a filter on the results. Thus, each AND-ed constraint is processed to first determine if
-            // it can be represented as a Lucene query; all other AND-ed constraints must be handled as
-            // a results filter. Since most queries will likely use one or more simple constraints AND-ed
-            // together, this approach will likely work very well.
-            //
-            // The only hairy case is when any AND-ed constraint is actually an OR-ed combination of multiple
-            // constraints of which at least one cannot be pushed down to Lucene. In this case, the entire
-            // AND-ed constraint must be treated as a results filter (even if many of those constraints that
-            // make up the OR-ed constraint can be pushed down). Hopefully, this will not be a common case
-            // in actual queries.
-
-            // For each of the AND-ed constraints ...
-            Query pushDownQuery = null;
-            Constraint postProcessConstraint = null;
-            try {
-                for (Constraint andedConstraint : this.andedConstraints) {
-                    // Determine if it can be represented as a Lucene query ...
-                    Query constraintQuery = createQuery(andedConstraint);
-                    if (constraintQuery != null) {
-                        // The AND-ed constraint _can_ be represented as a push-down Lucene query ...
-                        if (pushDownQuery == null) {
-                            // This must be the first query ...
-                            pushDownQuery = constraintQuery;
-                        } else if (pushDownQuery instanceof BooleanQuery) {
-                            // We have to add the constraint query to the existing boolean ...
-                            BooleanQuery booleanQuery = (BooleanQuery)pushDownQuery;
-                            booleanQuery.add(constraintQuery, Occur.MUST);
-                        } else {
-                            // This is the second push-down query, so create a BooleanQuery ...
-                            BooleanQuery booleanQuery = new BooleanQuery();
-                            booleanQuery.add(pushDownQuery, Occur.MUST);
-                            booleanQuery.add(constraintQuery, Occur.MUST);
-                            pushDownQuery = booleanQuery;
-                        }
-                    } else {
-                        // The AND-ed constraint _cannot_ be represented as a push-down Lucene query ...
-                        if (postProcessConstraint == null) {
-                            postProcessConstraint = andedConstraint;
-                        } else {
-                            postProcessConstraint = new And(postProcessConstraint, andedConstraint);
-                        }
-                    }
-                }
-            } catch (IOException e) {
-                // There was a error working with the constraints (such as a ValueFormatException) ...
-                QueryContext context = getContext();
-                I18n msg = SearchI18n.errorWhilePerformingQuery;
-                String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
-                context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
-                return emptyTuples();
-            } catch (RuntimeException e) {
-                // There was a error working with the constraints (such as a ValueFormatException) ...
-                QueryContext context = getContext();
-                I18n msg = SearchI18n.errorWhilePerformingQuery;
-                String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
-                context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
-                return emptyTuples();
-            }
-
-            if (pushDownQuery == null) {
-                // There are no constraints that can be pushed down, so return _all_ the nodes ...
-                pushDownQuery = new MatchAllDocsQuery();
-            }
-
-            // Get the results from Lucene ...
-            List<Object[]> tuples = null;
-            final Columns columns = getColumns();
-            final QueryContext context = getContext();
-            final ExecutionContext execContext = context.getExecutionContext();
-            try {
-                // Execute the query against the content indexes ...
-                IndexSearcher searcher = session.getContentSearcher();
-                TupleCollector collector = new TupleCollector(columns, execContext.getValueFactories().getUuidFactory());
-                searcher.search(pushDownQuery, collector);
-                tuples = collector.getTuples();
-            } catch (IOException e) {
-                // There was a problem executing the Lucene query ...
-                I18n msg = SearchI18n.errorWhilePerformingLuceneQuery;
-                String origQueryString = Visitors.readable(originalQuery, execContext);
-                context.getProblems().addError(e, msg, pushDownQuery, origQueryString, workspaceName, sourceName, e.getMessage());
-                return emptyTuples();
-            }
-
-            if (postProcessConstraint != null && !tuples.isEmpty()) {
-                // Create a delegate processing component that will return the tuples we've already found ...
-                final List<Object[]> allTuples = tuples;
-                ProcessingComponent tuplesProcessor = new ProcessingComponent(context, columns) {
-                    @Override
-                    public List<Object[]> execute() {
-                        return allTuples;
-                    }
-                };
-                // Create a processing component that will apply these constraints to the tuples we already found ...
-                return new SelectComponent(tuplesProcessor, postProcessConstraint, context.getVariables()).execute();
-            }
-            return tuples;
-        }
-
-        protected Query createQuery( Constraint constraint ) throws IOException {
-            if (constraint instanceof And) {
-                And and = (And)constraint;
-                Query leftQuery = createQuery(and.getLeft());
-                Query rightQuery = createQuery(and.getRight());
-                if (leftQuery == null || rightQuery == null) return null;
-                BooleanQuery booleanQuery = new BooleanQuery();
-                booleanQuery.add(createQuery(and.getLeft()), Occur.MUST);
-                booleanQuery.add(createQuery(and.getRight()), Occur.MUST);
-                return booleanQuery;
-            }
-            if (constraint instanceof Or) {
-                Or or = (Or)constraint;
-                Query leftQuery = createQuery(or.getLeft());
-                Query rightQuery = createQuery(or.getRight());
-                if (leftQuery == null) {
-                    return rightQuery != null ? rightQuery : null;
-                } else if (rightQuery == null) {
-                    return leftQuery;
-                }
-                BooleanQuery booleanQuery = new BooleanQuery();
-                booleanQuery.add(createQuery(or.getLeft()), Occur.SHOULD);
-                booleanQuery.add(createQuery(or.getRight()), Occur.SHOULD);
-                return booleanQuery;
-            }
-            if (constraint instanceof Not) {
-                Not not = (Not)constraint;
-                Query notted = createQuery(not.getConstraint());
-                if (notted == null) return new MatchAllDocsQuery();
-            }
-            if (constraint instanceof SetCriteria) {
-                SetCriteria setCriteria = (SetCriteria)constraint;
-                DynamicOperand left = setCriteria.getLeftOperand();
-                int numRightOperands = setCriteria.getRightOperands().size();
-                assert numRightOperands > 0;
-                if (numRightOperands == 1) {
-                    return createQuery(left, Operator.EQUAL_TO, setCriteria.getRightOperands().iterator().next());
-                }
-                BooleanQuery setQuery = new BooleanQuery();
-                for (StaticOperand right : setCriteria.getRightOperands()) {
-                    Query rightQuery = createQuery(left, Operator.EQUAL_TO, right);
-                    if (rightQuery == null) return null;
-                    setQuery.add(rightQuery, Occur.SHOULD);
-                }
-                return setQuery;
-            }
-            if (constraint instanceof PropertyExistence) {
-                PropertyExistence existence = (PropertyExistence)constraint;
-                return createQuery(existence.getSelectorName(), existence.getPropertyName());
-            }
-            if (constraint instanceof Between) {
-                Between between = (Between)constraint;
-                return createQuery(between);
-            }
-            if (constraint instanceof Comparison) {
-                Comparison comparison = (Comparison)constraint;
-                return createQuery(comparison.getOperand1(), comparison.getOperator(), comparison.getOperand2());
-            }
-            if (constraint instanceof FullTextSearch) {
-                FullTextSearch search = (FullTextSearch)constraint;
-                String fieldName = ContentIndex.FULL_TEXT;
-                Name propertyName = search.getPropertyName();
-                if (propertyName != null) {
-                    fieldName = session.fullTextFieldName(fieldNameFor(propertyName));
-                }
-                return createQuery(fieldName, search.getTerm());
-            }
-            try {
-                if (constraint instanceof SameNode) {
-                    SameNode sameNode = (SameNode)constraint;
-                    return session.findNodeAt(sameNode.getPath());
-                }
-                if (constraint instanceof ChildNode) {
-                    ChildNode childNode = (ChildNode)constraint;
-                    return session.findChildNodes(childNode.getParentPath());
-                }
-                if (constraint instanceof DescendantNode) {
-                    DescendantNode descendantNode = (DescendantNode)constraint;
-                    return session.findAllNodesBelow(descendantNode.getAncestorPath());
-                }
-            } catch (IOException e) {
-                I18n msg = SearchI18n.errorWhilePerformingQuery;
-                getContext().getProblems().addError(e,
-                                                    msg,
-                                                    Visitors.readable(originalQuery),
-                                                    workspaceName,
-                                                    sourceName,
-                                                    e.getMessage());
-                return null;
-            }
-            // Should not get here ...
-            assert false;
-            return null;
-        }
-
-        protected Query createQuery( DynamicOperand left,
-                                     Operator operator,
-                                     StaticOperand right ) throws IOException {
-            return createQuery(left, operator, right, true);
-        }
-
-        protected Query createQuery( DynamicOperand left,
-                                     Operator operator,
-                                     StaticOperand right,
-                                     boolean caseSensitive ) throws IOException {
-            // Handle the static operand ...
-            Object value = createOperand(right, caseSensitive);
-            assert value != null;
-
-            // Address the dynamic operand ...
-            if (left instanceof FullTextSearchScore) {
-                // This can only be represented as a filter ...
-                return null;
-            } else if (left instanceof PropertyValue) {
-                return session.findNodesWith((PropertyValue)left, operator, value, caseSensitive);
-            } else if (left instanceof Length) {
-                return session.findNodesWith((Length)left, operator, right);
-            } else if (left instanceof LowerCase) {
-                LowerCase lowercase = (LowerCase)left;
-                return createQuery(lowercase.getOperand(), operator, right, false);
-            } else if (left instanceof UpperCase) {
-                UpperCase lowercase = (UpperCase)left;
-                return createQuery(lowercase.getOperand(), operator, right, false);
-            } else if (left instanceof NodeDepth) {
-                assert operator != Operator.LIKE;
-                // Could be represented as a result filter, but let's do this now ...
-                return session.findNodesWith((NodeDepth)left, operator, value);
-            } else if (left instanceof NodePath) {
-                return session.findNodesWith((NodePath)left, operator, value, caseSensitive);
-            } else if (left instanceof NodeName) {
-                return session.findNodesWith((NodeName)left, operator, value, caseSensitive);
-            } else if (left instanceof NodeLocalName) {
-                return session.findNodesWith((NodeLocalName)left, operator, value, caseSensitive);
-            } else {
-                assert false;
-                return null;
-            }
-        }
-
-        protected Object createOperand( StaticOperand operand,
-                                        boolean caseSensitive ) {
-            Object value = null;
-            if (operand instanceof Literal) {
-                Literal literal = (Literal)operand;
-                value = literal.getValue();
-                if (!caseSensitive) value = lowerCase(value);
-            } else if (operand instanceof BindVariableName) {
-                BindVariableName variable = (BindVariableName)operand;
-                String variableName = variable.getVariableName();
-                value = getContext().getVariables().get(variableName);
-                if (!caseSensitive) value = lowerCase(value);
-            } else {
-                assert false;
-            }
-            return value;
-        }
-
-        protected Query createQuery( DynamicOperand left,
-                                     StaticOperand lower,
-                                     StaticOperand upper,
-                                     boolean includesLower,
-                                     boolean includesUpper,
-                                     boolean caseSensitive ) throws IOException {
-            // Handle the static operands ...
-            Object lowerValue = createOperand(lower, caseSensitive);
-            Object upperValue = createOperand(upper, caseSensitive);
-            assert lowerValue != null;
-            assert upperValue != null;
-
-            // Only in the case of a PropertyValue and Depth will we need to do something special ...
-            if (left instanceof NodeDepth) {
-                return session.findNodesWithNumericRange((NodeDepth)left, lowerValue, upperValue, includesLower, includesUpper);
-            } else if (left instanceof PropertyValue) {
-                PropertyType lowerType = PropertyType.discoverType(lowerValue);
-                PropertyType upperType = PropertyType.discoverType(upperValue);
-                if (upperType == lowerType) {
-                    switch (upperType) {
-                        case DATE:
-                        case LONG:
-                        case DOUBLE:
-                        case DECIMAL:
-                            return session.findNodesWithNumericRange((PropertyValue)left,
-                                                                     lowerValue,
-                                                                     upperValue,
-                                                                     includesLower,
-                                                                     includesUpper);
-                        default:
-                            // continue on and handle as boolean query ...
-                    }
-                }
-            }
-
-            // Otherwise, just create a boolean query ...
-            BooleanQuery query = new BooleanQuery();
-            Operator lowerOp = includesLower ? Operator.GREATER_THAN_OR_EQUAL_TO : Operator.GREATER_THAN;
-            Operator upperOp = includesUpper ? Operator.LESS_THAN_OR_EQUAL_TO : Operator.LESS_THAN;
-            Query lowerQuery = createQuery(left, lowerOp, lower, caseSensitive);
-            Query upperQuery = createQuery(left, upperOp, upper, caseSensitive);
-            if (lowerQuery == null || upperQuery == null) return null;
-            query.add(lowerQuery, Occur.MUST);
-            query.add(upperQuery, Occur.MUST);
-            return query;
-        }
-
-        protected Object lowerCase( Object value ) {
-            if (value instanceof String) {
-                return ((String)value).toLowerCase();
-            }
-            assert !(value instanceof Binary);
-            ValueFactory<String> stringFactory = getContext().getExecutionContext().getValueFactories().getStringFactory();
-            ValueFactory<?> valueFactory = getContext().getExecutionContext().getValueFactories().getValueFactory(value);
-            return valueFactory.create(stringFactory.create(value).toLowerCase());
-        }
-
-        protected Query createQuery( SelectorName selectorName,
-                                     Name propertyName ) {
-            Term term = new Term(fieldNameFor(propertyName));
-            return new TermQuery(term);
-        }
-
-        protected Query createQuery( String fieldName,
-                                     FullTextSearch.Term term ) {
-            if (term instanceof FullTextSearch.Conjunction) {
-                FullTextSearch.Conjunction conjunction = (FullTextSearch.Conjunction)term;
-                BooleanQuery query = new BooleanQuery();
-                for (FullTextSearch.Term nested : conjunction) {
-                    if (nested instanceof NegationTerm) {
-                        query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
-                    } else {
-                        query.add(createQuery(fieldName, nested), Occur.MUST);
-                    }
-                }
-                return query;
-            }
-            if (term instanceof FullTextSearch.Disjunction) {
-                FullTextSearch.Disjunction disjunction = (FullTextSearch.Disjunction)term;
-                BooleanQuery query = new BooleanQuery();
-                for (FullTextSearch.Term nested : disjunction) {
-                    if (nested instanceof NegationTerm) {
-                        query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
-                    } else {
-                        query.add(createQuery(fieldName, nested), Occur.SHOULD);
-                    }
-                }
-                return query;
-            }
-            if (term instanceof FullTextSearch.SimpleTerm) {
-                FullTextSearch.SimpleTerm simple = (FullTextSearch.SimpleTerm)term;
-                if (simple.isQuotingRequired()) {
-                    PhraseQuery query = new PhraseQuery();
-                    query.setSlop(0); // terms must be adjacent
-                    for (String value : simple.getValues()) {
-                        query.add(new Term(fieldName, value));
-                    }
-                    return query;
-                }
-                return new TermQuery(new Term(fieldName, simple.getValue()));
-            }
-            // Should not get here ...
-            assert false;
-            return null;
-        }
-    }
-
-    /**
-     * This collector is responsible for loading the value for each of the columns into each tuple array.
-     */
-    protected static class TupleCollector extends Collector {
-        private final LinkedList<Object[]> tuples = new LinkedList<Object[]>();
-        private final Columns columns;
-        private final int numValues;
-        private final boolean recordScore;
-        private final int scoreIndex;
-        private final FieldSelector fieldSelector;
-        private final int locationIndex;
-        private final ValueFactory<UUID> uuidFactory;
-        private Scorer scorer;
-        private IndexReader currentReader;
-        private int docOffset;
-
-        protected TupleCollector( Columns columns,
-                                  ValueFactory<UUID> uuidFactory ) {
-            this.columns = columns;
-            this.uuidFactory = uuidFactory;
-            assert this.columns != null;
-            assert this.uuidFactory != null;
-            this.numValues = this.columns.getTupleSize();
-            assert this.numValues >= 0;
-            assert this.columns.getSelectorNames().size() == 1;
-            final String selectorName = this.columns.getSelectorNames().get(0);
-            this.locationIndex = this.columns.getLocationIndex(selectorName);
-            this.recordScore = this.columns.hasFullTextSearchScores();
-            this.scoreIndex = this.recordScore ? this.columns.getFullTextSearchScoreIndexFor(selectorName) : -1;
-            final Set<String> columnNames = new HashSet<String>(this.columns.getColumnNames());
-            columnNames.add(ContentIndex.UUID); // add the UUID, which we'll put into the Location ...
-            this.fieldSelector = new FieldSelector() {
-                private static final long serialVersionUID = 1L;
-
-                public FieldSelectorResult accept( String fieldName ) {
-                    return columnNames.contains(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
-                }
-            };
-        }
-
-        /**
-         * @return tuples
-         */
-        public LinkedList<Object[]> getTuples() {
-            return tuples;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
-         */
-        @Override
-        public boolean acceptsDocsOutOfOrder() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
-         */
-        @Override
-        public void setNextReader( IndexReader reader,
-                                   int docBase ) {
-            this.currentReader = reader;
-            this.docOffset = docBase;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
-         */
-        @Override
-        public void setScorer( Scorer scorer ) {
-            this.scorer = scorer;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#collect(int)
-         */
-        @Override
-        public void collect( int doc ) throws IOException {
-            int docId = doc + docOffset;
-            Object[] tuple = new Object[numValues];
-            Document document = currentReader.document(docId, fieldSelector);
-            for (String columnName : columns.getColumnNames()) {
-                int index = columns.getColumnIndexForName(columnName);
-                // We just need to retrieve the first value if there is more than one ...
-                tuple[index] = document.get(columnName);
-            }
-
-            // Set the score column if required ...
-            if (recordScore) {
-                assert scorer != null;
-                tuple[scoreIndex] = scorer.score();
-            }
-
-            // Load the UUID into a Location object ...
-            UUID uuid = uuidFactory.create(document.get(ContentIndex.UUID));
-            tuple[locationIndex] = Location.create(uuid);
-            tuples.add(tuple);
-        }
-    }
-
-}

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfiguration.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfiguration.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfiguration.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,60 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.store.Directory;
+import org.jboss.dna.graph.search.SearchEngineException;
+
+/**
+ * Interface used to obtain the Lucene {@link Directory} instance that should be used for a workspace given the name of the
+ * workspace. There are several implementations (see {@link LuceneConfigurations}), but custom implementations can always be
+ * used.
+ */
+ at ThreadSafe
+public interface LuceneConfiguration {
+    /**
+     * Get the {@link Directory} that should be used for the workspace with the supplied name.
+     * 
+     * @param workspaceName the workspace name
+     * @param indexName the name of the index to be created
+     * @return the directory; never null
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem creating the directory
+     */
+    Directory getDirectory( String workspaceName,
+                            String indexName ) throws SearchEngineException;
+
+    /**
+     * Destroy the {@link Directory} that is used for the workspace with the supplied name.
+     * 
+     * @param workspaceName the workspace name
+     * @param indexName the name of the index to be created
+     * @return true if the directory existed and was destroyed, or false if the directory didn't exist
+     * @throws IllegalArgumentException if the workspace name is null
+     * @throws SearchEngineException if there is a problem creating the directory
+     */
+    boolean destroyDirectory( String workspaceName,
+                              String indexName ) throws SearchEngineException;
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfiguration.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfigurations.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfigurations.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfigurations.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,427 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.LockFactory;
+import org.apache.lucene.store.RAMDirectory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.FileUtil;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.search.SearchEngineException;
+
+/**
+ * A family of {@link LuceneConfiguration} implementations.
+ */
+public class LuceneConfigurations {
+
+    /**
+     * Return a new {@link LuceneConfiguration} that creates in-memory directories.
+     * 
+     * @return the new directory configuration; never null
+     */
+    public static final LuceneConfiguration inMemory() {
+        return new RamDirectoryFactory();
+    }
+
+    /**
+     * Return a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+     * workspace names.
+     * 
+     * @param parent the parent folder
+     * @return the new directory configuration; never null
+     * @throws IllegalArgumentException if the parent file is null
+     */
+    public static final LuceneConfiguration using( File parent ) {
+        return new FileSystemDirectoryFromNameFactory(parent);
+    }
+
+    /**
+     * Return a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+     * workspace names.
+     * 
+     * @param parent the parent folder
+     * @param lockFactory the lock factory; may be null
+     * @return the new directory configuration; never null
+     * @throws IllegalArgumentException if the parent file is null
+     */
+    public static final LuceneConfiguration using( File parent,
+                                                      LockFactory lockFactory ) {
+        return new FileSystemDirectoryFromNameFactory(parent, lockFactory);
+    }
+
+    /**
+     * Return a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+     * workspace names.
+     * 
+     * @param parent the parent folder
+     * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+     * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+     * @return the new directory configuration; never null
+     * @throws IllegalArgumentException if the parent file is null
+     */
+    public static final LuceneConfiguration using( File parent,
+                                                      TextEncoder workspaceNameEncoder,
+                                                      TextEncoder indexNameEncoder ) {
+        return new FileSystemDirectoryFromNameFactory(parent, workspaceNameEncoder, indexNameEncoder);
+    }
+
+    /**
+     * Return a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+     * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+     * workspace names.
+     * 
+     * @param parent the parent folder
+     * @param lockFactory the lock factory; may be null
+     * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+     * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+     * @return the new directory configuration; never null
+     * @throws IllegalArgumentException if the parent file is null
+     */
+    public static final LuceneConfiguration using( File parent,
+                                                      LockFactory lockFactory,
+                                                      TextEncoder workspaceNameEncoder,
+                                                      TextEncoder indexNameEncoder ) {
+        return new FileSystemDirectoryFromNameFactory(parent, lockFactory, workspaceNameEncoder, indexNameEncoder);
+    }
+
+    /**
+     * A {@link LuceneConfiguration} implementation that creates {@link Directory} instances of the supplied type for each
+     * workspace and pools the results, ensuring that the same {@link Directory} instance is always returned for the same
+     * workspace name.
+     * 
+     * @param <DirectoryType> the concrete type of the directory
+     */
+    @ThreadSafe
+    protected static abstract class PoolingDirectoryFactory<DirectoryType extends Directory> implements LuceneConfiguration {
+        private final ConcurrentHashMap<IndexId, DirectoryType> directories = new ConcurrentHashMap<IndexId, DirectoryType>();
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneConfiguration#getDirectory(java.lang.String, java.lang.String)
+         */
+        public Directory getDirectory( String workspaceName,
+                                       String indexName ) throws SearchEngineException {
+            CheckArg.isNotNull(workspaceName, "workspaceName");
+            IndexId id = new IndexId(workspaceName, indexName);
+            DirectoryType result = directories.get(id);
+            if (result == null) {
+                DirectoryType newDirectory = createDirectory(workspaceName, indexName);
+                result = directories.putIfAbsent(id, newDirectory);
+                if (result == null) result = newDirectory;
+            }
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneConfiguration#destroyDirectory(java.lang.String, java.lang.String)
+         */
+        public boolean destroyDirectory( String workspaceName,
+                                         String indexName ) throws SearchEngineException {
+            CheckArg.isNotNull(workspaceName, "workspaceName");
+            IndexId id = new IndexId(workspaceName, indexName);
+            DirectoryType result = directories.remove(id);
+            return result != null ? doDestroy(result) : false;
+        }
+
+        /**
+         * Method implemented by subclasses to create a new Directory implementation.
+         * 
+         * @param workspaceName the name of the workspace for which the {@link Directory} is to be created; never null
+         * @param indexName the name of the index to be created
+         * @return the new directory; may not be null
+         * @throws SearchEngineException if there is a problem creating the directory
+         */
+        protected abstract DirectoryType createDirectory( String workspaceName,
+                                                          String indexName ) throws SearchEngineException;
+
+        protected abstract boolean doDestroy( DirectoryType directory ) throws SearchEngineException;
+    }
+
+    /**
+     * A {@link LuceneConfiguration} implementation that creates {@link RAMDirectory} instances for each workspace and index
+     * name. Each factory instance maintains a pool of {@link RAMDirectory} instances, ensuring that the same {@link RAMDirectory}
+     * is always returned for the same workspace name.
+     */
+    @ThreadSafe
+    public static class RamDirectoryFactory extends PoolingDirectoryFactory<RAMDirectory> {
+        protected RamDirectoryFactory() {
+        }
+
+        @Override
+        protected RAMDirectory createDirectory( String workspaceName,
+                                                String indexName ) {
+            return new RAMDirectory();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
+         */
+        @Override
+        protected boolean doDestroy( RAMDirectory directory ) throws SearchEngineException {
+            return directory != null;
+        }
+    }
+
+    /**
+     * A {@link LuceneConfiguration} implementation that creates {@link FSDirectory} instances for each workspace and index
+     * name. This factory is created with a parent directory under which all workspace and index directories are created.
+     * <p>
+     * This uses the supplied encoders to translate the workspace and index names into valid directory names. By default, no
+     * encoding is performed, meaning that the workspace and index names are used explicitly as directory names. This default
+     * behavior, then, means that not all values of workspace names or index names will work. If you want to be sure that all
+     * workspace names work, supply an encoder for the workspace names. (Index names are currently such that they will always be
+     * valid directory names, but you can always supply an encoder if you'd like.)
+     * </p>
+     */
+    public static class FileSystemDirectoryFromNameFactory extends PoolingDirectoryFactory<FSDirectory> {
+        private final File parentFile;
+        private final LockFactory lockFactory;
+        private final TextEncoder workspaceNameEncoder;
+        private final TextEncoder indexNameEncoder;
+
+        /**
+         * Create a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+         * allowable workspace names.
+         * 
+         * @param parent the parent folder
+         * @throws IllegalArgumentException if the parent file is null
+         */
+        protected FileSystemDirectoryFromNameFactory( File parent ) {
+            this(parent, null, null, null);
+        }
+
+        /**
+         * Create a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+         * allowable workspace names.
+         * 
+         * @param parent the parent folder
+         * @param lockFactory the lock factory; may be null
+         * @throws IllegalArgumentException if the parent file is null
+         */
+        protected FileSystemDirectoryFromNameFactory( File parent,
+                                                      LockFactory lockFactory ) {
+            this(parent, lockFactory, null, null);
+        }
+
+        /**
+         * Create a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+         * allowable workspace names.
+         * 
+         * @param parent the parent folder
+         * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+         * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+         * @throws IllegalArgumentException if the parent file is null
+         */
+        protected FileSystemDirectoryFromNameFactory( File parent,
+                                                      TextEncoder workspaceNameEncoder,
+                                                      TextEncoder indexNameEncoder ) {
+            this(parent, null, workspaceNameEncoder, indexNameEncoder);
+        }
+
+        /**
+         * Create a new {@link LuceneConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+         * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+         * allowable workspace names.
+         * 
+         * @param parent the parent folder
+         * @param lockFactory the lock factory; may be null
+         * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+         * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+         * @throws IllegalArgumentException if the parent file is null
+         */
+        protected FileSystemDirectoryFromNameFactory( File parent,
+                                                      LockFactory lockFactory,
+                                                      TextEncoder workspaceNameEncoder,
+                                                      TextEncoder indexNameEncoder ) {
+            CheckArg.isNotNull(parent, "parent");
+            this.parentFile = parent;
+            this.lockFactory = lockFactory;
+            this.workspaceNameEncoder = workspaceNameEncoder != null ? workspaceNameEncoder : new NoOpEncoder();
+            this.indexNameEncoder = indexNameEncoder != null ? indexNameEncoder : new NoOpEncoder();
+        }
+
+        @Override
+        protected FSDirectory createDirectory( String workspaceName,
+                                               String indexName ) {
+            File workspaceFile = new File(parentFile, workspaceNameEncoder.encode(workspaceName));
+            if (!workspaceFile.exists()) {
+                workspaceFile.mkdirs();
+            } else {
+                if (!workspaceFile.isDirectory()) {
+                    I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
+                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+                }
+                if (!workspaceFile.canRead()) {
+                    I18n msg = SearchI18n.locationForIndexesCannotBeRead;
+                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+                }
+                if (!workspaceFile.canWrite()) {
+                    I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
+                    throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+                }
+            }
+            File directory = workspaceFile;
+            if (indexName != null) {
+                File indexFile = new File(workspaceFile, indexNameEncoder.encode(indexName));
+                if (!indexFile.exists()) {
+                    indexFile.mkdirs();
+                } else {
+                    if (!indexFile.isDirectory()) {
+                        I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
+                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+                    }
+                    if (!indexFile.canRead()) {
+                        I18n msg = SearchI18n.locationForIndexesCannotBeRead;
+                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+                    }
+                    if (!indexFile.canWrite()) {
+                        I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
+                        throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+                    }
+                }
+                directory = indexFile;
+            }
+            try {
+                return create(directory, lockFactory);
+            } catch (IOException e) {
+                throw new SearchEngineException(e);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.LuceneConfigurations.PoolingDirectoryFactory#doDestroy(org.apache.lucene.store.Directory)
+         */
+        @Override
+        protected boolean doDestroy( FSDirectory directory ) throws SearchEngineException {
+            File file = directory.getFile();
+            if (file.exists()) {
+                return FileUtil.delete(file);
+            }
+            return false;
+        }
+
+        /**
+         * Override this method to define which subclass of {@link FSDirectory} should be created.
+         * 
+         * @param directory the file system directory; never null
+         * @param lockFactory the lock factory; may be null
+         * @return the {@link FSDirectory} instance
+         * @throws IOException if there is a problem creating the FSDirectory instance
+         */
+        protected FSDirectory create( File directory,
+                                      LockFactory lockFactory ) throws IOException {
+            return FSDirectory.open(directory, lockFactory);
+        }
+    }
+
+    @Immutable
+    protected static final class IndexId {
+        private final String workspaceName;
+        private final String indexName;
+        private final int hc;
+
+        protected IndexId( String workspaceName,
+                           String indexName ) {
+            assert workspaceName != null;
+            this.workspaceName = workspaceName;
+            this.indexName = indexName;
+            this.hc = HashCode.compute(this.workspaceName, this.indexName);
+        }
+
+        /**
+         * @return indexName
+         */
+        public String getIndexName() {
+            return indexName;
+        }
+
+        /**
+         * @return workspaceName
+         */
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return hc;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof IndexId) {
+                IndexId that = (IndexId)obj;
+                if (this.hashCode() != that.hashCode()) return false;
+                if (!this.workspaceName.equals(that.workspaceName)) return false;
+                if (!this.indexName.equals(that.indexName)) return false;
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return indexName != null ? workspaceName + "/" + this.indexName : this.workspaceName;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneConfigurations.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneException.java (from rev 1328, trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneException.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneException.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,74 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.search;
+
+/**
+ * A {@link RuntimeException runtime exception} representing a problem operating against Lucene.
+ */
+public class LuceneException extends RuntimeException {
+
+    /**
+     */
+    private static final long serialVersionUID = 8281373010920861138L;
+
+    /**
+     * Construct a system failure exception with no message.
+     */
+    public LuceneException() {
+    }
+
+    /**
+     * Construct a system failure exception with a single message.
+     * 
+     * @param message the message describing the failure
+     */
+    public LuceneException( String message ) {
+        super(message);
+
+    }
+
+    /**
+     * Construct a system failure exception with another exception that is the cause of the failure.
+     * 
+     * @param cause the original cause of the failure
+     */
+    public LuceneException( Throwable cause ) {
+        super(cause);
+
+    }
+
+    /**
+     * Construct a system failure exception with a single message and another exception that is the cause of the failure.
+     * 
+     * @param message the message describing the failure
+     * @param cause the original cause of the failure
+     */
+    public LuceneException( String message,
+                            Throwable cause ) {
+        super(message, cause);
+
+    }
+
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneException.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,609 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Between;
+import org.jboss.dna.graph.query.model.BindVariableName;
+import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.UpperCase;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.AbstractAccessComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.SelectComponent;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+import org.jboss.dna.search.DualIndexSearchProvider.ContentIndex;
+
+/**
+ * 
+ */
+/**
+ * The {@link ProcessingComponent} implementation that executes a single atomic access query against the Lucene indexes.
+ */
+public class LuceneQueryComponent extends AbstractAccessComponent {
+    private final QueryCommand originalQuery;
+    private final LuceneSession session;
+    private final String sourceName;
+    private final String workspaceName;
+
+    protected LuceneQueryComponent( LuceneSession session,
+                                    QueryCommand originalQuery,
+                                    QueryContext context,
+                                    Columns columns,
+                                    PlanNode accessNode,
+                                    Analyzer analyzer,
+                                    String sourceName,
+                                    String workspaceName ) {
+        super(context, columns, accessNode);
+        this.originalQuery = originalQuery;
+        this.session = session;
+        this.sourceName = sourceName;
+        this.workspaceName = workspaceName;
+    }
+
+    protected String fieldNameFor( Name name ) {
+        return session.stringFactory.create(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+     */
+    @Override
+    public List<Object[]> execute() {
+
+        // Some kinds of constraints are not easily pushed down to Lucene as are of a Lucene Query, and
+        // instead are applied by filtering the results. For example, a FullTextSearchScore applies
+        // to the score of the tuple, which cannot be (easily?) applied as a Query.
+        //
+        // Therefore, each of the AND-ed constraints of the query are evaluated separately. After all,
+        // each of the tuples returned by the planned query must satisfy all of the AND-ed constraints.
+        // Or, to put it another way, if a tuple does not satisfy one of the AND-ed constraints, the
+        // tuple should not be included in the query results.
+        //
+        // Logically, any AND-ed criteria that cannot be pushed down to Lucene can of course be applied
+        // as a filter on the results. Thus, each AND-ed constraint is processed to first determine if
+        // it can be represented as a Lucene query; all other AND-ed constraints must be handled as
+        // a results filter. Since most queries will likely use one or more simple constraints AND-ed
+        // together, this approach will likely work very well.
+        //
+        // The only hairy case is when any AND-ed constraint is actually an OR-ed combination of multiple
+        // constraints of which at least one cannot be pushed down to Lucene. In this case, the entire
+        // AND-ed constraint must be treated as a results filter (even if many of those constraints that
+        // make up the OR-ed constraint can be pushed down). Hopefully, this will not be a common case
+        // in actual queries.
+
+        // For each of the AND-ed constraints ...
+        Query pushDownQuery = null;
+        Constraint postProcessConstraint = null;
+        try {
+            for (Constraint andedConstraint : this.andedConstraints) {
+                // Determine if it can be represented as a Lucene query ...
+                Query constraintQuery = createQuery(andedConstraint);
+                if (constraintQuery != null) {
+                    // The AND-ed constraint _can_ be represented as a push-down Lucene query ...
+                    if (pushDownQuery == null) {
+                        // This must be the first query ...
+                        pushDownQuery = constraintQuery;
+                    } else if (pushDownQuery instanceof BooleanQuery) {
+                        // We have to add the constraint query to the existing boolean ...
+                        BooleanQuery booleanQuery = (BooleanQuery)pushDownQuery;
+                        booleanQuery.add(constraintQuery, Occur.MUST);
+                    } else {
+                        // This is the second push-down query, so create a BooleanQuery ...
+                        BooleanQuery booleanQuery = new BooleanQuery();
+                        booleanQuery.add(pushDownQuery, Occur.MUST);
+                        booleanQuery.add(constraintQuery, Occur.MUST);
+                        pushDownQuery = booleanQuery;
+                    }
+                } else {
+                    // The AND-ed constraint _cannot_ be represented as a push-down Lucene query ...
+                    if (postProcessConstraint == null) {
+                        postProcessConstraint = andedConstraint;
+                    } else {
+                        postProcessConstraint = new And(postProcessConstraint, andedConstraint);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            // There was a error working with the constraints (such as a ValueFormatException) ...
+            QueryContext context = getContext();
+            I18n msg = SearchI18n.errorWhilePerformingQuery;
+            String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+            context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
+            return emptyTuples();
+        } catch (RuntimeException e) {
+            // There was a error working with the constraints (such as a ValueFormatException) ...
+            QueryContext context = getContext();
+            I18n msg = SearchI18n.errorWhilePerformingQuery;
+            String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+            context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
+            return emptyTuples();
+        }
+
+        if (pushDownQuery == null) {
+            // There are no constraints that can be pushed down, so return _all_ the nodes ...
+            pushDownQuery = new MatchAllDocsQuery();
+        }
+
+        // Get the results from Lucene ...
+        List<Object[]> tuples = null;
+        final Columns columns = getColumns();
+        final QueryContext context = getContext();
+        final ExecutionContext execContext = context.getExecutionContext();
+        try {
+            // Execute the query against the content indexes ...
+            IndexSearcher searcher = session.getContentSearcher();
+            TupleCollector collector = new TupleCollector(columns, execContext.getValueFactories().getUuidFactory());
+            searcher.search(pushDownQuery, collector);
+            tuples = collector.getTuples();
+        } catch (IOException e) {
+            // There was a problem executing the Lucene query ...
+            I18n msg = SearchI18n.errorWhilePerformingLuceneQuery;
+            String origQueryString = Visitors.readable(originalQuery, execContext);
+            context.getProblems().addError(e, msg, pushDownQuery, origQueryString, workspaceName, sourceName, e.getMessage());
+            return emptyTuples();
+        }
+
+        if (postProcessConstraint != null && !tuples.isEmpty()) {
+            // Create a delegate processing component that will return the tuples we've already found ...
+            final List<Object[]> allTuples = tuples;
+            ProcessingComponent tuplesProcessor = new ProcessingComponent(context, columns) {
+                @Override
+                public List<Object[]> execute() {
+                    return allTuples;
+                }
+            };
+            // Create a processing component that will apply these constraints to the tuples we already found ...
+            return new SelectComponent(tuplesProcessor, postProcessConstraint, context.getVariables()).execute();
+        }
+        return tuples;
+    }
+
+    protected Query createQuery( Constraint constraint ) throws IOException {
+        if (constraint instanceof And) {
+            And and = (And)constraint;
+            Query leftQuery = createQuery(and.getLeft());
+            Query rightQuery = createQuery(and.getRight());
+            if (leftQuery == null || rightQuery == null) return null;
+            BooleanQuery booleanQuery = new BooleanQuery();
+            booleanQuery.add(createQuery(and.getLeft()), Occur.MUST);
+            booleanQuery.add(createQuery(and.getRight()), Occur.MUST);
+            return booleanQuery;
+        }
+        if (constraint instanceof Or) {
+            Or or = (Or)constraint;
+            Query leftQuery = createQuery(or.getLeft());
+            Query rightQuery = createQuery(or.getRight());
+            if (leftQuery == null) {
+                return rightQuery != null ? rightQuery : null;
+            } else if (rightQuery == null) {
+                return leftQuery;
+            }
+            BooleanQuery booleanQuery = new BooleanQuery();
+            booleanQuery.add(createQuery(or.getLeft()), Occur.SHOULD);
+            booleanQuery.add(createQuery(or.getRight()), Occur.SHOULD);
+            return booleanQuery;
+        }
+        if (constraint instanceof Not) {
+            Not not = (Not)constraint;
+            Query notted = createQuery(not.getConstraint());
+            if (notted == null) return new MatchAllDocsQuery();
+        }
+        if (constraint instanceof SetCriteria) {
+            SetCriteria setCriteria = (SetCriteria)constraint;
+            DynamicOperand left = setCriteria.getLeftOperand();
+            int numRightOperands = setCriteria.getRightOperands().size();
+            assert numRightOperands > 0;
+            if (numRightOperands == 1) {
+                return createQuery(left, Operator.EQUAL_TO, setCriteria.getRightOperands().iterator().next());
+            }
+            BooleanQuery setQuery = new BooleanQuery();
+            for (StaticOperand right : setCriteria.getRightOperands()) {
+                Query rightQuery = createQuery(left, Operator.EQUAL_TO, right);
+                if (rightQuery == null) return null;
+                setQuery.add(rightQuery, Occur.SHOULD);
+            }
+            return setQuery;
+        }
+        if (constraint instanceof PropertyExistence) {
+            PropertyExistence existence = (PropertyExistence)constraint;
+            return createQuery(existence.getSelectorName(), existence.getPropertyName());
+        }
+        if (constraint instanceof Between) {
+            Between between = (Between)constraint;
+            return createQuery(between);
+        }
+        if (constraint instanceof Comparison) {
+            Comparison comparison = (Comparison)constraint;
+            return createQuery(comparison.getOperand1(), comparison.getOperator(), comparison.getOperand2());
+        }
+        if (constraint instanceof FullTextSearch) {
+            FullTextSearch search = (FullTextSearch)constraint;
+            String fieldName = ContentIndex.FULL_TEXT;
+            Name propertyName = search.getPropertyName();
+            if (propertyName != null) {
+                fieldName = session.fullTextFieldName(fieldNameFor(propertyName));
+            }
+            return createQuery(fieldName, search.getTerm());
+        }
+        try {
+            if (constraint instanceof SameNode) {
+                SameNode sameNode = (SameNode)constraint;
+                return session.findNodeAt(sameNode.getPath());
+            }
+            if (constraint instanceof ChildNode) {
+                ChildNode childNode = (ChildNode)constraint;
+                return session.findChildNodes(childNode.getParentPath());
+            }
+            if (constraint instanceof DescendantNode) {
+                DescendantNode descendantNode = (DescendantNode)constraint;
+                return session.findAllNodesBelow(descendantNode.getAncestorPath());
+            }
+        } catch (IOException e) {
+            I18n msg = SearchI18n.errorWhilePerformingQuery;
+            getContext().getProblems().addError(e,
+                                                msg,
+                                                Visitors.readable(originalQuery),
+                                                workspaceName,
+                                                sourceName,
+                                                e.getMessage());
+            return null;
+        }
+        // Should not get here ...
+        assert false;
+        return null;
+    }
+
+    protected Query createQuery( DynamicOperand left,
+                                 Operator operator,
+                                 StaticOperand right ) throws IOException {
+        return createQuery(left, operator, right, true);
+    }
+
+    protected Query createQuery( DynamicOperand left,
+                                 Operator operator,
+                                 StaticOperand right,
+                                 boolean caseSensitive ) throws IOException {
+        // Handle the static operand ...
+        Object value = createOperand(right, caseSensitive);
+        assert value != null;
+
+        // Address the dynamic operand ...
+        if (left instanceof FullTextSearchScore) {
+            // This can only be represented as a filter ...
+            return null;
+        } else if (left instanceof PropertyValue) {
+            return session.findNodesWith((PropertyValue)left, operator, value, caseSensitive);
+        } else if (left instanceof Length) {
+            return session.findNodesWith((Length)left, operator, right);
+        } else if (left instanceof LowerCase) {
+            LowerCase lowercase = (LowerCase)left;
+            return createQuery(lowercase.getOperand(), operator, right, false);
+        } else if (left instanceof UpperCase) {
+            UpperCase lowercase = (UpperCase)left;
+            return createQuery(lowercase.getOperand(), operator, right, false);
+        } else if (left instanceof NodeDepth) {
+            assert operator != Operator.LIKE;
+            // Could be represented as a result filter, but let's do this now ...
+            return session.findNodesWith((NodeDepth)left, operator, value);
+        } else if (left instanceof NodePath) {
+            return session.findNodesWith((NodePath)left, operator, value, caseSensitive);
+        } else if (left instanceof NodeName) {
+            return session.findNodesWith((NodeName)left, operator, value, caseSensitive);
+        } else if (left instanceof NodeLocalName) {
+            return session.findNodesWith((NodeLocalName)left, operator, value, caseSensitive);
+        } else {
+            assert false;
+            return null;
+        }
+    }
+
+    protected Object createOperand( StaticOperand operand,
+                                    boolean caseSensitive ) {
+        Object value = null;
+        if (operand instanceof Literal) {
+            Literal literal = (Literal)operand;
+            value = literal.getValue();
+            if (!caseSensitive) value = lowerCase(value);
+        } else if (operand instanceof BindVariableName) {
+            BindVariableName variable = (BindVariableName)operand;
+            String variableName = variable.getVariableName();
+            value = getContext().getVariables().get(variableName);
+            if (!caseSensitive) value = lowerCase(value);
+        } else {
+            assert false;
+        }
+        return value;
+    }
+
+    protected Query createQuery( DynamicOperand left,
+                                 StaticOperand lower,
+                                 StaticOperand upper,
+                                 boolean includesLower,
+                                 boolean includesUpper,
+                                 boolean caseSensitive ) throws IOException {
+        // Handle the static operands ...
+        Object lowerValue = createOperand(lower, caseSensitive);
+        Object upperValue = createOperand(upper, caseSensitive);
+        assert lowerValue != null;
+        assert upperValue != null;
+
+        // Only in the case of a PropertyValue and Depth will we need to do something special ...
+        if (left instanceof NodeDepth) {
+            return session.findNodesWithNumericRange((NodeDepth)left, lowerValue, upperValue, includesLower, includesUpper);
+        } else if (left instanceof PropertyValue) {
+            PropertyType lowerType = PropertyType.discoverType(lowerValue);
+            PropertyType upperType = PropertyType.discoverType(upperValue);
+            if (upperType == lowerType) {
+                switch (upperType) {
+                    case DATE:
+                    case LONG:
+                    case DOUBLE:
+                    case DECIMAL:
+                        return session.findNodesWithNumericRange((PropertyValue)left,
+                                                                 lowerValue,
+                                                                 upperValue,
+                                                                 includesLower,
+                                                                 includesUpper);
+                    default:
+                        // continue on and handle as boolean query ...
+                }
+            }
+        }
+
+        // Otherwise, just create a boolean query ...
+        BooleanQuery query = new BooleanQuery();
+        Operator lowerOp = includesLower ? Operator.GREATER_THAN_OR_EQUAL_TO : Operator.GREATER_THAN;
+        Operator upperOp = includesUpper ? Operator.LESS_THAN_OR_EQUAL_TO : Operator.LESS_THAN;
+        Query lowerQuery = createQuery(left, lowerOp, lower, caseSensitive);
+        Query upperQuery = createQuery(left, upperOp, upper, caseSensitive);
+        if (lowerQuery == null || upperQuery == null) return null;
+        query.add(lowerQuery, Occur.MUST);
+        query.add(upperQuery, Occur.MUST);
+        return query;
+    }
+
+    protected Object lowerCase( Object value ) {
+        if (value instanceof String) {
+            return ((String)value).toLowerCase();
+        }
+        assert !(value instanceof Binary);
+        ValueFactory<String> stringFactory = getContext().getExecutionContext().getValueFactories().getStringFactory();
+        ValueFactory<?> valueFactory = getContext().getExecutionContext().getValueFactories().getValueFactory(value);
+        return valueFactory.create(stringFactory.create(value).toLowerCase());
+    }
+
+    protected Query createQuery( SelectorName selectorName,
+                                 Name propertyName ) {
+        Term term = new Term(fieldNameFor(propertyName));
+        return new TermQuery(term);
+    }
+
+    protected Query createQuery( String fieldName,
+                                 FullTextSearch.Term term ) {
+        if (term instanceof FullTextSearch.Conjunction) {
+            FullTextSearch.Conjunction conjunction = (FullTextSearch.Conjunction)term;
+            BooleanQuery query = new BooleanQuery();
+            for (FullTextSearch.Term nested : conjunction) {
+                if (nested instanceof NegationTerm) {
+                    query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
+                } else {
+                    query.add(createQuery(fieldName, nested), Occur.MUST);
+                }
+            }
+            return query;
+        }
+        if (term instanceof FullTextSearch.Disjunction) {
+            FullTextSearch.Disjunction disjunction = (FullTextSearch.Disjunction)term;
+            BooleanQuery query = new BooleanQuery();
+            for (FullTextSearch.Term nested : disjunction) {
+                if (nested instanceof NegationTerm) {
+                    query.add(createQuery(fieldName, ((NegationTerm)nested).getNegatedTerm()), Occur.MUST_NOT);
+                } else {
+                    query.add(createQuery(fieldName, nested), Occur.SHOULD);
+                }
+            }
+            return query;
+        }
+        if (term instanceof FullTextSearch.SimpleTerm) {
+            FullTextSearch.SimpleTerm simple = (FullTextSearch.SimpleTerm)term;
+            if (simple.isQuotingRequired()) {
+                PhraseQuery query = new PhraseQuery();
+                query.setSlop(0); // terms must be adjacent
+                for (String value : simple.getValues()) {
+                    query.add(new Term(fieldName, value));
+                }
+                return query;
+            }
+            return new TermQuery(new Term(fieldName, simple.getValue()));
+        }
+        // Should not get here ...
+        assert false;
+        return null;
+    }
+
+    /**
+     * This collector is responsible for loading the value for each of the columns into each tuple array.
+     */
+    protected static class TupleCollector extends Collector {
+        private final LinkedList<Object[]> tuples = new LinkedList<Object[]>();
+        private final Columns columns;
+        private final int numValues;
+        private final boolean recordScore;
+        private final int scoreIndex;
+        private final FieldSelector fieldSelector;
+        private final int locationIndex;
+        private final ValueFactory<UUID> uuidFactory;
+        private Scorer scorer;
+        private IndexReader currentReader;
+        private int docOffset;
+
+        protected TupleCollector( Columns columns,
+                                  ValueFactory<UUID> uuidFactory ) {
+            this.columns = columns;
+            this.uuidFactory = uuidFactory;
+            assert this.columns != null;
+            assert this.uuidFactory != null;
+            this.numValues = this.columns.getTupleSize();
+            assert this.numValues >= 0;
+            assert this.columns.getSelectorNames().size() == 1;
+            final String selectorName = this.columns.getSelectorNames().get(0);
+            this.locationIndex = this.columns.getLocationIndex(selectorName);
+            this.recordScore = this.columns.hasFullTextSearchScores();
+            this.scoreIndex = this.recordScore ? this.columns.getFullTextSearchScoreIndexFor(selectorName) : -1;
+            final Set<String> columnNames = new HashSet<String>(this.columns.getColumnNames());
+            columnNames.add(ContentIndex.UUID); // add the UUID, which we'll put into the Location ...
+            this.fieldSelector = new FieldSelector() {
+                private static final long serialVersionUID = 1L;
+
+                public FieldSelectorResult accept( String fieldName ) {
+                    return columnNames.contains(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
+                }
+            };
+        }
+
+        /**
+         * @return tuples
+         */
+        public LinkedList<Object[]> getTuples() {
+            return tuples;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
+         */
+        @Override
+        public boolean acceptsDocsOutOfOrder() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public void setNextReader( IndexReader reader,
+                                   int docBase ) {
+            this.currentReader = reader;
+            this.docOffset = docBase;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
+         */
+        @Override
+        public void setScorer( Scorer scorer ) {
+            this.scorer = scorer;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#collect(int)
+         */
+        @Override
+        public void collect( int doc ) throws IOException {
+            int docId = doc + docOffset;
+            Object[] tuple = new Object[numValues];
+            Document document = currentReader.document(docId, fieldSelector);
+            for (String columnName : columns.getColumnNames()) {
+                int index = columns.getColumnIndexForName(columnName);
+                // We just need to retrieve the first value if there is more than one ...
+                tuple[index] = document.get(columnName);
+            }
+
+            // Set the score column if required ...
+            if (recordScore) {
+                assert scorer != null;
+                tuple[scoreIndex] = scorer.score();
+            }
+
+            // Load the UUID into a Location object ...
+            UUID uuid = uuidFactory.create(document.get(ContentIndex.UUID));
+            tuple[locationIndex] = Location.create(uuid);
+            tuples.add(tuple);
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -0,0 +1,368 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.graph.search.SearchProvider;
+import org.jboss.dna.search.DualIndexSearchProvider.PathIndex;
+
+/**
+ * 
+ */
+ at NotThreadSafe
+public abstract class LuceneSession implements SearchProvider.Session {
+    protected final ExecutionContext context;
+    protected final String sourceName;
+    protected final String workspaceName;
+    protected final IndexRules rules;
+    protected final Analyzer analyzer;
+    protected final boolean overwrite;
+    protected final boolean readOnly;
+    protected final ValueFactory<String> stringFactory;
+    protected final DateTimeFactory dateFactory;
+    protected final PathFactory pathFactory;
+    private int changeCount;
+    private QueryEngine queryEngine;
+
+    protected LuceneSession( ExecutionContext context,
+                             String sourceName,
+                             String workspaceName,
+                             IndexRules rules,
+                             Analyzer analyzer,
+                             boolean overwrite,
+                             boolean readOnly ) {
+        this.context = context;
+        this.sourceName = sourceName;
+        this.workspaceName = workspaceName;
+        this.rules = rules;
+        this.overwrite = overwrite;
+        this.readOnly = readOnly;
+        this.analyzer = analyzer;
+        this.stringFactory = context.getValueFactories().getStringFactory();
+        this.dateFactory = context.getValueFactories().getDateFactory();
+        this.pathFactory = context.getValueFactories().getPathFactory();
+        assert this.context != null;
+        assert this.sourceName != null;
+        assert this.workspaceName != null;
+        assert this.rules != null;
+        assert this.analyzer != null;
+        assert this.stringFactory != null;
+        assert this.dateFactory != null;
+    }
+
+    /**
+     * Create the field name that will be used to store the full-text searchable property values.
+     * 
+     * @param propertyName the name of the property; may not be null
+     * @return the field name for the full-text searchable property values; never null
+     */
+    protected abstract String fullTextFieldName( String propertyName );
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider.Session#getContext()
+     */
+    public final ExecutionContext getContext() {
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider.Session#getSourceName()
+     */
+    public final String getSourceName() {
+        return sourceName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider.Session#getWorkspaceName()
+     */
+    public String getWorkspaceName() {
+        return workspaceName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.search.SearchProvider.Session#hasChanges()
+     */
+    public boolean hasChanges() {
+        return changeCount > 0;
+    }
+
+    /**
+     * Get the Lucene index searcher that should be used to execute queries.
+     * 
+     * @return the searcher; never null
+     * @throws IOException if there is an error obtaining the index searcher
+     */
+    public abstract IndexSearcher getContentSearcher() throws IOException;
+
+    /**
+     * Get the query engine for this session.
+     * 
+     * @return the query engine; never null
+     */
+    protected QueryEngine queryEngine() {
+        if (queryEngine == null) {
+            // Create the query engine ...
+            Planner planner = new CanonicalPlanner();
+            Optimizer optimizer = new RuleBasedOptimizer() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+                 *      org.jboss.dna.graph.query.plan.PlanHints)
+                 */
+                @Override
+                protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+                                                  PlanHints hints ) {
+                    super.populateRuleStack(ruleStack, hints);
+                    // Add any custom rules here, either at the front of the stack or at the end
+                }
+            };
+            QueryProcessor processor = new QueryProcessor() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+                 *      org.jboss.dna.graph.query.QueryContext, org.jboss.dna.graph.query.plan.PlanNode,
+                 *      org.jboss.dna.graph.query.QueryResults.Columns,
+                 *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+                 */
+                @Override
+                protected ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                     QueryContext context,
+                                                                     PlanNode accessNode,
+                                                                     Columns resultColumns,
+                                                                     org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+                    try {
+                        return LuceneSession.this.createAccessComponent(originalQuery,
+                                                                        context,
+                                                                        accessNode,
+                                                                        resultColumns,
+                                                                        analyzer);
+                    } catch (IOException e) {
+                        I18n msg = SearchI18n.errorWhilePerformingQuery;
+                        context.getProblems().addError(e,
+                                                       msg,
+                                                       Visitors.readable(originalQuery),
+                                                       getWorkspaceName(),
+                                                       getSourceName(),
+                                                       e.getMessage());
+                        return null;
+                    }
+                }
+            };
+
+            queryEngine = new QueryEngine(planner, optimizer, processor);
+        }
+        return queryEngine;
+    }
+
+    protected abstract ProcessingComponent createAccessComponent( QueryCommand originalQuery,
+                                                                  QueryContext context,
+                                                                  PlanNode accessNode,
+                                                                  Columns resultColumns,
+                                                                  org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer )
+        throws IOException;
+
+    /**
+     * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
+     * 
+     * @param uuids the UUIDs of the nodes that are to be found; may not be null
+     * @return the query; never null
+     * @throws IOException if there is a problem creating this query
+     */
+    public abstract Query findAllNodesWithUuids( Set<UUID> uuids ) throws IOException;
+
+    public abstract Query findAllNodesBelow( Path ancestorPath ) throws IOException;
+
+    /**
+     * Return a query that can be used to find all of the documents that represent nodes that are children of the node at the
+     * supplied path.
+     * 
+     * @param parentPath the path of the parent node.
+     * @return the query; never null
+     * @throws IOException if there is an error creating the query
+     */
+    public abstract Query findChildNodes( Path parentPath ) throws IOException;
+
+    /**
+     * Create a query that can be used to find the one document (or node) that exists at the exact path supplied. This method
+     * first queries the {@link PathIndex path index} to find the UUID of the node at the supplied path, and then returns a query
+     * that matches the UUID.
+     * 
+     * @param path the path of the node
+     * @return the query; never null
+     * @throws IOException if there is an error creating the query
+     */
+    public abstract Query findNodeAt( Path path ) throws IOException;
+
+    /**
+     * Create a query that can be used to find documents (or nodes) that have a field value that satisfies the supplied LIKE
+     * expression.
+     * 
+     * @param fieldName the name of the document field to search
+     * @param likeExpression the JCR like expression
+     * @return the query; never null
+     * @throws IOException if there is an error creating the query
+     */
+    public abstract Query findNodesLike( String fieldName,
+                                         String likeExpression ) throws IOException;
+
+    public abstract Query findNodesWith( Length propertyLength,
+                                         Operator operator,
+                                         Object value ) throws IOException;
+
+    public abstract Query findNodesWith( PropertyValue propertyValue,
+                                         Operator operator,
+                                         Object value,
+                                         boolean caseSensitive ) throws IOException;
+
+    public abstract Query findNodesWithNumericRange( PropertyValue propertyValue,
+                                                     Object lowerValue,
+                                                     Object upperValue,
+                                                     boolean includesLower,
+                                                     boolean includesUpper ) throws IOException;
+
+    public abstract Query findNodesWithNumericRange( NodeDepth depth,
+                                                     Object lowerValue,
+                                                     Object upperValue,
+                                                     boolean includesLower,
+                                                     boolean includesUpper ) throws IOException;
+
+    // public abstract Query findNodesWithNumericRange( String field,
+    // Object lowerValue,
+    // Object upperValue,
+    // boolean includesLower,
+    // boolean includesUpper ) throws IOException;
+
+    public abstract Query findNodesWith( NodePath nodePath,
+                                         Operator operator,
+                                         Object value,
+                                         boolean caseSensitive ) throws IOException;
+
+    public abstract Query findNodesWith( NodeName nodeName,
+                                         Operator operator,
+                                         Object value,
+                                         boolean caseSensitive ) throws IOException;
+
+    public abstract Query findNodesWith( NodeLocalName nodeName,
+                                         Operator operator,
+                                         Object value,
+                                         boolean caseSensitive ) throws IOException;
+
+    public abstract Query findNodesWith( NodeDepth depthConstraint,
+                                         Operator operator,
+                                         Object value ) throws IOException;
+
+    // public abstract Query createLocalNameQuery( String likeExpression ) throws IOException;
+
+    // public abstract Query createSnsIndexQuery( String likeExpression ) throws IOException;
+
+    /**
+     * Convert the JCR like expression to a Lucene wildcard expression. The JCR like expression uses '%' to match 0 or more
+     * characters, '_' to match any single character, '\x' to match the 'x' character, and all other characters to match
+     * themselves.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    public String toWildcardExpression( String likeExpression ) {
+        assert likeExpression != null;
+        assert likeExpression.length() > 0;
+        return likeExpression.replace('%', '*').replace('_', '?').replaceAll("\\\\(.)", "$1");
+    }
+
+    /**
+     * Convert the JCR like expression to a regular expression. The JCR like expression uses '%' to match 0 or more characters,
+     * '_' to match any single character, '\x' to match the 'x' character, and all other characters to match themselves. Note that
+     * if any regex metacharacters appear in the like expression, they will be escaped within the resulting regular expression.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    public String toRegularExpression( String likeExpression ) {
+        assert likeExpression != null;
+        assert likeExpression.length() > 0;
+        // Replace all '\x' with 'x' ...
+        String result = likeExpression.replaceAll("\\\\(.)", "$1");
+        // Escape characters used as metacharacters in regular expressions, including
+        // '[', '^', '\', '$', '.', '|', '?', '*', '+', '(', and ')'
+        result = result.replaceAll("([[^\\\\$.|?*+()])", "\\$1");
+        // Replace '%'->'[.]+' and '_'->'[.]
+        result = likeExpression.replace("%", "[.]+").replace("_", "[.]");
+        return result;
+    }
+
+    public String pathAsString( Path path,
+                                ValueFactory<String> stringFactory ) {
+        assert path != null;
+        if (path.isRoot()) return "/";
+        String pathStr = stringFactory.create(path);
+        if (!pathStr.endsWith("]")) {
+            pathStr = pathStr + '[' + Path.DEFAULT_INDEX + ']';
+        }
+        return pathStr;
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,810 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import net.jcip.annotations.GuardedBy;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Subgraph;
-import org.jboss.dna.graph.SubgraphNode;
-import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-import org.jboss.dna.graph.request.InvalidWorkspaceException;
-
-/**
- * A component that acts as a search engine for the content within a single {@link RepositorySource}. This engine manages a set of
- * indexes and provides search functionality for each of the workspaces within the source, and provides various methods to
- * (re)index the content contained with source's workspaces and keep the indexes up-to-date via changes.
- */
- at ThreadSafe
-public class SearchEngine {
-
-    /**
-     * The default maximum number of changes that can be made to an index before the indexes are automatically optimized is * * *
-     * * {@value}
-     */
-    public static final int DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION = 0;
-
-    protected final ExecutionContext context;
-    private final String sourceName;
-    private final RepositoryConnectionFactory connectionFactory;
-    protected final IndexLayout indexLayout;
-    private final int maxChangesBeforeAutomaticOptimization;
-    @GuardedBy( "workspacesLock" )
-    private final Map<String, Workspace> workspacesByName = new HashMap<String, Workspace>();
-    private final ReadWriteLock workspacesLock = new ReentrantReadWriteLock();
-
-    /**
-     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
-     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
-     * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
-     * 
-     * @param context the execution context for indexing and optimization operations
-     * @param sourceName the name of the {@link RepositorySource}
-     * @param connectionFactory the connection factory
-     * @param indexLayout the specification of the Lucene index layout
-     * @param maxChangesBeforeAutomaticOptimization the number of changes that can be made to the index before the indexes are
-     *        automatically optimized; may be 0 or a negative number if no automatic optimization should be done
-     * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
-     */
-    public SearchEngine( ExecutionContext context,
-                         String sourceName,
-                         RepositoryConnectionFactory connectionFactory,
-                         IndexLayout indexLayout,
-                         int maxChangesBeforeAutomaticOptimization ) {
-        CheckArg.isNotNull(context, "context");
-        CheckArg.isNotNull(sourceName, "sourceName");
-        CheckArg.isNotNull(connectionFactory, "connectionFactory");
-        this.sourceName = sourceName;
-        this.connectionFactory = connectionFactory;
-        this.indexLayout = indexLayout;
-        this.context = context;
-        this.maxChangesBeforeAutomaticOptimization = maxChangesBeforeAutomaticOptimization < 0 ? 0 : maxChangesBeforeAutomaticOptimization;
-    }
-
-    /**
-     * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
-     * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
-     * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
-     * 
-     * @param context the execution context for indexing and optimization operations
-     * @param sourceName the name of the {@link RepositorySource}
-     * @param connectionFactory the connection factory
-     * @param indexLayout the specification of the Lucene index layout
-     * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
-     */
-    public SearchEngine( ExecutionContext context,
-                         String sourceName,
-                         RepositoryConnectionFactory connectionFactory,
-                         IndexLayout indexLayout ) {
-        this(context, sourceName, connectionFactory, indexLayout, DEFAULT_MAX_CHANGES_BEFORE_AUTOMATIC_OPTIMIZATION);
-    }
-
-    /**
-     * Get the name of the RepositorySource that this engine is to use.
-     * 
-     * @return the source name; never null
-     */
-    public String getSourceName() {
-        return sourceName;
-    }
-
-    /**
-     * Get the context in which all indexing operations execute.
-     * 
-     * @return the execution context; never null
-     */
-    public ExecutionContext getContext() {
-        return context;
-    }
-
-    /**
-     * @return maxChangesBeforeAutomaticOptimization
-     */
-    public int getMaxChangesBeforeAutomaticOptimization() {
-        return maxChangesBeforeAutomaticOptimization;
-    }
-
-    /**
-     * Utility to create a Graph for the source.
-     * 
-     * @return the graph instance; never null
-     */
-    final Graph graph() {
-        return Graph.create(sourceName, connectionFactory, context);
-    }
-
-    /**
-     * Utility to obtain the root path.
-     * 
-     * @return the root path; never null
-     */
-    final Path rootPath() {
-        return context.getValueFactories().getPathFactory().createRootPath();
-    }
-
-    final String readable( Path path ) {
-        return context.getValueFactories().getStringFactory().create(path);
-    }
-
-    /**
-     * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
-     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
-     * the workspace and source.
-     * <p>
-     * This method operates synchronously and returns when the requested indexing is completed.
-     * </p>
-     * 
-     * @param workspaceName the name of the workspace
-     * @param startingPoint the location that represents the content to be indexed; must have a path
-     * @param depthPerRead the depth of each subgraph read operation
-     * @throws IllegalArgumentException if the workspace name or location are null
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    public void index( String workspaceName,
-                       Location startingPoint,
-                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        CheckArg.isNotNull(startingPoint, "startingPoint");
-        assert startingPoint.hasPath();
-
-        Workspace workspace = getWorkspace(workspaceName);
-        if (startingPoint.getPath().isRoot()) {
-            // More efficient to just start over with a new index ...
-            workspace.execute(true, addContent(startingPoint, depthPerRead));
-        } else {
-            // Have to first remove the content below the starting point, then add it again ...
-            workspace.execute(false, removeContent(startingPoint), addContent(startingPoint, depthPerRead));
-        }
-    }
-
-    /**
-     * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
-     * If the starting point is the root node, then this method will drop the existing index(es) and rebuild from the content in
-     * the workspace and source.
-     * <p>
-     * This method operates synchronously and returns when the requested indexing is completed.
-     * </p>
-     * 
-     * @param workspaceName the name of the workspace
-     * @param startingPoint the path that represents the content to be indexed
-     * @param depthPerRead the depth of each subgraph read operation
-     * @throws IllegalArgumentException if the workspace name or path are null
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    public void index( String workspaceName,
-                       Path startingPoint,
-                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        CheckArg.isNotNull(startingPoint, "startingPoint");
-        index(workspaceName, Location.create(startingPoint), depthPerRead);
-    }
-
-    /**
-     * Index all of the content in the named workspace within the {@link #getSourceName() source}. This method operates
-     * synchronously and returns when the requested indexing is completed.
-     * 
-     * @param workspaceName the name of the workspace
-     * @param depthPerRead the depth of each subgraph read operation
-     * @throws IllegalArgumentException if the workspace name is null
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    public void index( String workspaceName,
-                       int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        index(workspaceName, rootPath(), depthPerRead);
-    }
-
-    /**
-     * Index (or re-index) all of the content in all of the workspaces within the source. This method operates synchronously and
-     * returns when the requested indexing is completed.
-     * 
-     * @param depthPerRead the depth of each subgraph read operation
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     */
-    public void index( int depthPerRead ) throws RepositorySourceException, SearchEngineException {
-        Path rootPath = rootPath();
-        for (String workspaceName : graph().getWorkspaces()) {
-            index(workspaceName, rootPath, depthPerRead);
-        }
-    }
-
-    /**
-     * Update the indexes with the supplied set of changes to the content.
-     * 
-     * @param changes the set of changes to the content
-     * @throws IllegalArgumentException if the path is null
-     * @throws RepositorySourceException if there is a problem accessing the content
-     * @throws SearchEngineException if there is a problem updating the indexes
-     */
-    public void index( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
-        // First break up all the changes into different collections, one collection per workspace ...
-        Map<String, Collection<ChangeRequest>> changesByWorkspace = new HashMap<String, Collection<ChangeRequest>>();
-        for (ChangeRequest request : changes) {
-            String workspaceName = request.changedWorkspace();
-            Collection<ChangeRequest> changesForWorkspace = changesByWorkspace.get(workspaceName);
-            if (changesForWorkspace == null) {
-                changesForWorkspace = new LinkedList<ChangeRequest>();
-                changesByWorkspace.put(workspaceName, changesForWorkspace);
-            }
-            changesForWorkspace.add(request);
-        }
-        // Now update the indexes for each workspace (serially). This minimizes the time that each workspace
-        // locks its indexes for writing.
-        for (Map.Entry<String, Collection<ChangeRequest>> entry : changesByWorkspace.entrySet()) {
-            String workspaceName = entry.getKey();
-            Collection<ChangeRequest> changesForWorkspace = entry.getValue();
-            getWorkspace(workspaceName).execute(false, updateContent(changesForWorkspace));
-        }
-    }
-
-    /**
-     * Invoke the engine's garbage collection on all indexes used by all workspaces in the source. This method reclaims space and
-     * optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
-     * 
-     * @throws SearchEngineException if there is a problem during optimization
-     */
-    public void optimize() throws SearchEngineException {
-        for (String workspaceName : graph().getWorkspaces()) {
-            getWorkspace(workspaceName).execute(false, optimizeContent());
-        }
-    }
-
-    /**
-     * Invoke the engine's garbage collection for the indexes associated with the specified workspace. This method reclaims space
-     * and optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
-     * 
-     * @param workspaceName the name of the workspace
-     * @throws IllegalArgumentException if the workspace name is null
-     * @throws SearchEngineException if there is a problem during optimization
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    public void optimize( String workspaceName ) throws SearchEngineException {
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        getWorkspace(workspaceName).execute(false, optimizeContent());
-    }
-
-    /**
-     * Perform a full-text search of the content in the named workspace, given the maximum number of results and the offset
-     * defining the first result the caller is interested in.
-     * 
-     * @param context the execution context in which the search is to take place; may not be null
-     * @param workspaceName the name of the workspace
-     * @param fullTextSearch the full-text search to be performed; may not be null
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @return the activity that will perform the work
-     * @throws IllegalArgumentException if the execution context or workspace name are null
-     * @throws SearchEngineException if there is a problem during optimization
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    public List<Location> fullTextSearch( ExecutionContext context,
-                                          String workspaceName,
-                                          String fullTextSearch,
-                                          int maxResults,
-                                          int offset ) {
-        CheckArg.isNotNull(context, "context");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        Search searchActivity = searchContent(context, fullTextSearch, maxResults, offset);
-        getWorkspace(workspaceName).execute(false, searchActivity);
-        return searchActivity.getResults();
-    }
-
-    /**
-     * Perform a query of the content in the named workspace, given the Abstract Query Model representation of the query.
-     * 
-     * @param context the execution context in which the search is to take place; may not be null
-     * @param workspaceName the name of the workspace
-     * @param query the query that is to be executed, in the form of the Abstract Query Model
-     * @param schemata the definition of the tables and views that can be used in the query; may not be null
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context, query, or schemata references are null
-     */
-    public QueryResults query( ExecutionContext context,
-                               String workspaceName,
-                               QueryCommand query,
-                               Schemata schemata ) {
-        CheckArg.isNotNull(context, "context");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        CheckArg.isNotNull(query, "query");
-        CheckArg.isNotNull(schemata, "schemata");
-        QueryContext queryContext = new QueryContext(context, schemata);
-        Query queryActivity = queryContent(queryContext, query);
-        getWorkspace(workspaceName).execute(false, queryActivity);
-        return queryActivity.getResults();
-    }
-
-    /**
-     * Remove the supplied index from the search engine. This is typically done when the workspace has been deleted from the
-     * source, or when
-     * 
-     * @param workspaceName the name of the workspace
-     * @throws IllegalArgumentException if the workspace name is null
-     * @throws SearchEngineException if there is a problem removing the workspace
-     */
-    public void removeWorkspace( String workspaceName ) throws SearchEngineException {
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        try {
-            workspacesLock.writeLock().lock();
-            // Check whether another thread got in and created the engine while we waited ...
-            Workspace workspace = workspacesByName.remove(workspaceName);
-            if (workspace != null) {
-                indexLayout.destroyIndexes(context, getSourceName(), workspaceName);
-            }
-        } catch (IOException e) {
-            String message = SearchI18n.errorWhileRemovingIndexesForWorkspace.text(sourceName, workspaceName, e.getMessage());
-            throw new SearchEngineException(message, e);
-        } finally {
-            workspacesLock.writeLock().unlock();
-        }
-    }
-
-    /**
-     * Remove from the search engine all workspace-related indexes, thereby cleaning up any resources used by this search engine.
-     * 
-     * @throws SearchEngineException if there is a problem removing any of the workspace
-     */
-    public void removeWorkspaces() throws SearchEngineException {
-        try {
-            workspacesLock.writeLock().lock();
-            for (String workspaceName : new HashSet<String>(workspacesByName.keySet())) {
-                removeWorkspace(workspaceName);
-            }
-        } finally {
-            workspacesLock.writeLock().unlock();
-        }
-    }
-
-    /**
-     * Get the search engine for the workspace with the supplied name.
-     * 
-     * @param workspaceName the name of the workspace
-     * @return the workspace's search engine
-     * @throws InvalidWorkspaceException if the workspace does not exist
-     */
-    protected Workspace getWorkspace( String workspaceName ) {
-        Workspace workspace = null;
-        try {
-            workspacesLock.readLock().lock();
-            workspace = workspacesByName.get(workspaceName);
-        } finally {
-            workspacesLock.readLock().unlock();
-        }
-
-        if (workspace == null) {
-            // Verify the workspace does exist ...
-            if (!graph().getWorkspaces().contains(workspaceName)) {
-                String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, getSourceName());
-                throw new InvalidWorkspaceException(msg);
-            }
-            try {
-                workspacesLock.writeLock().lock();
-                // Check whether another thread got in and created the engine while we waited ...
-                workspace = workspacesByName.get(workspaceName);
-                if (workspace == null) {
-                    // Create the engine and register it ...
-                    workspace = new Workspace(workspaceName);
-                    workspacesByName.put(workspaceName, workspace);
-                }
-            } finally {
-                workspacesLock.writeLock().unlock();
-            }
-        }
-        return workspace;
-    }
-
-    protected class Workspace {
-        private final String sourceName;
-        private final String workspaceName;
-        protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
-
-        protected Workspace( String workspaceName ) {
-            this.workspaceName = workspaceName;
-            this.sourceName = getSourceName();
-        }
-
-        /**
-         * Get the workspace name.
-         * 
-         * @return the workspace name; never null
-         */
-        public String getWorkspaceName() {
-            return workspaceName;
-        }
-
-        /**
-         * Execute the supplied activities against the indexes.
-         * 
-         * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
-         * @param activities the activities to execute
-         * @throws SearchEngineException if there is a problem performing the activities
-         */
-        protected final void execute( boolean overwrite,
-                                      Activity... activities ) throws SearchEngineException {
-            // Determine if the activities are readonly ...
-            boolean readOnly = true;
-            for (Activity activity : activities) {
-                if (!(activity instanceof ReadOnlyActivity)) {
-                    readOnly = false;
-                    break;
-                }
-            }
-
-            // Create a session ...
-            IndexSession session = indexLayout.createSession(context, sourceName, workspaceName, overwrite, readOnly);
-            assert session != null;
-
-            // Execute the various activities ...
-            Throwable error = null;
-            try {
-                int numChanges = 0;
-                for (Activity activity : activities) {
-                    try {
-                        numChanges += activity.execute(session);
-                    } catch (IOException e) {
-                        error = e;
-                        throw new SearchEngineException(activity.messageFor(e, sourceName, workspaceName), e);
-                    } catch (ParseException e) {
-                        error = e;
-                        throw new SearchEngineException(activity.messageFor(e, sourceName, workspaceName), e);
-                    } catch (RuntimeException e) {
-                        error = e;
-                        throw e;
-                    }
-                }
-                if (numChanges > 0) {
-                    numChanges = this.modifiedNodesSinceLastOptimize.addAndGet(numChanges);
-                    // Determine if there have been enough changes made to run the optimizer ...
-                    int maxChanges = getMaxChangesBeforeAutomaticOptimization();
-                    if (maxChanges > 0 && numChanges >= maxChanges) {
-                        Activity optimizer = optimizeContent();
-                        try {
-                            optimizer.execute(session);
-                        } catch (ParseException e) {
-                            error = e;
-                            throw new SearchEngineException(optimizer.messageFor(e, sourceName, workspaceName), e);
-                        } catch (IOException e) {
-                            error = e;
-                            throw new SearchEngineException(optimizer.messageFor(e, sourceName, workspaceName), e);
-                        } catch (RuntimeException e) {
-                            error = e;
-                            throw e;
-                        }
-                    }
-                }
-            } finally {
-                try {
-                    if (error == null) {
-                        session.commit();
-                    } else {
-                        session.rollback();
-                    }
-                } catch (IOException e2) {
-                    // We don't want to lose the existing error, if there is one ...
-                    if (error == null) {
-                        I18n msg = SearchI18n.errorWhileCommittingIndexChanges;
-                        throw new SearchEngineException(msg.text(workspaceName, sourceName, e2.getMessage()), e2);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Create an activity that will optimize the indexes.
-     * 
-     * @return the activity that will perform the work
-     */
-    protected Activity optimizeContent() {
-        return new Activity() {
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
-             */
-            public int execute( IndexSession indexSession ) throws IOException {
-                indexSession.optimize();
-                return 0; // no lines changed
-            }
-
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                return SearchI18n.errorWhileOptimizingIndexes.text(sourceName, workspaceName, error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will read from the source the content at the supplied location and add the content to the search
-     * index.
-     * 
-     * @param location the location of the content to read; may not be null
-     * @param depthPerRead the depth of each read operation; always positive
-     * @return the activity that will perform the work
-     */
-    protected Activity addContent( final Location location,
-                                   final int depthPerRead ) {
-        return new Activity() {
-            public int execute( IndexSession indexSession ) throws IOException {
-
-                // Create a queue that we'll use to walk the content ...
-                LinkedList<Location> locationsToRead = new LinkedList<Location>();
-                locationsToRead.add(location);
-                int count = 0;
-
-                // Now read and index the content ...
-                Graph graph = graph();
-                graph.useWorkspace(indexSession.getWorkspaceName());
-                while (!locationsToRead.isEmpty()) {
-                    Location location = locationsToRead.poll();
-                    if (location == null) continue;
-                    Subgraph subgraph = graph.getSubgraphOfDepth(depthPerRead).at(location);
-                    // Index all of the nodes within this subgraph ...
-                    for (SubgraphNode node : subgraph) {
-                        // Index the node ...
-                        indexSession.index(node);
-                        ++count;
-
-                        // Process the children ...
-                        for (Location child : node.getChildren()) {
-                            if (!subgraph.includes(child)) {
-                                // Record this location as needing to be read ...
-                                locationsToRead.add(child);
-                            }
-                        }
-                    }
-                }
-                return count;
-            }
-
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                String path = readable(location.getPath());
-                return SearchI18n.errorWhileIndexingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will remove from the indexes all documents that represent content at or below the specified
-     * location.
-     * 
-     * @param location the location of the content to removed; may not be null
-     * @return the activity that will perform the work
-     */
-    protected Activity removeContent( final Location location ) {
-        return new Activity() {
-
-            public int execute( IndexSession indexSession ) throws IOException {
-                // Delete the content at/below the path ...
-                return indexSession.deleteBelow(location.getPath());
-            }
-
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                String path = readable(location.getPath());
-                return SearchI18n.errorWhileRemovingContentAtPath.text(path, workspaceName, sourceName, error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will update the indexes with changes that were already made to the content.
-     * 
-     * @param changes the changes that have been made to the content; may not be null
-     * @return the activity that will perform the work
-     */
-    protected Activity updateContent( final Iterable<ChangeRequest> changes ) {
-        return new Activity() {
-
-            public int execute( IndexSession indexSession ) throws IOException {
-                return indexSession.apply(changes);
-            }
-
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                return SearchI18n.errorWhileUpdatingContent.text(workspaceName, sourceName, error.getMessage());
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will perform a full-text search given the supplied query.
-     * 
-     * @param context the context in which the search is to be performed; may not be null
-     * @param fullTextSearch the full-text search to be performed; may not be null
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @return the activity that will perform the work; never null
-     */
-    protected Search searchContent( final ExecutionContext context,
-                                    final String fullTextSearch,
-                                    final int maxResults,
-                                    final int offset ) {
-        final List<Location> results = new ArrayList<Location>(maxResults);
-        return new Search() {
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
-             */
-            public int execute( IndexSession session ) throws IOException, ParseException {
-                session.search(context, fullTextSearch, maxResults, offset, results);
-                return 0;
-            }
-
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
-             *      java.lang.String)
-             */
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                return SearchI18n.errorWhilePerformingSearch.text(fullTextSearch, workspaceName, sourceName, error.getMessage());
-            }
-
-            public List<Location> getResults() {
-                return results;
-            }
-        };
-    }
-
-    /**
-     * Create an activity that will perform a query against the index.
-     * 
-     * @param context the context in which the search is to be performed; may not be null
-     * @param query the query to be performed; may not be null
-     * @return the activity that will perform the query; never null
-     */
-    protected Query queryContent( final QueryContext context,
-                                  final QueryCommand query ) {
-        return new Query() {
-            private QueryResults results = null;
-
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.search.SearchEngine.Activity#execute(org.jboss.dna.search.IndexSession)
-             */
-            public int execute( IndexSession session ) throws IOException, ParseException {
-                results = session.query(context, query);
-                return 0;
-            }
-
-            /**
-             * {@inheritDoc}
-             * 
-             * @see org.jboss.dna.search.SearchEngine.Activity#messageFor(java.lang.Throwable, java.lang.String,
-             *      java.lang.String)
-             */
-            public String messageFor( Throwable error,
-                                      String sourceName,
-                                      String workspaceName ) {
-                return SearchI18n.errorWhilePerformingQuery.text(query, workspaceName, sourceName, error.getMessage());
-            }
-
-            public QueryResults getResults() {
-                return results;
-            }
-        };
-    }
-
-    /**
-     * Interface for activities that will be executed against a workspace. These activities don't have to commit or roll back the
-     * writer, nor do they have to translate the exceptions, since this is done by the
-     * {@link Workspace#execute(boolean, Activity...)} method.
-     */
-    protected interface Activity {
-
-        /**
-         * Perform the activity by using the index writer.
-         * 
-         * @param indexSession the index session that should be used by the activity; never null
-         * @return the number of changes that were made by this activity
-         * @throws IOException if there is an error using the writer
-         * @throws ParseException if there is an error due to parsing
-         */
-        int execute( IndexSession indexSession ) throws IOException, ParseException;
-
-        /**
-         * Translate an exception obtained during {@link #execute(IndexSession) execution} into a single message.
-         * 
-         * @param t the exception
-         * @param sourceName the name of the source
-         * @param workspaceName the name of the workspace
-         * @return the error message
-         */
-        String messageFor( Throwable t,
-                           String sourceName,
-                           String workspaceName );
-    }
-
-    /**
-     * A read-only activity.
-     */
-    protected interface ReadOnlyActivity extends Activity {
-    }
-
-    /**
-     * A search activity.
-     */
-    protected interface Search extends ReadOnlyActivity {
-        /**
-         * Get the results of the search.
-         * 
-         * @return the list of {@link Location} objects for each node satisfying the results; never null
-         */
-        List<Location> getResults();
-    }
-
-    /**
-     * A query activity.
-     */
-    protected interface Query extends ReadOnlyActivity {
-        /**
-         * Get the results of the query.
-         * 
-         * @return the results of a query; never null
-         */
-        QueryResults getResults();
-    }
-
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,67 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-/**
- * An exception that represents a problem within a search engine.
- */
-public class SearchEngineException extends RuntimeException {
-
-    /**
-     */
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 
-     */
-    public SearchEngineException() {
-    }
-
-    /**
-     * @param message
-     */
-    public SearchEngineException( String message ) {
-        super(message);
-
-    }
-
-    /**
-     * @param cause
-     */
-    public SearchEngineException( Throwable cause ) {
-        super(cause);
-
-    }
-
-    /**
-     * @param message
-     * @param cause
-     */
-    public SearchEngineException( String message,
-                                  Throwable cause ) {
-        super(message, cause);
-
-    }
-
-}

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -28,25 +28,15 @@
 import org.jboss.dna.common.CommonI18n;
 import org.jboss.dna.common.i18n.I18n;
 
-/**
- * 
- */
 public class SearchI18n {
 
     public static I18n locationForIndexesIsNotDirectory;
     public static I18n locationForIndexesCannotBeRead;
     public static I18n locationForIndexesCannotBeWritten;
-    public static I18n errorWhileIndexingContentAtPath;
-    public static I18n errorWhileRemovingContentAtPath;
-    public static I18n errorWhileUpdatingContent;
     public static I18n errorWhileCommittingIndexChanges;
     public static I18n errorWhileRollingBackIndexChanges;
-    public static I18n errorCreatingIndexWriter;
-    public static I18n errorWhileOptimizingIndexes;
     public static I18n errorWhilePerformingSearch;
     public static I18n errorWhilePerformingQuery;
-    public static I18n errorWhileInitializingSearchEngine;
-    public static I18n errorWhileRemovingIndexesForWorkspace;
     public static I18n errorWhilePerformingLuceneQuery;
 
     static {

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/filters/ResultFilter.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,36 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search.filters;
-
-import org.jboss.dna.graph.query.QueryResults.Columns;
-
-/**
- * 
- */
-public interface ResultFilter {
-
-    boolean allows( Object[] tuple,
-                    Columns columns );
-
-}

Modified: trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
===================================================================
--- trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties	2009-11-18 19:39:05 UTC (rev 1329)
@@ -26,15 +26,8 @@
 locationForIndexesCannotBeRead = Location "{0}" cannot be used for search indexes for workspace "{1}" because it cannot be read
 locationForIndexesCannotBeWritten = Location "{0}" cannot be used for search indexes for workspace "{1}" because its contents cannot be written or updated
 
-errorWhileIndexingContentAtPath = Error while indexing the content at "{0}" in the "{1}" workspace of the "{2}" source: {3}
-errorWhileRemovingContentAtPath = Error while removing the content at/below "{0}" in the "{1}" workspace of the "{2}" source: {3}
-errorWhileUpdatingContent = Error while updating content in the "{0}" workspace of the "{1}" source: {2}
 errorWhileCommittingIndexChanges = Error while committing changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
 errorWhileRollingBackIndexChanges = Error while rolling back changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
-errorCreatingIndexWriter = Error attempting to create an index writer for the "{0}" index for the "{1}" workspace of the "{2}" source: {3}
-errorWhileOptimizingIndexes = Error while optimizing the indexes for the "{0}" workspace of the "{1}" source: {2}
-errorWhilePerformingSearch = Error while searching for "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhilePerformingSearch = Error while searching the "{0}" workspace in the "{1}" source for "{2}": {3}
 errorWhilePerformingQuery = Error while performing the query "{0}" against the content in the "{1}" workspace of the "{2}" source: {3}
-errorWhileInitializingSearchEngine = Error while initializing the search engine for the "{0}" workspace of the "{1}" source: {2}
-errorWhileRemovingIndexesForWorkspace = Error while removing the indexes for the "{0}" workspace of the "{1}" source: {2}
 errorWhilePerformingLuceneQuery = Error while performing the Lucene query "{0}" as part of the "{1}" query against the "{2}" workspace of the "{3}" source: {4}
\ No newline at end of file

Deleted: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-18 19:37:57 UTC (rev 1328)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-18 19:39:05 UTC (rev 1329)
@@ -1,353 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.stub;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import java.util.List;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.model.Query;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.InvalidWorkspaceException;
-import org.jboss.dna.search.IndexLayout;
-import org.jboss.dna.search.IndexSession;
-import org.jboss.dna.search.SearchEngine;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SearchEngineTest {
-
-    private SearchEngine engine;
-    private ExecutionContext context;
-    private String sourceName;
-    private String workspaceName1;
-    private String workspaceName2;
-    private InMemoryRepositorySource source;
-    private RepositoryConnectionFactory connectionFactory;
-    private IndexLayout layout;
-    private IndexSession sessionWs1;
-    private IndexSession sessionWs2;
-    private IndexSession sessionDefault;
-    private Graph content;
-
-    @Before
-    public void beforeEach() throws Exception {
-        context = new ExecutionContext();
-        sourceName = "sourceA";
-        workspaceName1 = "workspace1";
-        workspaceName2 = "workspace2";
-
-        // Set up the source and graph instance ...
-        source = new InMemoryRepositorySource();
-        source.setName(sourceName);
-        content = Graph.create(source, context);
-
-        // Create the workspaces ...
-        content.createWorkspace().named(workspaceName1);
-        content.createWorkspace().named(workspaceName2);
-
-        // Set up the connection factory ...
-        connectionFactory = new RepositoryConnectionFactory() {
-            @SuppressWarnings( "synthetic-access" )
-            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
-                return source.getConnection();
-            }
-        };
-
-        // Set up the index layout ...
-        layout = mock(IndexLayout.class);
-        sessionWs1 = mockSession(layout, workspaceName1);
-        sessionWs2 = mockSession(layout, workspaceName2);
-        sessionDefault = mockSession(layout, "");
-
-        // Now set up the search engine ...
-        engine = new SearchEngine(context, sourceName, connectionFactory, layout);
-    }
-
-    protected IndexSession mockSession( IndexLayout mockLayout,
-                                        String workspaceName ) {
-        IndexSession session = mock(IndexSession.class);
-        stub(layout.createSession(context, sourceName, workspaceName, false, false)).toReturn(session);
-        stub(layout.createSession(context, sourceName, workspaceName, false, true)).toReturn(session);
-        stub(layout.createSession(context, sourceName, workspaceName, true, false)).toReturn(session);
-        stub(layout.createSession(context, sourceName, workspaceName, true, true)).toReturn(session);
-        stub(session.getWorkspaceName()).toReturn(workspaceName);
-        stub(session.getSourceName()).toReturn(sourceName);
-        return session;
-    }
-
-    protected Path path( String path ) {
-        return context.getValueFactories().getPathFactory().create(path);
-    }
-
-    protected void loadContent() throws Exception {
-        // Load some content ...
-        content.useWorkspace(workspaceName1);
-        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
-        content.useWorkspace(workspaceName2);
-        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
-    }
-
-    @Test
-    public void shouldReturnSearchWorkspaceForExistingWorkspaceInSource() {
-        SearchEngine.Workspace workspace = engine.getWorkspace(workspaceName1);
-        assertThat(workspace, is(notNullValue()));
-        assertThat(workspace.modifiedNodesSinceLastOptimize.get(), is(0));
-        assertThat(workspace.getWorkspaceName(), is(workspaceName1));
-    }
-
-    @Test( expected = InvalidWorkspaceException.class )
-    public void shouldFailToReturnSearchWorkspaceForNonExistantWorkspaceInSource() {
-        engine.getWorkspace(workspaceName1 + "foobar");
-    }
-
-    @Test
-    public void shouldDoNothingDuringRemoveWorkspaceIfWorkspaceHasNotBeenLoaded() throws Exception {
-        engine.removeWorkspace(workspaceName1);
-        verifyZeroInteractions(layout);
-    }
-
-    @Test
-    public void shouldForwardRemoveWorkspaceToIndexLayout() throws Exception {
-        engine.getWorkspace(workspaceName1);
-        engine.removeWorkspace(workspaceName1);
-        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
-        verifyNoMoreInteractions(layout);
-    }
-
-    @Test
-    public void shouldForwardRemoveWorkspaceToIndexLayoutForEachWorkspaceThatWasLoaded() throws Exception {
-        engine.getWorkspace(workspaceName1);
-        engine.removeWorkspaces();
-        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
-        verifyZeroInteractions(layout);
-    }
-
-    @Test
-    public void shouldForwardRemoveWorkspaceToIndexLayoutForAllWorkspacesThatWereLoaded() throws Exception {
-        engine.getWorkspace(workspaceName1);
-        engine.getWorkspace(workspaceName2);
-        engine.removeWorkspaces();
-        verify(layout).destroyIndexes(context, sourceName, workspaceName1);
-        verify(layout).destroyIndexes(context, sourceName, workspaceName2);
-        verifyNoMoreInteractions(layout);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldFailIfNullWorkspaceNamePassedToRemoveWorkspace() throws Exception {
-        engine.removeWorkspace(null);
-    }
-
-    @Test
-    public void shouldForwardOptimizeOfWorkspaceToIndexSession() throws Exception {
-        engine.optimize(workspaceName1);
-        verify(sessionWs1).optimize();
-        verify(sessionWs1).commit();
-        verifyNoMoreInteractions(sessionWs1);
-    }
-
-    @Test
-    public void shouldForwardOptimizeOfAllWorkspacesToEachIndexSession() throws Exception {
-        engine.optimize(); // will find all three workspaces
-        verify(sessionWs1).optimize();
-        verify(sessionWs1).commit();
-        verifyNoMoreInteractions(sessionWs1);
-        verify(sessionWs2).optimize();
-        verify(sessionWs2).commit();
-        verifyNoMoreInteractions(sessionWs2);
-        verify(sessionDefault).optimize();
-        verify(sessionDefault).commit();
-        verifyNoMoreInteractions(sessionDefault);
-    }
-
-    @Test
-    public void shouldForwardIndexOfWorkspaceToIndexSession() throws Exception {
-        loadContent();
-        engine.index(workspaceName1, 3);
-        verify(sessionWs1, times(18)).index((Node)anyObject());
-        verify(sessionWs1).commit();
-    }
-
-    @Test
-    public void shouldForwardIndexOfSubgraphInWorkspaceToIndexSession() throws Exception {
-        loadContent();
-        engine.index(workspaceName1, path("/Cars"), 3);
-        verify(sessionWs1).deleteBelow(path("/Cars"));
-        verify(sessionWs1, times(17)).index((Node)anyObject());
-        verify(sessionWs1).commit();
-    }
-
-    @Test
-    public void shouldForwardIndexEntireWorkspaceToIndexSession() throws Exception {
-        loadContent();
-        engine.index(workspaceName1, path("/"), 3);
-        verify(sessionWs1, times(18)).index((Node)anyObject());
-        verify(sessionWs1).commit();
-    }
-
-    @Test
-    public void shouldForwardIndexOfAllWorkspacesToEachIndexSession() throws Exception {
-        loadContent();
-        engine.index(3); // will find all three workspaces
-        verify(sessionWs1, times(18)).index((Node)anyObject());
-        verify(sessionWs1).commit();
-        verify(sessionWs2, times(24)).index((Node)anyObject());
-        verify(sessionWs2).commit();
-        verify(sessionDefault, times(1)).index((Node)anyObject());
-        verify(sessionDefault).commit();
-    }
-
-    @SuppressWarnings( "unchecked" )
-    @Test
-    public void shouldForwardSearchToIndexSession() throws Exception {
-        String query = "term1 term2";
-        engine.fullTextSearch(context, workspaceName1, query, 3, 0);
-        verify(sessionWs1).search(eq(context), eq(query), eq(3), eq(0), (List<Location>)anyObject());
-        verify(sessionWs1).commit();
-    }
-
-    @Test
-    public void shouldForwardQueryToIndexSession() throws Exception {
-        Query query = mock(Query.class);
-        Schemata schemata = mock(Schemata.class);
-        engine.query(context, workspaceName1, query, schemata);
-        verify(sessionWs1).query(eq(new QueryContext(context, schemata)), eq(query));
-        verify(sessionWs1).commit();
-    }
-
-    // These tests expect there to be some real IndexLayout ...
-
-    // @Test
-    // public void shouldIndexAllContentInRepositorySource() throws Exception {
-    // loadContent();
-    // engine.index(3);
-    // }
-    //
-    // @Test
-    // public void shouldIndexAllContentInWorkspace() throws Exception {
-    // loadContent();
-    // engine.index(workspaceName1, 3);
-    // engine.index(workspaceName2, 5);
-    // }
-    //
-    // @Test
-    // public void shouldIndexAllContentInWorkspaceBelowPath() throws Exception {
-    // loadContent();
-    // engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
-    // engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
-    // }
-    //
-    // @Test
-    // public void shouldReIndexAllContentInWorkspaceBelowPath() throws Exception {
-    // loadContent();
-    // for (int i = 0; i != 0; i++) {
-    // engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
-    // engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
-    // }
-    // }
-    //
-    // @Test
-    // public void shouldHaveLoadedTestContentIntoRepositorySource() {
-    // assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfOne() {
-    // engine.index(workspaceName1, path("/"), 1);
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTwo() {
-    // engine.index(workspaceName1, path("/"), 2);
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfThree() {
-    // engine.index(workspaceName1, path("/"), 3);
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfFour() {
-    // engine.index(workspaceName1, path("/"), 4);
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTen() {
-    // engine.index(workspaceName1, path("/"), 10);
-    // }
-    //
-    // @Test
-    // public void shouldIndexRepositoryContentStartingAtNonRootNode() {
-    // engine.index(workspaceName1, path("/Cars"), 10);
-    // }
-    //
-    // @Test
-    // public void shouldReIndexRepositoryContentStartingAtNonRootNode() {
-    // for (int i = 0; i != 3; ++i) {
-    // engine.index(workspaceName1, path("/Cars"), 10);
-    // }
-    // }
-    //
-    // @Test
-    // public void shouldFindNodesByFullTextSearch() {
-    // engine.index(workspaceName1, path("/"), 100);
-    // List<Location> results = engine.fullTextSearch(context, workspaceName1, "Toyota Prius", 10, 0);
-    // assertThat(results, is(notNullValue()));
-    // assertThat(results.size(), is(2));
-    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
-    // assertThat(results.get(1).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
-    // }
-    //
-    // @Test
-    // public void shouldFindNodesByFullTextSearchWithOffset() {
-    // engine.index(workspaceName1, path("/"), 100);
-    // List<Location> results = engine.fullTextSearch(context, workspaceName1, "toyota prius", 1, 0);
-    // assertThat(results, is(notNullValue()));
-    // assertThat(results.size(), is(1));
-    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
-    //
-    // results = engine.fullTextSearch(context, workspaceName1, "+Toyota", 1, 1);
-    // assertThat(results, is(notNullValue()));
-    // assertThat(results.size(), is(1));
-    // assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
-    // }
-}


From dna-commits at lists.jboss.org  Wed Nov 18 14:42:05 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Nov 2009 14:42:05 -0500
Subject: [dna-commits] DNA SVN: r1330 -
	trunk/dna-search/src/test/java/org/jboss/dna/search/query.
Message-ID: <200911181942.nAIJg5m6016964@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-18 14:42:04 -0500 (Wed, 18 Nov 2009)
New Revision: 1330

Added:
   trunk/dna-search/src/test/java/org/jboss/dna/search/query/NotQueryTest.java
Removed:
   trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java
Log:
DNA-467 Renamed unit test to reflect the name of the class it's testing.

Deleted: trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java	2009-11-18 19:39:05 UTC (rev 1329)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java	2009-11-18 19:42:04 UTC (rev 1330)
@@ -1,126 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search.query;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.stub;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Similarity;
-import org.jboss.dna.search.query.NotQuery;
-import org.junit.Test;
-
-public class LuceneNotQueryTest {
-
-    @Test
-    public void scorerShouldSkipAdjacentDocsIfScoredByOperandScorer() throws IOException {
-        IndexReader reader = mock(IndexReader.class);
-        stub(reader.isDeleted(anyInt())).toReturn(false);
-        stub(reader.maxDoc()).toReturn(10);
-        Scorer operandScorer = new MockScorer(0, 1, 2, 3, 4);
-        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
-        assertScores(notScorer, 5, 6, 7, 8, 9);
-    }
-
-    @Test
-    public void scorerShouldSkipDocsAtEndIfScoredByOperandScorer() throws IOException {
-        IndexReader reader = mock(IndexReader.class);
-        stub(reader.isDeleted(anyInt())).toReturn(false);
-        stub(reader.maxDoc()).toReturn(10);
-        Scorer operandScorer = new MockScorer(8, 9);
-        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
-        assertScores(notScorer, 0, 1, 2, 3, 4, 5, 6, 7);
-    }
-
-    @Test
-    public void scorerShouldScoreFirstDocsIfNotScoredByOperandScorer() throws IOException {
-        IndexReader reader = mock(IndexReader.class);
-        stub(reader.isDeleted(anyInt())).toReturn(false);
-        stub(reader.maxDoc()).toReturn(10);
-        Scorer operandScorer = new MockScorer(2, 3, 4);
-        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
-        assertScores(notScorer, 0, 1, 5, 6, 7, 8, 9);
-    }
-
-    @Test
-    public void scorerShouldScoreNonAdjacentDocsNotScoredByOperandScorer() throws IOException {
-        IndexReader reader = mock(IndexReader.class);
-        stub(reader.isDeleted(anyInt())).toReturn(false);
-        stub(reader.maxDoc()).toReturn(10);
-        Scorer operandScorer = new MockScorer(2, 4, 8);
-        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
-        assertScores(notScorer, 0, 1, 3, 5, 6, 7, 9);
-    }
-
-    protected void assertScores( Scorer scorer,
-                                 int... docIds ) throws IOException {
-        for (int docId : docIds) {
-            assertThat(scorer.nextDoc(), is(docId));
-            assertThat(scorer.score(), is(1.0f));
-        }
-        assertThat(scorer.nextDoc(), is(Scorer.NO_MORE_DOCS));
-    }
-
-    protected static class MockScorer extends Scorer {
-        private final Iterator<Integer> docIds;
-
-        protected MockScorer( int... docIds ) {
-            super(Similarity.getDefault());
-            List<Integer> ids = new ArrayList<Integer>();
-            for (int docId : docIds) {
-                ids.add(new Integer(docId));
-            }
-            this.docIds = ids.iterator();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
-         */
-        @Override
-        public int nextDoc() {
-            if (docIds.hasNext()) return docIds.next();
-            return Scorer.NO_MORE_DOCS;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Scorer#score()
-         */
-        @Override
-        public float score() {
-            throw new UnsupportedOperationException("Should not be called");
-        }
-    }
-
-}

Copied: trunk/dna-search/src/test/java/org/jboss/dna/search/query/NotQueryTest.java (from rev 1329, trunk/dna-search/src/test/java/org/jboss/dna/search/query/LuceneNotQueryTest.java)
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/query/NotQueryTest.java	                        (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/query/NotQueryTest.java	2009-11-18 19:42:04 UTC (rev 1330)
@@ -0,0 +1,126 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Similarity;
+import org.jboss.dna.search.query.NotQuery;
+import org.junit.Test;
+
+public class NotQueryTest {
+
+    @Test
+    public void scorerShouldSkipAdjacentDocsIfScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(0, 1, 2, 3, 4);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 5, 6, 7, 8, 9);
+    }
+
+    @Test
+    public void scorerShouldSkipDocsAtEndIfScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(8, 9);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 2, 3, 4, 5, 6, 7);
+    }
+
+    @Test
+    public void scorerShouldScoreFirstDocsIfNotScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(2, 3, 4);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 5, 6, 7, 8, 9);
+    }
+
+    @Test
+    public void scorerShouldScoreNonAdjacentDocsNotScoredByOperandScorer() throws IOException {
+        IndexReader reader = mock(IndexReader.class);
+        stub(reader.isDeleted(anyInt())).toReturn(false);
+        stub(reader.maxDoc()).toReturn(10);
+        Scorer operandScorer = new MockScorer(2, 4, 8);
+        Scorer notScorer = new NotQuery.NotScorer(operandScorer, reader);
+        assertScores(notScorer, 0, 1, 3, 5, 6, 7, 9);
+    }
+
+    protected void assertScores( Scorer scorer,
+                                 int... docIds ) throws IOException {
+        for (int docId : docIds) {
+            assertThat(scorer.nextDoc(), is(docId));
+            assertThat(scorer.score(), is(1.0f));
+        }
+        assertThat(scorer.nextDoc(), is(Scorer.NO_MORE_DOCS));
+    }
+
+    protected static class MockScorer extends Scorer {
+        private final Iterator<Integer> docIds;
+
+        protected MockScorer( int... docIds ) {
+            super(Similarity.getDefault());
+            List<Integer> ids = new ArrayList<Integer>();
+            for (int docId : docIds) {
+                ids.add(new Integer(docId));
+            }
+            this.docIds = ids.iterator();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() {
+            if (docIds.hasNext()) return docIds.next();
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            throw new UnsupportedOperationException("Should not be called");
+        }
+    }
+
+}


Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/query/NotQueryTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Thu Nov 19 18:07:26 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 19 Nov 2009 18:07:26 -0500
Subject: [dna-commits] DNA SVN: r1331 - in
	branches/ddl_sequencer/dna-common/src:
	main/java/org/jboss/dna/common/text and 2 other directories.
Message-ID: <200911192307.nAJN7QAK025368@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-19 18:07:26 -0500 (Thu, 19 Nov 2009)
New Revision: 1331

Added:
   branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
Modified:
   branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
   branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
   branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
   branches/ddl_sequencer/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
   branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
Log:
Added starting Index "in content" to Position object. Changed integer  Token Stream constants to bit-mask values.

Modified: branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java	2009-11-18 19:42:04 UTC (rev 1330)
+++ branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java	2009-11-19 23:07:26 UTC (rev 1331)
@@ -104,6 +104,7 @@
     public static I18n expectingValidIntegerAtLineAndColumn;
     public static I18n expectingValidLongAtLineAndColumn;
     public static I18n expectingValidBooleanAtLineAndColumn;
+    public static I18n endPositionMustBeGreaterThanStartingPosition;
 
     static {
         try {

Modified: branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/Position.java	2009-11-18 19:42:04 UTC (rev 1330)
+++ branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/Position.java	2009-11-19 23:07:26 UTC (rev 1331)
@@ -7,14 +7,37 @@
  */
 @Immutable
 public final class Position {
+	/**
+	 * The position is used when there is no content.
+	 */
+	public final static Position EMPTY_CONTENT_POSITION = new Position(-1, 1, 0);
+	
     private final int line;
     private final int column;
+    private final int indexInContent;
 
-    public Position( int line,
+    public Position( int indexInContent, 
+    				 int line,
                      int column ) {
+    	this.indexInContent = indexInContent < 0 ? -1:indexInContent;
         this.line = line;
         this.column = column;
+        
+        assert this.indexInContent >= -1;
+        assert this.line > 0;
+        assert this.column >= 0;
+        // make sure that negative index means an EMPTY_CONTENT_POSITION
+        assert this.indexInContent < 0 ? this.line == 1 && this.column == 0: true;
     }
+    
+    /**
+     * Get the 0-based index of this position in the content character array.
+     * 
+     * @return the index; never negative except for the first position in an empty content.
+     */
+    public int getIndexInContent() {
+        return indexInContent;
+    }
 
     /**
      * Get the 1-based column number of the character.
@@ -41,7 +64,7 @@
      */
     @Override
     public int hashCode() {
-        return line;
+        return indexInContent;
     }
 
     /**
@@ -51,6 +74,28 @@
      */
     @Override
     public String toString() {
-        return "" + line + ':' + column;
+        return "" + indexInContent + ':' + line + ':' + column;
     }
+    
+    /**
+     * Return a new position that is the addition of this position and that supplied.
+     * 
+     * @param position the position to add to this object; may not be null
+     * @return the combined position
+     */
+    public Position add(Position position) {
+    	if( this.getIndexInContent() < 0 ) {
+    		return position.getIndexInContent() < 0 ? EMPTY_CONTENT_POSITION:position;
+    	}
+    	
+    	if( position.getIndexInContent() < 0 ) {
+    		return this;
+    	}
+
+        int index = this.getIndexInContent() + position.getIndexInContent();
+        int line = position.getLine() + this.getLine() - 1;
+        int column = this.getLine() == 1 ? this.getColumn() + position.getColumn() : this.getColumn();
+        
+        return new Position(index, line, column);
+    }
 }

Modified: branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-18 19:42:04 UTC (rev 1330)
+++ branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-19 23:07:26 UTC (rev 1331)
@@ -384,6 +384,20 @@
     private final boolean caseSensitive;
     private final Tokenizer tokenizer;
     private List<Token> tokens;
+    /**
+     * This class navigates the Token objects using this iterator. However, because it very often needs to access the
+     * "current token" in the "consume(...)" and "canConsume(...)" and "matches(...)" methods, the class caches a "current token"
+     * and makes this iterator point to the 2nd token.
+     * 
+     * <pre>
+     *     T1     T2    T3    T4    T5
+     *         &circ;   &circ;  &circ;
+     *         |   |  |
+     *         |   |  +- The position of the tokenIterator, where tokenIterator.hasNext() will return T3
+     *         |   +---- The token referenced by currentToken
+     *         +-------- The logical position of the TokenStream object, where the "consume()" would return T2
+     * </pre>
+     */
     private ListIterator<Token> tokenIterator;
     private Token currentToken;
     private boolean completed;
@@ -412,7 +426,7 @@
             TokenFactory tokenFactory = caseSensitive ? new CaseSensitiveTokenFactory() : new CaseInsensitiveTokenFactory();
             CharacterStream characterStream = new CharacterArrayStream(inputContent);
             tokenizer.tokenize(characterStream, tokenFactory);
-            this.tokens = tokenFactory.getTokens();
+            this.tokens = initializeTokens(tokenFactory.getTokens());
         }
 
         // Create the iterator ...
@@ -420,6 +434,28 @@
         moveToNextToken();
         return this;
     }
+    
+    /**
+     * Method to allow subclasses to preprocess the set of tokens and return the correct tokens to use.
+     * The default behavior is to simply return the supplied tokens.
+     * 
+     * @param tokens
+     * @return list of tokens.
+     */
+    protected List<Token> initializeTokens( List<Token> tokens) {
+    	return tokens;
+    }
+    
+    /**
+     * Method to allow tokens to be re-used from the start without re-tokenizing content.
+     */
+    public void rewind() {
+        // recreate the iterator ...
+        tokenIterator = this.tokens.listIterator();
+        completed = false;
+        currentToken = null;
+        moveToNextToken();
+    }
 
     /**
      * Get the position of the previous token.
@@ -526,7 +562,7 @@
 
     protected void throwNoMoreContent() throws ParsingException {
         String msg = CommonI18n.noMoreContent.text();
-        Position pos = tokens.isEmpty() ? new Position(1, 0) : tokens.get(tokens.size() - 1).position();
+        Position pos = tokens.isEmpty() ? new Position(-1, 1, 0) : tokens.get(tokens.size() - 1).position();
         throw new ParsingException(pos, msg);
     }
 
@@ -954,7 +990,7 @@
      * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
      */
     public boolean matches( int expectedType ) throws IllegalStateException {
-        return !completed && (expectedType == ANY_TYPE || currentToken().type() == expectedType);
+        return !completed && currentToken().matches(expectedType);
     }
 
     /**
@@ -1047,7 +1083,7 @@
         ListIterator<Token> iter = tokens.listIterator(tokenIterator.previousIndex());
         if (!iter.hasNext()) return false;
         Token token = iter.next();
-        if (currentExpectedType != ANY_TYPE || currentToken().type() != currentExpectedType) return false;
+        if (currentExpectedType != ANY_TYPE && currentToken().type() != currentExpectedType) return false;
         for (int nextExpectedType : expectedTypeForNextTokens) {
             if (!iter.hasNext()) return false;
             token = iter.next();
@@ -1075,7 +1111,7 @@
             if (!iter.hasNext()) return false;
             token = iter.next();
             if (nextExpectedType == ANY_TYPE) continue;
-            if (token.type() != nextExpectedType) return false;
+            if (!token.matches(nextExpectedType)) return false;
         }
         return true;
     }
@@ -1233,6 +1269,29 @@
     }
 
     /**
+     * Gets the content string starting at the first position (inclusive) and continuing up to the end position (exclusive).
+     * 
+     * @param starting the position marking the beginning of the desired content string.
+     * @param end the position located directly after the returned content string;  can be null, which means end of content
+     * @return the content string; never null
+     */
+    public String getContentBetween(Position starting, Position end) {
+    	CheckArg.isNotNull(starting, "starting");
+    	
+    	int startIndex = starting.getIndexInContent();
+    	int endIndex = inputString.length();
+    	if( end != null ) {
+    		endIndex = end.getIndexInContent();
+    	}
+    	
+    	if( startIndex >= endIndex ) {
+    		throw new IllegalArgumentException(CommonI18n.endPositionMustBeGreaterThanStartingPosition.text(startIndex, endIndex));
+    	}
+    	
+    	return inputString.substring(startIndex, endIndex);
+    }
+    
+    /**
      * Get the previous token. This does not modify the state.
      * 
      * @return the previous token; never null
@@ -1249,11 +1308,10 @@
             }
             throw new IllegalStateException(CommonI18n.startMethodMustBeCalledBeforeConsumingOrMatching.text());
         }
-        if (!tokenIterator.hasPrevious()) {
+        if (tokenIterator.previousIndex() == 0 ) {
             throw new NoSuchElementException(CommonI18n.noMoreContent.text());
         }
-        ListIterator<Token> temp = tokens.listIterator(tokenIterator.previousIndex());
-        return temp.next();
+        return tokens.get(tokenIterator.previousIndex()-1);
     }
 
     String generateFragment() {
@@ -1336,10 +1394,11 @@
 
         /**
          * Get the position for the last character returned from {@link #next()}.
+         * @param startIndex 
          * 
          * @return the position of the last character returned; never null
          */
-        Position position();
+        Position position(int startIndex);
 
         /**
          * Determine if the next character on the sream is a {@link Character#isWhitespace(char) whitespace character}. This
@@ -1495,6 +1554,14 @@
          * @return true if the token's value matches the supplied character value, or false otherwise
          */
         boolean matches( char expected );
+        
+        /**
+         * Determine if the token matches the supplied type.
+         * 
+         * @param expectedType the expected integer type
+         * @return true if the token's value matches the supplied integer type, or false otherwise
+         */
+        boolean matches( int expectedType );
 
         /**
          * Get the type of the token.
@@ -1530,6 +1597,14 @@
          * @return the position; never null
          */
         Position position();
+        
+        /**
+         * Bitmask ORed with existing type value.
+         * 
+         * @param typeMask
+         * @return copy of Token with new type
+         */
+        Token withType( int typeMask);
     }
 
     /**
@@ -1553,6 +1628,17 @@
         }
 
         /**
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+		 */
+		@Override
+		public Token withType(int typeMask) {
+			int type = this.type | typeMask;
+			return new CaseSensitiveToken(startIndex, endIndex, type, position);
+		}
+
+		/**
          * {@inheritDoc}
          * 
          * @see org.jboss.dna.common.text.TokenStream.Token#type()
@@ -1607,6 +1693,16 @@
         }
 
         /**
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.common.text.TokenStream.Token#matches(int)
+		 */
+		@Override
+		public final boolean matches(int expectedType) {
+			return expectedType == ANY_TYPE || (currentToken().type() & expectedType) == expectedType;
+		}
+
+		/**
          * {@inheritDoc}
          * 
          * @see org.jboss.dna.common.text.TokenStream.Token#value()
@@ -1657,6 +1753,17 @@
         protected String matchString() {
             return inputUppercased;
         }
+        
+        /**
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+		 */
+		@Override
+		public Token withType(int typeMask) {
+			int type = this.type() | typeMask;
+			return new CaseInsensitiveToken(startIndex(), endIndex(), type, position());
+		}
     }
 
     protected abstract class TokenFactory implements Tokens {
@@ -1755,11 +1862,13 @@
 
         /**
          * {@inheritDoc}
+         * @param startIndex 
+         * @return the position of the token. never null
          * 
-         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#position()
+         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#position(int)
          */
-        public Position position() {
-            return new Position(lineNumber, columnNumber);
+        public Position position(int startIndex) {
+            return new Position(startIndex, lineNumber, columnNumber);
         }
 
         /**
@@ -1776,10 +1885,10 @@
             if (result == '\r') {
                 nextCharMayBeLineFeed = true;
                 ++lineNumber;
-                columnNumber = 1;
+                columnNumber = 0;
             } else if (result == '\n') {
                 if (!nextCharMayBeLineFeed) ++lineNumber;
-                columnNumber = 1;
+                columnNumber = 0;
             } else if (nextCharMayBeLineFeed) {
                 nextCharMayBeLineFeed = false;
             }
@@ -1922,22 +2031,22 @@
         /**
          * The {@link Token#type() token type} for tokens that consist of an individual '.' character.
          */
-        public static final int DECIMAL = 3;
+        public static final int DECIMAL = 4;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters within single-quotes. Single quote
          * characters are included if they are preceded (escaped) by a '\' character.
          */
-        public static final int SINGLE_QUOTED_STRING = 4;
+        public static final int SINGLE_QUOTED_STRING = 8;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters within double-quotes. Double quote
          * characters are included if they are preceded (escaped) by a '\' character.
          */
-        public static final int DOUBLE_QUOTED_STRING = 5;
+        public static final int DOUBLE_QUOTED_STRING = 16;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters between "/*" and "&#42;/" or between
          * "//" and the next line terminator (e.g., '\n', '\r' or "\r\n").
          */
-        public static final int COMMENT = 6;
+        public static final int COMMENT = 32;
 
         private final boolean useComments;
 
@@ -1981,14 +2090,14 @@
                     case '|':
                     case '=':
                     case ':':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                         break;
                     case '.':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, DECIMAL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, DECIMAL);
                         break;
                     case '\"':
                         int startIndex = input.index();
-                        Position startingPosition = input.position();
+                        Position startingPosition = input.position(startIndex);
                         boolean foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -2005,11 +2114,11 @@
                             throw new ParsingException(startingPosition, msg);
                         }
                         int endIndex = input.index() + 1; // beyond last character read
-                        tokens.addToken(input.position(), startIndex, endIndex, DOUBLE_QUOTED_STRING);
+                        tokens.addToken(startingPosition, startIndex, endIndex, DOUBLE_QUOTED_STRING);
                         break;
                     case '\'':
                         startIndex = input.index();
-                        startingPosition = input.position();
+                        startingPosition = input.position(startIndex);
                         foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -2026,10 +2135,11 @@
                             throw new ParsingException(startingPosition, msg);
                         }
                         endIndex = input.index() + 1; // beyond last character read
-                        tokens.addToken(input.position(), startIndex, endIndex, SINGLE_QUOTED_STRING);
+                        tokens.addToken(startingPosition, startIndex, endIndex, SINGLE_QUOTED_STRING);
                         break;
                     case '/':
                         startIndex = input.index();
+                        startingPosition = input.position(startIndex);
                         if (input.isNext('/')) {
                             // End-of-line comment ...
                             boolean foundLineTerminator = false;
@@ -2044,7 +2154,7 @@
                             if (!foundLineTerminator) ++endIndex; // must point beyond last char
                             if (c == '\r' && input.isNext('\n')) input.next();
                             if (useComments) {
-                                tokens.addToken(input.position(), startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
                         } else if (input.isNext('*')) {
                             // Multi-line comment ...
@@ -2055,21 +2165,22 @@
                             if (input.hasNext()) input.next(); // consume the '/'
                             if (useComments) {
                                 endIndex = input.index() + 1; // the token will include the '/' and '*' characters
-                                tokens.addToken(input.position(), startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
                         } else {
                             // just a regular slash ...
-                            tokens.addToken(input.position(), startIndex, startIndex + 1, SYMBOL);
+                            tokens.addToken(startingPosition, startIndex, startIndex + 1, SYMBOL);
                         }
                         break;
                     default:
                         startIndex = input.index();
+                        startingPosition = input.position(startIndex);
                         // Read until another whitespace/symbol/decimal/slash is found
                         while (input.hasNext() && !(input.isNextWhitespace() || input.isNextAnyOf("/.-(){}*,;+%?$[]!<>|=:"))) {
                             c = input.next();
                         }
                         endIndex = input.index() + 1; // beyond last character that was included
-                        tokens.addToken(input.position(), startIndex, endIndex, WORD);
+                        tokens.addToken(startingPosition, startIndex, endIndex, WORD);
                 }
             }
         }

Modified: branches/ddl_sequencer/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
===================================================================
--- branches/ddl_sequencer/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties	2009-11-18 19:42:04 UTC (rev 1330)
+++ branches/ddl_sequencer/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties	2009-11-19 23:07:26 UTC (rev 1331)
@@ -93,3 +93,4 @@
 expectingValidIntegerAtLineAndColumn = Expecting a valid integer value but found '{0}' at line {1}, column {2}
 expectingValidLongAtLineAndColumn = Expecting a valid long value but found '{0}' at line {1}, column {2}
 expectingValidBooleanAtLineAndColumn = Expecting a valid boolean value but found '{0}' at line {1}, column {2}
+endPositionMustBeGreaterThanStartingPosition = End position {1} must be greater than starting position {0}

Added: branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java	                        (rev 0)
+++ branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java	2009-11-19 23:07:26 UTC (rev 1331)
@@ -0,0 +1,85 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.common.text;
+
+import static org.junit.Assert.assertThat;
+
+import static org.hamcrest.core.Is.is;
+
+import org.junit.Test;
+
+
+public class PositionTest {
+	
+	private int combinedIndex(int firstIndex, int secondIndex) {
+		Position first = new Position(firstIndex, 1, 0);
+		Position second = new Position(secondIndex, 1, 0);
+		
+		int firstPlusSecond = first.add(second).getIndexInContent();
+		int secondPlusFirst = second.add(first).getIndexInContent();
+		
+		assertThat(firstPlusSecond, is(secondPlusFirst));
+		
+		return firstPlusSecond;
+	}
+	
+	@Test
+	public void shouldAddNoContentPositionToValidPosition() {
+		// -1 to >=0
+		assertThat(combinedIndex(-1, 0), is(0));
+		assertThat(combinedIndex(-1, 1), is(1));
+		assertThat(combinedIndex(-1, 10), is(10));
+	}
+	
+	@Test
+	public void shouldAddValidPositionToNoContentPosition() {
+		// >= 0 to -1
+		assertThat(combinedIndex(0, -1), is(0));
+		assertThat(combinedIndex(1, -1), is(1));
+		assertThat(combinedIndex(10, -1), is(10));
+	}
+	
+	@Test
+	public void shouldAddValidPositionToValidPosition() {
+		// positive to positive
+		assertThat(combinedIndex(1, 1), is(2));
+		assertThat(combinedIndex(10, 1), is(11));
+		assertThat(combinedIndex(1, 10), is(11));
+		assertThat(combinedIndex(10, 10), is(20));
+	}
+	
+	@Test
+	public void shouldAddStartingPositionToStartingPosition() {
+		// 0 to 0
+		assertThat(combinedIndex(0, 0), is(0));
+	}
+	
+	@Test
+	public void shouldAddNoContentPositionToNoContentPosition() {
+		// -1 to -1
+		assertThat(combinedIndex(-1, -1), is(-1));
+		assertThat(combinedIndex(-10, -1), is(-1));
+		assertThat(combinedIndex(-1, -10), is(-1));
+	}
+}


Property changes on: branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java	2009-11-18 19:42:04 UTC (rev 1330)
+++ branches/ddl_sequencer/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java	2009-11-19 23:07:26 UTC (rev 1331)
@@ -25,7 +25,9 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
+
 import java.util.Arrays;
+
 import org.jboss.dna.common.text.TokenStream.BasicTokenizer;
 import org.jboss.dna.common.text.TokenStream.Tokenizer;
 import org.junit.Before;
@@ -336,6 +338,19 @@
         assertThat(tokens.canConsume("FROM", "THIS", "TABLE"), is(true));
         assertThat(tokens.hasNext(), is(false));
     }
+    
+    @Test
+    public void shouldReturnTrueFromMatchIfAllTypeValuesMatch() {
+        makeCaseInsensitive();
+        assertThat(tokens.matches(BasicTokenizer.WORD, BasicTokenizer.WORD), is(true));
+    }
+    
+    @Test
+    public void shouldReturnFalseFromMatchIfAllTypeValuesDoNotMatch() {
+        makeCaseInsensitive();
+        assertThat(tokens.matches(BasicTokenizer.WORD, BasicTokenizer.DECIMAL), is(false));
+        assertThat(tokens.matches(BasicTokenizer.DECIMAL, BasicTokenizer.WORD), is(false));
+    }
 
     @Test
     public void shouldConsumeMultipleTokensWithAnyValueConstant() {
@@ -397,4 +412,51 @@
         assertThat(tokens.canConsume(TokenStream.ANY_VALUE), is(false));
         assertThat(tokens.canConsume(BasicTokenizer.SYMBOL), is(false));
     }
+    
+    @Test
+    public void shouldFindNextPositionStartIndex() {
+    	makeCaseInsensitive();
+    	// "Select all columns from this table";
+    	tokens.consume();
+    	// Next position should be line 1, column 8
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(7));
+    	assertThat(tokens.nextPosition().getColumn(), is(8));
+    	assertThat(tokens.nextPosition().getLine(), is(1));
+    }
+    
+    @Test
+    public void shouldFindPreviousPositionStartIndex() {
+    	makeCaseInsensitive();
+    	// "Select all columns from this table";
+    	tokens.consume();
+    	tokens.consume();
+    	// previous position should be line 1, column 8
+    	assertThat(tokens.previousPosition().getIndexInContent(), is(7));
+    	assertThat(tokens.previousPosition().getColumn(), is(8));
+    	assertThat(tokens.previousPosition().getLine(), is(1));
+    }
+    
+    @Test
+    public void shouldParseMultiLineString() {
+    	makeCaseInsensitive();
+    	String content = "ALTER DATABASE \n"
+    			+ "DO SOMETHING; \n"
+    			+ "ALTER DATABASE \n"
+    			+ "      SET DEFAULT BIGFILE TABLESPACE;";
+        tokens = new TokenStream(content, tokenizer, true);
+        tokens.start();
+
+    	tokens.consume(); // LINE
+    	tokens.consume(); // ONE
+    	tokens.consume(); // DO
+    	tokens.consume(); // SOMETHING
+    	tokens.consume(); // ;
+    	
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(31));
+    	assertThat(tokens.nextPosition().getColumn(), is(1));
+    	tokens.consume(); // ALTER
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(37));
+    	assertThat(tokens.nextPosition().getColumn(), is(7));
+
+    }
 }


From dna-commits at lists.jboss.org  Thu Nov 19 18:08:36 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 19 Nov 2009 18:08:36 -0500
Subject: [dna-commits] DNA SVN: r1332 - in
	branches/ddl_sequencer/dna-graph/src:
	test/java/org/jboss/dna/graph/query/parse and 1 other directory.
Message-ID: <200911192308.nAJN8a46025671@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-19 18:08:36 -0500 (Thu, 19 Nov 2009)
New Revision: 1332

Modified:
   branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
Log:
Fixed problem in token positions having the wrong column number.

Modified: branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-19 23:07:26 UTC (rev 1331)
+++ branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-19 23:08:36 UTC (rev 1332)
@@ -398,15 +398,12 @@
     }
 
     protected Term parseFullTextSearchExpression( String expression,
-                                                  Position position ) {
+                                                  Position startOfExpression ) {
         try {
             return new FullTextSearchParser().parse(expression);
         } catch (ParsingException e) {
             // Convert the position in the exception into a position in the query.
-            Position exprPos = e.getPosition();
-            int line = position.getLine() + exprPos.getLine() - 1;
-            int column = exprPos.getLine() == 1 ? exprPos.getColumn() + position.getColumn() : exprPos.getColumn();
-            Position queryPos = new Position(line, column);
+            Position queryPos = startOfExpression.add(e.getPosition());
             throw new ParsingException(queryPos, e.getMessage());
         }
     }
@@ -864,14 +861,14 @@
                     case '|':
                     case '=':
                     case ':':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                         break;
                     case '\'':
                     case '[':
                     case '\"':
                         int startIndex = input.index();
                         char closingChar = c == '[' ? ']' : c;
-                        Position pos = input.position();
+                        Position pos = input.position(startIndex);
                         boolean foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -896,7 +893,7 @@
                         break;
                     case '-':
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         if (input.isNext('-')) {
                             // End-of-line comment ...
                             boolean foundLineTerminator = false;
@@ -914,13 +911,13 @@
                                 tokens.addToken(pos, startIndex, endIndex, COMMENT);
                             }
                         } else {
-                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                             break;
                         }
                         break;
                     case '/':
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         if (input.isNext('*')) {
                             // Multi-line comment ...
                             while (input.hasNext() && !input.isNext('*', '/')) {
@@ -933,13 +930,13 @@
                                 tokens.addToken(pos, startIndex, endIndex, COMMENT);
                             }
                         } else {
-                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                             break;
                         }
                         break;
                     default:
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         // Read as long as there is a valid XML character ...
                         int tokenType = (Character.isLetterOrDigit(c) || c == '_') ? WORD : OTHER;
                         while (input.isNextLetterOrDigit() || input.isNext('_')) {

Modified: branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-19 23:07:26 UTC (rev 1331)
+++ branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-19 23:08:36 UTC (rev 1332)
@@ -625,7 +625,7 @@
 
     @Test
     public void shouldParseFullTextSearchExpressionFromStringWithValidExpression() {
-        Position pos = new Position(100, 13);
+        Position pos = new Position(500, 100, 13);
         FullTextSearch.Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
         assertThat(result, is(notNullValue()));
         assertThat(result, is(instanceOf(Disjunction.class)));
@@ -644,11 +644,12 @@
     @Test
     public void shouldConvertPositionWhenUnableToParseFullTextSearchExpression() {
         try {
-            parser.parseFullTextSearchExpression("", new Position(100, 13));
+            parser.parseFullTextSearchExpression("", new Position(500, 100, 13));
             fail("Should have thrown an exception");
         } catch (ParsingException e) {
             assertThat(e.getPosition().getLine(), is(100));
             assertThat(e.getPosition().getColumn(), is(13));
+            assertThat(e.getPosition().getIndexInContent(), is(500));
         }
     }
 


From dna-commits at lists.jboss.org  Mon Nov 23 06:03:25 2009
From: dna-commits at lists.jboss.org (VIAGRA ® Official Site)
Date: Mon, 23 Nov 2009 06:03:25 -0500
Subject: [dna-commits] Dear dna-commits@lists.jboss.org SALE 80% OFF on
	Pfizer
Message-ID: <200911231103.nANB3PNV025984@lists01.dmz-a.mwc.hst.phx2.redhat.com>

An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/dna-commits/attachments/20091123/355329d1/attachment.html 

From dna-commits at lists.jboss.org  Mon Nov 23 15:41:11 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Nov 2009 15:41:11 -0500
Subject: [dna-commits] DNA SVN: r1333 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/session.
Message-ID: <200911232041.nANKfB9U025689@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-23 15:41:11 -0500 (Mon, 23 Nov 2009)
New Revision: 1333

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
Log:
DNA-554 Corrected the logic of the GraphSession.Node.containsChangesWithExternalDependencies() method, which returned a boolean value that was opposite of the documented result. All unit tests and TCK tests continue to pass.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-19 23:08:36 UTC (rev 1332)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-23 20:41:11 UTC (rev 1333)
@@ -705,7 +705,7 @@
                          boolean keepChanges ) throws InvalidStateException, RepositorySourceException {
         if (!node.isRoot() && node.isChanged(true)) {
             // Need to make sure that changes to this branch are not dependent upon changes to nodes outside of this branch...
-            if (!node.containsChangesWithExternalDependencies()) {
+            if (node.containsChangesWithExternalDependencies()) {
                 I18n msg = GraphI18n.unableToRefreshBranchBecauseChangesDependOnChangesToNodesOutsideOfBranch;
                 String path = node.getPath().getString(context.getNamespaceRegistry());
                 throw new InvalidStateException(msg.text(path, workspaceName));
@@ -847,7 +847,7 @@
         }
 
         // Need to make sure that changes to this branch are not dependent upon changes to nodes outside of this branch...
-        if (!node.containsChangesWithExternalDependencies()) {
+        if (node.containsChangesWithExternalDependencies()) {
             I18n msg = GraphI18n.unableToSaveBranchBecauseChangesDependOnChangesToNodesOutsideOfBranch;
             String path = node.getPath().getString(context.getNamespaceRegistry());
             throw new ValidationException(msg.text(path, workspaceName));
@@ -1772,13 +1772,15 @@
                 if (!changedNode.isAtOrBelow(this)) {
                     // The node is not within this branch, so the original parent must not be at or below this node ...
                     if (cache.nodes.get(dependency.getMovedFrom()).isAtOrBelow(this)) {
-                        return false;
+                        // The original parent is below 'this' but the changed node is not ...
+                        return true;
                     }
                     // None of the other dependencies can be within this branch ...
                     for (NodeId dependentId : dependency.getRequireChangesTo()) {
                         // The dependent node must not be at or below this node ...
                         if (cache.nodes.get(dependentId).isAtOrBelow(this)) {
-                            return false;
+                            // The other node that must change is at or below 'this'
+                            return true;
                         }
                     }
                     // Otherwise, continue with the next change ...
@@ -1789,20 +1791,26 @@
                 // Second, check whether this node was moved from outside this branch ...
                 if (dependency.getMovedFrom() != null) {
                     Node<Payload, PropertyPayload> originalParent = cache.nodes.get(dependency.getMovedFrom());
+                    // If the original parent cannot be found ...
+                    if (originalParent == null) {
+                        continue;
+                    }
                     // The original parent must be at or below this node ...
                     if (!originalParent.isAtOrBelow(this)) {
-                        return false;
+                        // The original parent is not within this branch (but the new parent is)
+                        return true;
                     }
                     // All of the other dependencies must be within this branch ...
                     for (NodeId dependentId : dependency.getRequireChangesTo()) {
                         // The dependent node must not be at or below this node ...
                         if (!cache.nodes.get(dependentId).isAtOrBelow(this)) {
-                            return false;
+                            // Another dependent node is not at or below this branch either ...
+                            return true;
                         }
                     }
                 }
             }
-            return true;
+            return false;
         }
 
         /**


From dna-commits at lists.jboss.org  Mon Nov 23 17:46:42 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Nov 2009 17:46:42 -0500
Subject: [dna-commits] DNA SVN: r1334 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/request.
Message-ID: <200911232246.nANMkg3f016626@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-23 17:46:41 -0500 (Mon, 23 Nov 2009)
New Revision: 1334

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
Log:
Merge branch 'patches'

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -47,7 +47,8 @@
                                      Path path );
 
     /**
-     * Get the location of the top-most node that is to be changed by this request.
+     * Get the location of the top-most node that is to be changed by this request. If this request has been completed, this
+     * location will always have a {@link Location#getPath() path}.
      * 
      * @return the location changed by this request
      */

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -261,7 +261,7 @@
      */
     @Override
     public Location changedLocation() {
-        return into;
+        return actualIntoLocation != null ? actualIntoLocation : into;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -268,7 +268,7 @@
      */
     @Override
     public Location changedLocation() {
-        return into;
+        return actualIntoLocation != null ? actualIntoLocation : into;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -322,7 +322,7 @@
      */
     @Override
     public Location changedLocation() {
-        return actualLocation;
+        return actualLocation != null ? actualLocation : under;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -131,7 +131,7 @@
      */
     @Override
     public Location changedLocation() {
-        return at;
+        return actualLocation != null ? actualLocation : at;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -217,7 +217,7 @@
      */
     @Override
     public Location changedLocation() {
-        return at;
+        return actualLocation != null ? actualLocation : at;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -167,7 +167,7 @@
      */
     @Override
     public Location changedLocation() {
-        return actualLocation;
+        return actualLocation != null ? actualLocation : at;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -297,12 +297,15 @@
 
     /**
      * {@inheritDoc}
+     * <p>
+     * This method returns the {@link #getActualLocationAfter()} location, or if null the {@link #into()} location.
+     * </p>
      * 
      * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
      */
     @Override
     public Location changedLocation() {
-        return into != null ? into : before;
+        return actualNewLocation != null ? actualNewLocation : into != null ? into : before;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -186,7 +186,7 @@
      */
     @Override
     public Location changedLocation() {
-        return from;
+        return actualLocation != null ? actualLocation : from;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -138,6 +138,7 @@
             String msg = GraphI18n.actualNewLocationMustHaveSameNameAsRequest.text(newLocation, toName());
             throw new IllegalArgumentException(msg);
         }
+        this.actualOldLocation = oldLocation;
         this.actualNewLocation = newLocation;
     }
 
@@ -172,12 +173,15 @@
 
     /**
      * {@inheritDoc}
+     * <p>
+     * This method returns the {@link #getActualLocationAfter()} location, or if null the {@link #at()} location.
+     * </p>
      * 
      * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
      */
     @Override
     public Location changedLocation() {
-        return at;
+        return actualNewLocation != null ? actualNewLocation : at;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -187,7 +187,7 @@
      */
     @Override
     public Location changedLocation() {
-        return on;
+        return actualLocation != null ? actualLocation : on;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -133,7 +133,7 @@
      */
     @Override
     public Location changedLocation() {
-        return actualLocation;
+        return actualLocation != null ? actualLocation : at;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-23 20:41:11 UTC (rev 1333)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-23 22:46:41 UTC (rev 1334)
@@ -204,7 +204,7 @@
      */
     @Override
     public Location changedLocation() {
-        return on;
+        return actualLocation != null ? actualLocation : on;
     }
 
     /**


From dna-commits at lists.jboss.org  Mon Nov 23 18:26:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Nov 2009 18:26:00 -0500
Subject: [dna-commits] DNA SVN:  r1335 - trunk/web/dna-web-jcr-rest.
Message-ID: <200911232326.nANNQ0PX026047@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-23 18:25:59 -0500 (Mon, 23 Nov 2009)
New Revision: 1335

Modified:
   trunk/web/dna-web-jcr-rest/pom.xml
Log:
Merge branch 'patches'

Modified: trunk/web/dna-web-jcr-rest/pom.xml
===================================================================
--- trunk/web/dna-web-jcr-rest/pom.xml	2009-11-23 22:46:41 UTC (rev 1334)
+++ trunk/web/dna-web-jcr-rest/pom.xml	2009-11-23 23:25:59 UTC (rev 1335)
@@ -12,39 +12,25 @@
 	<description>JBoss DNA REST support library</description>
 	<url>http://labs.jboss.org/dna</url>
 	<dependencies>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <version>1.2.GA</version>
+        </dependency>
 		<dependency>
 			<groupId>org.jboss.resteasy</groupId>
-			<artifactId>resteasy-jaxrs</artifactId>
-			<version>1.1.GA</version>
-		</dependency>
-		<dependency>
-			<groupId>org.jboss.resteasy</groupId>
 			<artifactId>resteasy-jaxb-provider</artifactId>
-			<version>1.1.GA</version>
-			<exclusions>
-				<exclusion>
-					<groupId>com.sun.xml.bind</groupId>
-					<artifactId>jaxb-impl</artifactId>
-				</exclusion>
-			</exclusions>
+			<version>1.2.GA</version>
 		</dependency>
-
-		<!--
-			This is only included as a temp. workaround. Current release of
-			RESTEasy depends on 2.1.8, which is not in our repo at this time This
-			should be removed after RESTEasy 1.1GA is released and added to this
-			pom.
-		-->
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jettison-provider</artifactId>
+            <version>1.2.GA</version>
+        </dependency>
 		<dependency>
-			<groupId>com.sun.xml.bind</groupId>
-			<artifactId>jaxb-impl</artifactId>
-			<version>2.1.9</version>
-		</dependency>
-		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-jcr</artifactId>
 		</dependency>
-
 		<dependency>
 			<groupId>org.slf4j</groupId>
 			<artifactId>slf4j-log4j12</artifactId>


From dna-commits at lists.jboss.org  Mon Nov 23 21:19:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Nov 2009 21:19:50 -0500
Subject: [dna-commits] DNA SVN: r1336 - in trunk/web:
	dna-web-jcr-rest-client and 1 other directory.
Message-ID: <200911240219.nAO2JorO030093@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-23 21:19:50 -0500 (Mon, 23 Nov 2009)
New Revision: 1336

Modified:
   trunk/web/dna-web-jcr-rest-client/pom.xml
   trunk/web/dna-web-jcr-rest/pom.xml
Log:
Merge branch 'patches'

Modified: trunk/web/dna-web-jcr-rest/pom.xml
===================================================================
--- trunk/web/dna-web-jcr-rest/pom.xml	2009-11-23 23:25:59 UTC (rev 1335)
+++ trunk/web/dna-web-jcr-rest/pom.xml	2009-11-24 02:19:50 UTC (rev 1336)
@@ -15,17 +15,23 @@
         <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-jaxrs</artifactId>
-            <version>1.2.GA</version>
+            <version>1.2.1.GA</version>
         </dependency>
 		<dependency>
 			<groupId>org.jboss.resteasy</groupId>
 			<artifactId>resteasy-jaxb-provider</artifactId>
-			<version>1.2.GA</version>
+			<version>1.2.1.GA</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.sun.xml.bind</groupId>
+                    <artifactId>jaxb-impl</artifactId>
+                </exclusion>
+            </exclusions>
 		</dependency>
         <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-jettison-provider</artifactId>
-            <version>1.2.GA</version>
+            <version>1.2.1.GA</version>
         </dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>

Modified: trunk/web/dna-web-jcr-rest-client/pom.xml
===================================================================
--- trunk/web/dna-web-jcr-rest-client/pom.xml	2009-11-23 23:25:59 UTC (rev 1335)
+++ trunk/web/dna-web-jcr-rest-client/pom.xml	2009-11-24 02:19:50 UTC (rev 1336)
@@ -37,6 +37,27 @@
 			<version>${pom.version}</version>
 			<scope>integration-test</scope>
 		</dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <version>1.2.1.GA</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxb-provider</artifactId>
+            <version>1.2.1.GA</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.sun.xml.bind</groupId>
+                    <artifactId>jaxb-impl</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jettison-provider</artifactId>
+            <version>1.2.1.GA</version>
+        </dependency>
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
 			<artifactId>httpclient</artifactId>
@@ -83,16 +104,6 @@
 			<groupId>net.jcip</groupId>
 			<artifactId>jcip-annotations</artifactId>
 		</dependency>
-		<dependency>
-			<groupId>org.jboss.resteasy</groupId>
-			<artifactId>resteasy-jaxrs</artifactId>
-			<version>1.1.GA</version>
-		</dependency>
-		<dependency>
-			<groupId>org.jboss.resteasy</groupId>
-			<artifactId>resteasy-jaxb-provider</artifactId>
-			<version>1.1.GA</version>
-		</dependency>
 	</dependencies>
 	<reporting>
 		<plugins>


From dna-commits at lists.jboss.org  Tue Nov 24 12:43:33 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 12:43:33 -0500
Subject: [dna-commits] DNA SVN: r1337 - in trunk/dna-search:
	src/main/java/org/jboss/dna/search and 1 other directories.
Message-ID: <200911241743.nAOHhXQd023180@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-24 12:43:33 -0500 (Tue, 24 Nov 2009)
New Revision: 1337

Added:
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Modified:
   trunk/dna-search/pom.xml
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
Log:
DNA-467 Changed the implementation of the method to delete nodes under a specified branch, and added several test cases to verify that the content can be indexed, re-indexed (multiple times), and searched.

Modified: trunk/dna-search/pom.xml
===================================================================
--- trunk/dna-search/pom.xml	2009-11-24 02:19:50 UTC (rev 1336)
+++ trunk/dna-search/pom.xml	2009-11-24 17:43:33 UTC (rev 1337)
@@ -45,22 +45,22 @@
     <dependency>
       <groupId>org.apache.lucene</groupId>
       <artifactId>lucene-core</artifactId>
-      <version>2.9.0</version>
+      <version>2.9.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.lucene</groupId>
       <artifactId>lucene-analyzers</artifactId>
-      <version>2.9.0</version>
+      <version>2.9.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.lucene</groupId>
       <artifactId>lucene-snowball</artifactId>
-      <version>2.9.0</version>
+      <version>2.9.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.lucene</groupId>
       <artifactId>lucene-regex</artifactId>
-      <version>2.9.0</version>
+      <version>2.9.1</version>
     </dependency>
     <!-- 
     Testing (note the scope)

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 02:19:50 UTC (rev 1336)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 17:43:33 UTC (rev 1337)
@@ -134,7 +134,7 @@
     static {
         IndexRules.Builder builder = IndexRules.createBuilder();
         // Configure the default behavior ...
-        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
+        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE | IndexRules.FULL_TEXT);
         // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
         builder.store(JcrLexicon.UUID, DnaLexicon.UUID);
         // Configure the properties that we'll treat as dates ...
@@ -324,7 +324,13 @@
         protected IndexWriter getPathsWriter() throws IOException {
             assert !readOnly;
             if (pathsWriter == null) {
-                pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+                if (overwrite) {
+                    // Always overwrite it ...
+                    pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+                } else {
+                    // Don't overwrite, but create if missing ...
+                    pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, MaxFieldLength.UNLIMITED);
+                }
             }
             return pathsWriter;
         }
@@ -332,7 +338,13 @@
         protected IndexWriter getContentWriter() throws IOException {
             assert !readOnly;
             if (contentWriter == null) {
-                contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+                if (overwrite) {
+                    // Always overwrite it ...
+                    contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+                } else {
+                    // Don't overwrite, but create if missing ...
+                    contentWriter = new IndexWriter(contentIndexDirectory, analyzer, MaxFieldLength.UNLIMITED);
+                }
             }
             return contentWriter;
         }
@@ -492,33 +504,14 @@
          */
         public int deleteBelow( Path path ) {
             assert !readOnly;
-            // Perform a query using the reader to find those nodes at/below the path ...
             try {
-                IndexReader pathReader = getPathsReader();
-                IndexSearcher pathSearcher = new IndexSearcher(pathReader);
-                String pathStr = stringFactory.create(path) + "/";
-                PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
-                int numberDeleted = 0;
-                while (true) {
-                    // Execute the query and get the results ...
-                    TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
-                    int numResultsInBatch = results.scoreDocs.length;
-                    // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
-                    IndexReader contentReader = getContentReader();
-                    for (ScoreDoc result : results.scoreDocs) {
-                        int docId = result.doc;
-                        // Find the UUID of the node ...
-                        Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
-                        String uuid = doc.get(PathIndex.UUID);
-                        // Delete the document from the paths index ...
-                        pathReader.deleteDocument(docId);
-                        // Delete the corresponding document from the content index ...
-                        contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
-                    }
-                    numberDeleted += numResultsInBatch;
-                    if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
-                }
-                return numberDeleted;
+                // Create a query to find all the nodes at or below the specified path ...
+                Set<UUID> uuids = getUuidsForDescendantsOf(path, true);
+                Query uuidQuery = findAllNodesWithUuids(uuids);
+                // Now delete the documents from each index using this query, which we can reuse ...
+                getPathsWriter().deleteDocuments(uuidQuery);
+                getContentWriter().deleteDocuments(uuidQuery);
+                return uuids.size();
             } catch (FileNotFoundException e) {
                 // There are no index files yet, so nothing to delete ...
                 return 0;
@@ -628,42 +621,42 @@
                 }
             }
             if (pathsWriter != null) {
+                // try {
+                // pathsWriter.commit();
+                // } catch (IOException e) {
+                // if (ioError == null) ioError = e;
+                // } catch (RuntimeException e) {
+                // if (runtimeError == null) runtimeError = e;
+                // } finally {
                 try {
-                    pathsWriter.commit();
+                    pathsWriter.close();
                 } catch (IOException e) {
-                    ioError = e;
+                    if (ioError == null) ioError = e;
                 } catch (RuntimeException e) {
-                    runtimeError = e;
+                    if (runtimeError == null) runtimeError = e;
                 } finally {
-                    try {
-                        pathsWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        pathsWriter = null;
-                    }
+                    pathsWriter = null;
                 }
+                // }
             }
             if (contentWriter != null) {
+                // try {
+                // contentWriter.commit();
+                // } catch (IOException e) {
+                // if (ioError == null) ioError = e;
+                // } catch (RuntimeException e) {
+                // if (runtimeError == null) runtimeError = e;
+                // } finally {
                 try {
-                    contentWriter.commit();
+                    contentWriter.close();
                 } catch (IOException e) {
                     if (ioError == null) ioError = e;
                 } catch (RuntimeException e) {
                     if (runtimeError == null) runtimeError = e;
                 } finally {
-                    try {
-                        contentWriter.close();
-                    } catch (IOException e) {
-                        ioError = e;
-                    } catch (RuntimeException e) {
-                        runtimeError = e;
-                    } finally {
-                        contentWriter = null;
-                    }
+                    contentWriter = null;
                 }
+                // }
             }
             if (ioError != null) {
                 String msg = SearchI18n.errorWhileCommittingIndexChanges.text(workspaceName, sourceName, ioError.getMessage());
@@ -916,7 +909,7 @@
                 }
                 return query;
             }
-            // Returna query that will always find all of the UUIDs ...
+            // Return a query that will always find all of the UUIDs ...
             return new UuidsQuery(ContentIndex.UUID, uuids, getContext().getValueFactories().getUuidFactory());
         }
 
@@ -1515,8 +1508,9 @@
     protected static class UuidCollector extends Collector {
         private final Set<UUID> uuids = new HashSet<UUID>();
         private String[] uuidsByDocId;
-        private int baseDocId;
 
+        // private int baseDocId;
+
         protected UuidCollector() {
         }
 
@@ -1555,10 +1549,9 @@
          * @see org.apache.lucene.search.Collector#collect(int)
          */
         @Override
-        public void collect( int doc ) {
-            int index = doc - baseDocId;
-            assert index >= 0;
-            String uuidString = uuidsByDocId[index];
+        public void collect( int docId ) {
+            assert docId >= 0;
+            String uuidString = uuidsByDocId[docId];
             assert uuidString != null;
             uuids.add(UUID.fromString(uuidString));
         }
@@ -1572,7 +1565,7 @@
         public void setNextReader( IndexReader reader,
                                    int docBase ) throws IOException {
             this.uuidsByDocId = FieldCache.DEFAULT.getStrings(reader, UUID_FIELD);
-            this.baseDocId = docBase;
+            // this.baseDocId = docBase;
         }
     }
 }

Added: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	                        (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 17:43:33 UTC (rev 1337)
@@ -0,0 +1,193 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.io.IOException;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.search.SearchEngine;
+import org.jboss.dna.graph.search.SearchProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class SearchEngineTest {
+
+    private SearchEngine engine;
+    private SearchProvider provider;
+    private ExecutionContext context;
+    private String sourceName;
+    private String workspaceName1;
+    private String workspaceName2;
+    private InMemoryRepositorySource source;
+    private RepositoryConnectionFactory connectionFactory;
+    private Graph content;
+
+    @Before
+    public void beforeEach() throws Exception {
+        context = new ExecutionContext();
+        sourceName = "sourceA";
+        workspaceName1 = "workspace1";
+        workspaceName2 = "workspace2";
+
+        // Set up the source and graph instance ...
+        source = new InMemoryRepositorySource();
+        source.setName(sourceName);
+        content = Graph.create(source, context);
+
+        // Create the workspaces ...
+        content.createWorkspace().named(workspaceName1);
+        content.createWorkspace().named(workspaceName2);
+
+        // Set up the connection factory ...
+        connectionFactory = new RepositoryConnectionFactory() {
+            @SuppressWarnings( "synthetic-access" )
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                return source.getConnection();
+            }
+        };
+
+        // Set up the provider and the search engine ...
+        IndexRules rules = DualIndexSearchProvider.DEFAULT_RULES;
+        LuceneConfiguration luceneConfig = LuceneConfigurations.inMemory();
+        // LuceneConfiguration luceneConfig = LuceneConfigurations.using(new File("target/testIndexes"));
+        provider = new DualIndexSearchProvider(luceneConfig, rules);
+        engine = new SearchEngine(context, sourceName, connectionFactory, provider);
+        loadContent();
+    }
+
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected void loadContent() throws IOException, SAXException {
+        // Load the content ...
+        content.useWorkspace(workspaceName1);
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
+        content.useWorkspace(workspaceName2);
+        content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
+    }
+
+    @Test
+    public void shouldIndexAllContentInRepositorySource() throws Exception {
+        engine.index(3);
+    }
+
+    @Test
+    public void shouldIndexAllContentInWorkspace() throws Exception {
+        engine.index(workspaceName1, 3);
+        engine.index(workspaceName2, 5);
+    }
+
+    @Test
+    public void shouldIndexAllContentInWorkspaceBelowPath() throws Exception {
+        engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
+        engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
+    }
+
+    @Test
+    public void shouldReIndexAllContentInWorkspaceBelowPath() throws Exception {
+        for (int i = 0; i != 0; i++) {
+            engine.index(workspaceName1, path("/Cars/Hybrid"), 3);
+            engine.index(workspaceName2, path("/Aircraft/Commercial"), 5);
+        }
+    }
+
+    @Test
+    public void shouldHaveLoadedTestContentIntoRepositorySource() {
+        content.useWorkspace(workspaceName1);
+        assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfOne() {
+        engine.index(workspaceName1, path("/"), 1);
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTwo() {
+        engine.index(workspaceName1, path("/"), 2);
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfThree() {
+        engine.index(workspaceName1, path("/"), 3);
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfFour() {
+        engine.index(workspaceName1, path("/"), 4);
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTen() {
+        engine.index(workspaceName1, path("/"), 10);
+    }
+
+    @Test
+    public void shouldIndexRepositoryContentStartingAtNonRootNode() {
+        engine.index(workspaceName1, path("/Cars"), 10);
+    }
+
+    @Test
+    public void shouldReIndexRepositoryContentStartingAtNonRootNode() {
+        engine.index(workspaceName1, path("/Cars"), 10);
+        engine.index(workspaceName1, path("/Cars"), 10);
+        engine.index(workspaceName1, path("/Cars"), 10);
+    }
+
+    @Test
+    public void shouldFindNodesByFullTextSearch() {
+        engine.index(workspaceName1, path("/"), 100);
+        List<Location> results = engine.fullTextSearch(context, workspaceName1, "Toyota Prius", 10, 0);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.size(), is(2));
+        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+        assertThat(results.get(1).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+    }
+
+    @Test
+    public void shouldFindNodesByFullTextSearchWithOffset() {
+        engine.index(workspaceName1, path("/"), 100);
+        List<Location> results = engine.fullTextSearch(context, workspaceName1, "toyota prius", 1, 0);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.size(), is(1));
+        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+
+        results = engine.fullTextSearch(context, workspaceName1, "+Toyota", 1, 1);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.size(), is(1));
+        assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+    }
+}


Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Tue Nov 24 12:43:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 12:43:50 -0500
Subject: [dna-commits] DNA SVN: r1338 -
	trunk/dna-search/src/main/java/org/jboss/dna/search.
Message-ID: <200911241743.nAOHhoCq023234@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-24 12:43:50 -0500 (Tue, 24 Nov 2009)
New Revision: 1338

Modified:
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
Log:
DNA-467 Removed use of a method that was deprecated in 2.9 (or maybe 2.9.1)

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 17:43:33 UTC (rev 1337)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 17:43:50 UTC (rev 1338)
@@ -539,7 +539,7 @@
 
             try {
                 // Parse the full-text search and search against the 'fts' field ...
-                QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+                QueryParser parser = new QueryParser(Version.LUCENE_29, ContentIndex.FULL_TEXT, createAnalyzer());
                 Query query = parser.parse(fullTextString);
                 TopDocs docs = getContentSearcher().search(query, maxResults + offset);
 


From dna-commits at lists.jboss.org  Tue Nov 24 12:44:29 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 12:44:29 -0500
Subject: [dna-commits] DNA SVN: r1339 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/process and
	3 other directories.
Message-ID: <200911241744.nAOHiTad023322@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-24 12:44:29 -0500 (Tue, 24 Nov 2009)
New Revision: 1339

Added:
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/IdsQuery.java
Removed:
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Log:
DNA-467 Integrated the Lucene implementation of the SearchEngine component, added a number of unit tests.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -80,6 +80,13 @@
     public List<Object[]> getTuples();
 
     /**
+     * Get the number of rows in the results.
+     * 
+     * @return the number of rows; never negative
+     */
+    public int getRowCount();
+
+    /**
      * Get the problems encountered during execution.
      * 
      * @return the problems; never null but possibly empty

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -162,7 +162,7 @@
         switch (node.getType()) {
             case ACCESS:
                 // If the ACCESS node will not have results ...
-                if (node.getProperty(Property.ACCESS_NO_RESULTS, Boolean.class)) {
+                if (node.hasProperty(Property.ACCESS_NO_RESULTS)) {
                     component = new NoResultsComponent(context, columns);
                 } else {
                     // Create the component to handle the ACCESS node ...

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -138,6 +138,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.query.QueryResults#getRowCount()
+     */
+    public int getRowCount() {
+        return tuples.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.query.QueryResults#getProblems()
      */
     public Problems getProblems() {
@@ -251,6 +260,7 @@
             for (Object[] tuple : getTuples()) {
                 for (int i = 0, j = 1; i != tupleLength; ++i, ++j) {
                     String valueStr = stringOf(tuple[i], stringFactory);
+                    if (valueStr == null) continue;
                     columnWidths[j] = Math.max(Math.min(maxWidth, valueStr.length()), columnWidths[j]);
                 }
             }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -28,6 +28,7 @@
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -60,15 +61,14 @@
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.WildcardQuery;
 import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
-import org.apache.lucene.search.regex.RegexQuery;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.Version;
 import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.SecureHashTextEncoder;
 import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.common.util.SecureHash.Algorithm;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrLexicon;
@@ -77,8 +77,8 @@
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.property.ValueFactories;
@@ -107,13 +107,14 @@
 import org.jboss.dna.graph.search.SearchException;
 import org.jboss.dna.graph.search.SearchProvider;
 import org.jboss.dna.search.IndexRules.Rule;
+import org.jboss.dna.search.LuceneSession.TupleCollector;
 import org.jboss.dna.search.query.CompareLengthQuery;
 import org.jboss.dna.search.query.CompareNameQuery;
 import org.jboss.dna.search.query.ComparePathQuery;
 import org.jboss.dna.search.query.CompareStringQuery;
+import org.jboss.dna.search.query.IdsQuery;
 import org.jboss.dna.search.query.MatchNoneQuery;
 import org.jboss.dna.search.query.NotQuery;
-import org.jboss.dna.search.query.UuidsQuery;
 
 /**
  * A simple {@link SearchProvider} implementation that relies upon two separate indexes: one for the node content and a second one
@@ -134,8 +135,8 @@
     static {
         IndexRules.Builder builder = IndexRules.createBuilder();
         // Configure the default behavior ...
-        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE | IndexRules.FULL_TEXT);
-        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+        builder.defaultTo(IndexRules.INDEX | IndexRules.FULL_TEXT | IndexRules.STORE);
+        // Configure the UUID properties to be just indexed and stored (not analyzed, not included in full-text) ...
         builder.store(JcrLexicon.UUID, DnaLexicon.UUID);
         // Configure the properties that we'll treat as dates ...
         builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
@@ -150,24 +151,40 @@
     protected static final double MAX_DOUBLE = Double.MAX_VALUE;
     protected static final int MIN_DEPTH = 0;
     protected static final int MAX_DEPTH = 100;
+    protected static final int MIN_SNS_INDEX = 1;
+    protected static final int MAX_SNS_INDEX = 1000; // assume there won't be more than 1000 same-name-siblings
 
     protected static final String PATHS_INDEX_NAME = "paths";
     protected static final String CONTENT_INDEX_NAME = "content";
 
-    protected static final String UUID_FIELD = "uuid";
-    protected static final String FULL_TEXT_SUFFIX = "/fs"; // the slash character is not allowed in a property name unescaped
+    /**
+     * Given the name of a property field of the form "&lt;namespace>:&lt;local>" (where &lt;namespace> can be zero-length), this
+     * provider also stores the value(s) for free-text searching in a field named ":ft:&lt;namespace>:&lt;local>". Thus, even if
+     * the namespace is zero-length, the free-text search field will be named ":ft::&lt;local>" and will not clash with any other
+     * property name.
+     */
+    protected static final String FULL_TEXT_PREFIX = ":ft:";
 
+    /**
+     * This index stores only these fields, so we can use the most obvious names and not worry about clashes.
+     */
     static class PathIndex {
-        public static final String PATH = "path";
-        public static final String LOCAL_NAME = "name";
+        public static final String PATH = "pth";
+        public static final String NODE_NAME = "nam";
+        public static final String LOCAL_NAME = "loc";
         public static final String SNS_INDEX = "sns";
-        public static final String UUID = UUID_FIELD;
-        public static final String DEPTH = "depth";
+        public static final String LOCATION_ID_PROPERTIES = "idp";
+        public static final String ID = ContentIndex.ID;
+        public static final String DEPTH = "dep";
     }
 
+    /**
+     * This index stores these two fields <i>plus</i> all properties. Therefore, we have to worry about name clashes, which is why
+     * these field names are prefixed with '::', which is something that does appear in property names as they are serialized.
+     */
     static class ContentIndex {
-        public static final String UUID = UUID_FIELD;
-        public static final String FULL_TEXT = "fts";
+        public static final String ID = "::id";
+        public static final String FULL_TEXT = "::fts";
     }
 
     /**
@@ -190,12 +207,13 @@
         private static final long serialVersionUID = 1L;
 
         public FieldSelectorResult accept( String fieldName ) {
-            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+            return PathIndex.ID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
         }
     };
 
     private final IndexRules rules;
     private final LuceneConfiguration directoryConfiguration;
+    private final TextEncoder namespaceEncoder;
 
     public DualIndexSearchProvider( LuceneConfiguration directoryConfiguration,
                                     IndexRules rules ) {
@@ -203,6 +221,7 @@
         assert rules != null;
         this.rules = rules;
         this.directoryConfiguration = directoryConfiguration;
+        this.namespaceEncoder = new SecureHashTextEncoder(Algorithm.SHA_1, 10);
     }
 
     public DualIndexSearchProvider( LuceneConfiguration directoryConfiguration ) {
@@ -253,7 +272,9 @@
         assert contentIndexDirectory != null;
         Analyzer analyzer = createAnalyzer();
         assert analyzer != null;
-        return new DualIndexSession(context, sourceName, workspaceName, rules, pathIndexDirectory, contentIndexDirectory,
+        NamespaceRegistry encodingRegistry = new EncodingNamespaceRegistry(context.getNamespaceRegistry(), namespaceEncoder);
+        ExecutionContext encodingContext = context.with(encodingRegistry);
+        return new DualIndexSession(encodingContext, sourceName, workspaceName, rules, pathIndexDirectory, contentIndexDirectory,
                                     analyzer, overwrite, readOnly);
     }
 
@@ -304,9 +325,82 @@
          */
         @Override
         protected String fullTextFieldName( String propertyName ) {
-            return propertyName + FULL_TEXT_SUFFIX;
+            return FULL_TEXT_PREFIX + propertyName;
         }
 
+        protected void addIdProperties( Location location,
+                                        Document doc ) {
+            if (!location.hasIdProperties()) return;
+            for (Property idProp : location.getIdProperties()) {
+                String fieldValue = serializeProperty(idProp);
+                doc.add(new Field(PathIndex.LOCATION_ID_PROPERTIES, fieldValue, Field.Store.YES, Field.Index.NOT_ANALYZED));
+            }
+        }
+
+        protected Location readLocation( Document doc ) {
+            // Read the path ...
+            String pathString = doc.get(PathIndex.PATH);
+            Path path = pathFactory.create(pathString);
+            // Look for the Location's ID properties ...
+            String[] idProps = doc.getValues(PathIndex.LOCATION_ID_PROPERTIES);
+            if (idProps.length == 0) {
+                return Location.create(path);
+            }
+            if (idProps.length == 1) {
+                Property idProp = deserializeProperty(idProps[0]);
+                if (idProp == null) return Location.create(path);
+                if (idProp.isSingle() && (idProp.getName().equals(JcrLexicon.UUID) || idProp.getName().equals(DnaLexicon.UUID))) {
+                    return Location.create(path, (UUID)idProp.getFirstValue()); // know that deserialize returns UUID value
+                }
+                return Location.create(path, idProp);
+            }
+            List<Property> properties = new LinkedList<Property>();
+            for (String idProp : doc.getValues(PathIndex.LOCATION_ID_PROPERTIES)) {
+                Property prop = deserializeProperty(idProp);
+                if (prop != null) properties.add(prop);
+            }
+            return properties.isEmpty() ? Location.create(path) : Location.create(path, properties);
+
+        }
+
+        protected final String serializeProperty( Property property ) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(stringFactory.create(property.getName()));
+            sb.append('=');
+            Iterator<?> iter = property.getValues();
+            if (iter.hasNext()) {
+                sb.append(stringFactory.create(iter.next()));
+            }
+            while (iter.hasNext()) {
+                sb.append('\n');
+                sb.append(stringFactory.create(iter.next()));
+            }
+            return sb.toString();
+        }
+
+        protected final Property deserializeProperty( String propertyString ) {
+            int index = propertyString.indexOf('=');
+            assert index > -1;
+            if (index == propertyString.length() - 1) return null;
+            Name propName = nameFactory.create(propertyString.substring(0, index));
+            String valueString = propertyString.substring(index + 1);
+            // Break into multiple values if multiple lines ...
+            String[] values = valueString.split("\\n");
+            if (values.length == 0) return null;
+            if (values.length == 1) {
+                Object value = values[0];
+                if (DnaLexicon.UUID.equals(propName) || JcrLexicon.UUID.equals(propName)) {
+                    value = uuidFactory.create(value);
+                }
+                return context.getPropertyFactory().create(propName, value);
+            }
+            List<String> propValues = new LinkedList<String>();
+            for (String value : values) {
+                propValues.add(value);
+            }
+            return context.getPropertyFactory().create(propName, propValues);
+        }
+
         protected IndexReader getPathsReader() throws IOException {
             if (pathsReader == null) {
                 pathsReader = IndexReader.open(pathsIndexDirectory, readOnly);
@@ -379,9 +473,10 @@
             UUID uuid = location.getUuid();
             if (uuid == null) uuid = UUID.randomUUID();
             Path path = location.getPath();
-            String uuidStr = stringFactory.create(uuid);
+            String idStr = stringFactory.create(uuid);
             String pathStr = pathAsString(path, stringFactory);
             String nameStr = path.isRoot() ? "" : stringFactory.create(path.getLastSegment().getName());
+            String localNameStr = path.isRoot() ? "" : path.getLastSegment().getName().getLocalName();
             int sns = path.isRoot() ? 1 : path.getLastSegment().getIndex();
 
             Logger logger = Logger.getLogger(getClass());
@@ -395,15 +490,17 @@
                 // be changed without changing any other content fields ...
                 Document doc = new Document();
                 doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-                doc.add(new Field(PathIndex.LOCAL_NAME, nameStr, Field.Store.YES, Field.Index.ANALYZED));
-                doc.add(new NumericField(PathIndex.LOCAL_NAME, Field.Store.YES, true).setIntValue(sns));
-                doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new Field(PathIndex.NODE_NAME, nameStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new Field(PathIndex.LOCAL_NAME, localNameStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new NumericField(PathIndex.SNS_INDEX, Field.Store.YES, true).setIntValue(sns));
+                doc.add(new Field(PathIndex.ID, idStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
                 doc.add(new NumericField(PathIndex.DEPTH, Field.Store.YES, true).setIntValue(path.size()));
+                addIdProperties(location, doc);
                 getPathsWriter().addDocument(doc);
 
                 // Create the document for the content (properties) ...
                 doc = new Document();
-                doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+                doc.add(new Field(ContentIndex.ID, idStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
                 String stringValue = null;
                 StringBuilder fullTextSearchValue = null;
                 for (Property property : node.getProperties()) {
@@ -506,12 +603,12 @@
             assert !readOnly;
             try {
                 // Create a query to find all the nodes at or below the specified path ...
-                Set<UUID> uuids = getUuidsForDescendantsOf(path, true);
-                Query uuidQuery = findAllNodesWithUuids(uuids);
+                Set<String> ids = getIdsForDescendantsOf(path, true);
+                Query uuidQuery = findAllNodesWithIds(ids);
                 // Now delete the documents from each index using this query, which we can reuse ...
                 getPathsWriter().deleteDocuments(uuidQuery);
                 getContentWriter().deleteDocuments(uuidQuery);
-                return uuids.size();
+                return ids.size();
             } catch (FileNotFoundException e) {
                 // There are no index files yet, so nothing to delete ...
                 return 0;
@@ -551,24 +648,19 @@
                 int numberOfResults = scoreDocs.length;
                 if (numberOfResults > offset) {
                     // There are enough results to satisfy the offset ...
-                    PathFactory pathFactory = context.getValueFactories().getPathFactory();
                     for (int i = offset, num = scoreDocs.length; i != num; ++i) {
                         ScoreDoc result = scoreDocs[i];
                         int docId = result.doc;
                         // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
                         Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
-                        String uuid = doc.get(ContentIndex.UUID);
-                        // Find the path for this node (is there a better way to do this than one search per UUID?) ...
-                        TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
-                        if (pathDocs.scoreDocs.length < 1) {
+                        String id = doc.get(ContentIndex.ID);
+                        Location location = getLocationForDocument(id, pathReader, pathSearcher);
+                        if (location == null) {
                             // No path record found ...
                             continue;
                         }
-                        Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
-                        String pathString = pathDoc.get(PathIndex.PATH);
-                        Path path = pathFactory.create(pathString);
                         // Now add the location ...
-                        results.add(Location.create(path, UUID.fromString(uuid)));
+                        results.add(location);
                     }
                 }
             } catch (ParseException e) {
@@ -579,6 +671,26 @@
             }
         }
 
+        protected Location getLocationForDocument( String id,
+                                                   IndexReader pathReader,
+                                                   IndexSearcher pathSearcher ) throws IOException {
+            // Find the path for this node (is there a better way to do this than one search per ID?) ...
+            TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.ID, id)), 1);
+            if (pathDocs.scoreDocs.length < 1) {
+                // No path record found ...
+                return null;
+            }
+            Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+            return readLocation(pathDoc);
+        }
+
+        protected UUID getUuid( Document document,
+                                Name name ) {
+            String nameString = stringFactory.create(name);
+            String value = document.get(nameString);
+            return value != null ? uuidFactory.create(value) : null;
+        }
+
         /**
          * {@inheritDoc}
          * 
@@ -802,13 +914,23 @@
         }
 
         /**
-         * Get the set of UUIDs for the children of the node at the given path.
+         * {@inheritDoc}
          * 
+         * @see org.jboss.dna.search.LuceneSession#createTupleCollector(Columns)
+         */
+        @Override
+        public TupleCollector createTupleCollector( Columns columns ) {
+            return new DualIndexTupleCollector(this, columns);
+        }
+
+        /**
+         * Get the set of IDs for the children of the node at the given path.
+         * 
          * @param parentPath the path to the parent node; may not be null
-         * @return the UUIDs of the child nodes; never null but possibly empty
+         * @return the doc IDs of the child nodes; never null but possibly empty
          * @throws IOException if there is an error accessing the indexes
          */
-        protected Set<UUID> getUuidsForChildrenOf( Path parentPath ) throws IOException {
+        protected Set<String> getIdsForChildrenOf( Path parentPath ) throws IOException {
             // Find the path of the parent ...
             String stringifiedPath = pathAsString(parentPath, stringFactory);
             // Append a '/' to the parent path, so we'll only get decendants ...
@@ -825,23 +947,23 @@
             combinedQuery.add(depthQuery, Occur.MUST);
             query = combinedQuery;
 
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return uuidCollector.getUuids();
+            searcher.search(query, idCollector);
+            return idCollector.getIds();
         }
 
         /**
-         * Get the set of UUIDs for the nodes that are descendants of the node at the given path.
+         * Get the set of IDs for the nodes that are descendants of the node at the given path.
          * 
          * @param parentPath the path to the parent node; may not be null and <i>may not be the root node</i>
          * @param includeParent true if the parent node should be included in the results, or false if only the descendants should
          *        be included
-         * @return the UUIDs of the nodes; never null but possibly empty
+         * @return the IDs of the nodes; never null but possibly empty
          * @throws IOException if there is an error accessing the indexes
          */
-        protected Set<UUID> getUuidsForDescendantsOf( Path parentPath,
+        protected Set<String> getIdsForDescendantsOf( Path parentPath,
                                                       boolean includeParent ) throws IOException {
             assert !parentPath.isRoot();
 
@@ -855,21 +977,21 @@
             // Create a prefix query ...
             Query query = new PrefixQuery(new Term(PathIndex.PATH, stringifiedPath));
 
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return uuidCollector.getUuids();
+            searcher.search(query, idCollector);
+            return idCollector.getIds();
         }
 
         /**
-         * Get the set containing the single UUID for the node at the given path.
+         * Get the set containing the single ID for the node at the given path.
          * 
          * @param path the path to the node; may not be null
-         * @return the UUID of the supplied node; or null if the node cannot be found
+         * @return the ID of the supplied node; or null if the node cannot be found
          * @throws IOException if there is an error accessing the indexes
          */
-        protected UUID getUuidFor( Path path ) throws IOException {
+        protected String getIdFor( Path path ) throws IOException {
             // Create a query to find all the nodes below the parent path ...
             IndexSearcher searcher = getPathsSearcher();
             String stringifiedPath = pathAsString(path, stringFactory);
@@ -879,38 +1001,38 @@
             TopDocs topDocs = searcher.search(query, 1);
             if (topDocs.totalHits == 0) return null;
             Document pathDoc = getPathsReader().document(topDocs.scoreDocs[0].doc);
-            String uuidString = pathDoc.get(PathIndex.UUID);
-            return UUID.fromString(uuidString);
+            String idString = pathDoc.get(PathIndex.ID);
+            assert idString != null;
+            return idString;
         }
 
         /**
-         * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
+         * {@inheritDoc}
          * 
-         * @param uuids the UUIDs of the nodes that are to be found; may not be null
-         * @return the query; never null
+         * @see org.jboss.dna.search.LuceneSession#findAllNodesWithIds(java.util.Set)
          */
         @Override
-        public Query findAllNodesWithUuids( Set<UUID> uuids ) {
-            if (uuids.isEmpty()) {
+        public Query findAllNodesWithIds( Set<String> ids ) {
+            if (ids.isEmpty()) {
                 // There are no children, so return a null query ...
                 return new MatchNoneQuery();
             }
-            if (uuids.size() == 1) {
-                UUID uuid = uuids.iterator().next();
-                if (uuid == null) return new MatchNoneQuery();
-                return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+            if (ids.size() == 1) {
+                String id = ids.iterator().next();
+                if (id == null) return new MatchNoneQuery();
+                return new TermQuery(new Term(ContentIndex.ID, id));
             }
-            if (uuids.size() < 50) {
+            if (ids.size() < 50) {
                 // Create an OR boolean query for all the UUIDs, since this is probably more efficient ...
                 BooleanQuery query = new BooleanQuery();
-                for (UUID uuid : uuids) {
-                    Query uuidQuery = new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+                for (String id : ids) {
+                    Query uuidQuery = new TermQuery(new Term(ContentIndex.ID, id));
                     query.add(uuidQuery, Occur.SHOULD);
                 }
                 return query;
             }
             // Return a query that will always find all of the UUIDs ...
-            return new UuidsQuery(ContentIndex.UUID, uuids, getContext().getValueFactories().getUuidFactory());
+            return new IdsQuery(ContentIndex.ID, ids);
         }
 
         @Override
@@ -918,8 +1040,8 @@
             if (ancestorPath.isRoot()) {
                 return new MatchAllDocsQuery();
             }
-            Set<UUID> uuids = getUuidsForDescendantsOf(ancestorPath, false);
-            return findAllNodesWithUuids(uuids);
+            Set<String> ids = getIdsForDescendantsOf(ancestorPath, false);
+            return findAllNodesWithIds(ids);
         }
 
         /**
@@ -935,58 +1057,37 @@
             if (parentPath.isRoot()) {
                 return new MatchAllDocsQuery();
             }
-            Set<UUID> childUuids = getUuidsForChildrenOf(parentPath);
-            return findAllNodesWithUuids(childUuids);
+            Set<String> childIds = getIdsForChildrenOf(parentPath);
+            return findAllNodesWithIds(childIds);
         }
 
         /**
          * Create a query that can be used to find the one document (or node) that exists at the exact path supplied. This method
-         * first queries the {@link PathIndex path index} to find the UUID of the node at the supplied path, and then returns a
-         * query that matches the UUID.
+         * first queries the {@link PathIndex path index} to find the ID of the node at the supplied path, and then returns a
+         * query that matches the ID.
          * 
          * @param path the path of the node
          * @return the query; never null
-         * @throws IOException if there is an error finding the UUID for the supplied path
+         * @throws IOException if there is an error finding the ID for the supplied path
          */
         @Override
         public Query findNodeAt( Path path ) throws IOException {
-            UUID uuid = getUuidFor(path);
-            if (uuid == null) return null;
-            return new TermQuery(new Term(ContentIndex.UUID, uuid.toString()));
+            String id = getIdFor(path);
+            if (id == null) return null;
+            return new TermQuery(new Term(ContentIndex.ID, id));
         }
 
         /**
-         * Create a query that can be used to find documents (or nodes) that have a field value that satisfies the supplied LIKE
-         * expression.
+         * {@inheritDoc}
          * 
-         * @param fieldName the name of the document field to search
-         * @param likeExpression the JCR like expression
-         * @return the query; never null
+         * @see org.jboss.dna.search.LuceneSession#findNodesLike(java.lang.String, java.lang.String, boolean)
          */
         @Override
         public Query findNodesLike( String fieldName,
-                                    String likeExpression ) {
-            assert likeExpression != null;
-            assert likeExpression.length() > 0;
-
-            // '%' matches 0 or more characters
-            // '_' matches any single character
-            // '\x' matches 'x'
-            // all other characters match themselves
-
-            // Wildcard queries are a better match, but they can be slow and should not be used
-            // if the first character of the expression is a '%' or '_' ...
-            char firstChar = likeExpression.charAt(0);
-            if (firstChar != '%' && firstChar != '_') {
-                // Create a wildcard query ...
-                String expression = toWildcardExpression(likeExpression);
-                return new WildcardQuery(new Term(fieldName, expression));
-            }
-            // Create a regex query,
-            String regex = toRegularExpression(likeExpression);
-            RegexQuery query = new RegexQuery(new Term(fieldName, regex));
-            query.setRegexImplementation(new JavaUtilRegexCapabilities());
-            return query;
+                                    String likeExpression,
+                                    boolean caseSensitive ) {
+            ValueFactories factories = context.getValueFactories();
+            return CompareStringQuery.createQueryForNodesWithFieldLike(likeExpression, fieldName, factories, caseSensitive);
         }
 
         @Override
@@ -1042,9 +1143,15 @@
                     if (!caseSensitive) stringValue = stringValue.toLowerCase();
                     switch (operator) {
                         case EQUAL_TO:
-                            return new TermQuery(new Term(field, stringValue));
+                            return CompareStringQuery.createQueryForNodesWithFieldEqualTo(stringValue,
+                                                                                          field,
+                                                                                          factories,
+                                                                                          caseSensitive);
                         case NOT_EQUAL_TO:
-                            Query query = new TermQuery(new Term(field, stringValue));
+                            Query query = CompareStringQuery.createQueryForNodesWithFieldEqualTo(stringValue,
+                                                                                                 field,
+                                                                                                 factories,
+                                                                                                 caseSensitive);
                             return new NotQuery(query);
                         case GREATER_THAN:
                             return CompareStringQuery.createQueryForNodesWithFieldGreaterThan(stringValue,
@@ -1067,7 +1174,7 @@
                                                                                                     factories,
                                                                                                     caseSensitive);
                         case LIKE:
-                            return findNodesLike(field, stringValue);
+                            return findNodesLike(field, stringValue, caseSensitive);
                     }
                     break;
                 case DATE:
@@ -1239,7 +1346,8 @@
                     return new NotQuery(findNodeAt(pathValue));
                 case LIKE:
                     String likeExpression = stringFactory.create(value);
-                    return findNodesLike(PathIndex.PATH, likeExpression);
+                    query = findNodesLike(PathIndex.PATH, likeExpression, caseSensitive);
+                    break;
                 case GREATER_THAN:
                     query = ComparePathQuery.createQueryForNodesWithPathGreaterThan(pathValue,
                                                                                     PathIndex.PATH,
@@ -1265,11 +1373,11 @@
                                                                                           caseSensitive);
                     break;
             }
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
+            searcher.search(query, idCollector);
+            return findAllNodesWithIds(idCollector.getIds());
         }
 
         @Override
@@ -1277,6 +1385,7 @@
                                     Operator operator,
                                     Object value,
                                     boolean caseSensitive ) throws IOException {
+            ValueFactories factories = getContext().getValueFactories();
             String stringValue = stringFactory.create(value);
             if (!caseSensitive) stringValue = stringValue.toLowerCase();
             Path.Segment segment = operator != Operator.LIKE ? pathFactory.createSegment(stringValue) : null;
@@ -1285,42 +1394,42 @@
             switch (operator) {
                 case EQUAL_TO:
                     BooleanQuery booleanQuery = new BooleanQuery();
-                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.NODE_NAME, stringValue)), Occur.MUST);
                     booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
                                      Occur.MUST);
                     return booleanQuery;
                 case NOT_EQUAL_TO:
                     booleanQuery = new BooleanQuery();
-                    booleanQuery.add(new TermQuery(new Term(PathIndex.LOCAL_NAME, stringValue)), Occur.MUST);
+                    booleanQuery.add(new TermQuery(new Term(PathIndex.NODE_NAME, stringValue)), Occur.MUST);
                     booleanQuery.add(NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, snsIndex, snsIndex, true, false),
                                      Occur.MUST);
                     return new NotQuery(booleanQuery);
                 case GREATER_THAN:
                     query = CompareNameQuery.createQueryForNodesWithNameGreaterThan(segment,
-                                                                                    PathIndex.LOCAL_NAME,
+                                                                                    PathIndex.NODE_NAME,
                                                                                     PathIndex.SNS_INDEX,
-                                                                                    context.getValueFactories(),
+                                                                                    factories,
                                                                                     caseSensitive);
                     break;
                 case GREATER_THAN_OR_EQUAL_TO:
                     query = CompareNameQuery.createQueryForNodesWithNameGreaterThanOrEqualTo(segment,
-                                                                                             PathIndex.LOCAL_NAME,
+                                                                                             PathIndex.NODE_NAME,
                                                                                              PathIndex.SNS_INDEX,
-                                                                                             context.getValueFactories(),
+                                                                                             factories,
                                                                                              caseSensitive);
                     break;
                 case LESS_THAN:
                     query = CompareNameQuery.createQueryForNodesWithNameLessThan(segment,
-                                                                                 PathIndex.LOCAL_NAME,
+                                                                                 PathIndex.NODE_NAME,
                                                                                  PathIndex.SNS_INDEX,
-                                                                                 context.getValueFactories(),
+                                                                                 factories,
                                                                                  caseSensitive);
                     break;
                 case LESS_THAN_OR_EQUAL_TO:
                     query = CompareNameQuery.createQueryForNodesWithNameLessThanOrEqualTo(segment,
-                                                                                          PathIndex.LOCAL_NAME,
+                                                                                          PathIndex.NODE_NAME,
                                                                                           PathIndex.SNS_INDEX,
-                                                                                          context.getValueFactories(),
+                                                                                          factories,
                                                                                           caseSensitive);
                     break;
                 case LIKE:
@@ -1330,7 +1439,10 @@
                     if (openBracketIndex != -1) {
                         String localNameExpression = likeExpression.substring(0, openBracketIndex);
                         String snsIndexExpression = likeExpression.substring(openBracketIndex);
-                        Query localNameQuery = createLocalNameQuery(localNameExpression);
+                        Query localNameQuery = CompareStringQuery.createQueryForNodesWithFieldLike(localNameExpression,
+                                                                                                   PathIndex.NODE_NAME,
+                                                                                                   factories,
+                                                                                                   caseSensitive);
                         Query snsQuery = createSnsIndexQuery(snsIndexExpression);
                         if (localNameQuery == null) {
                             if (snsQuery == null) {
@@ -1353,17 +1465,20 @@
                         }
                     } else {
                         // There is no SNS expression ...
-                        query = createLocalNameQuery(likeExpression);
+                        query = CompareStringQuery.createQueryForNodesWithFieldLike(likeExpression,
+                                                                                    PathIndex.NODE_NAME,
+                                                                                    factories,
+                                                                                    caseSensitive);
                     }
                     assert query != null;
                     break;
             }
 
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
+            searcher.search(query, idCollector);
+            return findAllNodesWithIds(idCollector.getIds());
         }
 
         @Override
@@ -1376,12 +1491,20 @@
             switch (operator) {
                 case LIKE:
                     String likeExpression = stringFactory.create(value);
-                    return findNodesLike(PathIndex.LOCAL_NAME, likeExpression); // already is a query with UUIDs
+                    query = findNodesLike(PathIndex.LOCAL_NAME, likeExpression, caseSensitive);
+                    break;
                 case EQUAL_TO:
-                    query = new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue));
+                    query = CompareStringQuery.createQueryForNodesWithFieldEqualTo(nameValue,
+                                                                                   PathIndex.LOCAL_NAME,
+                                                                                   context.getValueFactories(),
+                                                                                   caseSensitive);
                     break;
                 case NOT_EQUAL_TO:
-                    query = new NotQuery(new TermQuery(new Term(PathIndex.LOCAL_NAME, nameValue)));
+                    query = CompareStringQuery.createQueryForNodesWithFieldEqualTo(nameValue,
+                                                                                   PathIndex.LOCAL_NAME,
+                                                                                   context.getValueFactories(),
+                                                                                   caseSensitive);
+                    query = new NotQuery(query);
                     break;
                 case GREATER_THAN:
                     query = CompareStringQuery.createQueryForNodesWithFieldGreaterThan(nameValue,
@@ -1409,11 +1532,11 @@
                     break;
             }
 
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
+            searcher.search(query, idCollector);
+            return findAllNodesWithIds(idCollector.getIds());
         }
 
         @Override
@@ -1447,25 +1570,34 @@
                     return null;
             }
 
-            // Now execute and collect the UUIDs ...
-            UuidCollector uuidCollector = new UuidCollector();
+            // Now execute and collect the IDs ...
+            IdCollector idCollector = new IdCollector();
             IndexSearcher searcher = getPathsSearcher();
-            searcher.search(query, uuidCollector);
-            return findAllNodesWithUuids(uuidCollector.getUuids());
+            searcher.search(query, idCollector);
+            return findAllNodesWithIds(idCollector.getIds());
         }
 
-        protected Query createLocalNameQuery( String likeExpression ) {
+        protected Query createLocalNameQuery( String likeExpression,
+                                              boolean caseSensitive ) {
             if (likeExpression == null) return null;
-            likeExpression = likeExpression.trim();
-            if (likeExpression.length() == 0) return null;
-            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
-                // The local name is a like ...
-                return findNodesLike(PathIndex.LOCAL_NAME, likeExpression);
-            }
-            // The local name is an exact match ...
-            return new TermQuery(new Term(PathIndex.LOCAL_NAME, likeExpression));
+            ValueFactories factories = getContext().getValueFactories();
+            return CompareStringQuery.createQueryForNodesWithFieldLike(likeExpression,
+                                                                       PathIndex.LOCAL_NAME,
+                                                                       factories,
+                                                                       caseSensitive);
         }
 
+        /**
+         * Utility method to generate a query against the SNS indexes. This method attempts to generate a query that works most
+         * efficiently, depending upon the supplied expression. For example, if the supplied expression is just "[3]", then a
+         * range query is used to find all values matching '3'. However, if "[3_]" is used (where '_' matches any
+         * single-character, or digit in this case), then a range query is used to find all values between '30' and '39'.
+         * Similarly, if "[3%]" is used, then a regular expression query is used.
+         * 
+         * @param likeExpression the expression that uses the JCR 2.0 LIKE representation, and which includes the leading '[' and
+         *        trailing ']' characters
+         * @return the query, or null if the expression cannot be represented as a query
+         */
         protected Query createSnsIndexQuery( String likeExpression ) {
             if (likeExpression == null) return null;
             likeExpression = likeExpression.trim();
@@ -1480,16 +1612,51 @@
             if (closeBracketIndex != -1) {
                 likeExpression = likeExpression.substring(0, closeBracketIndex);
             }
-            // If SNS expression contains '?' or '*' ...
-            if (likeExpression.indexOf('?') != -1 || likeExpression.indexOf('*') != -1) {
-                // There is a LIKE expression for the SNS ...
-                return findNodesLike(PathIndex.SNS_INDEX, likeExpression);
+            if (likeExpression.equals("_")) {
+                // The SNS expression can only be one digit ...
+                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, MIN_SNS_INDEX, 9, true, true);
             }
+            if (likeExpression.equals("%")) {
+                // The SNS expression can be any digits ...
+                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, MIN_SNS_INDEX, MAX_SNS_INDEX, true, true);
+            }
+            if (likeExpression.indexOf('_') != -1) {
+                if (likeExpression.indexOf('%') != -1) {
+                    // Contains both ...
+                    return findNodesLike(PathIndex.SNS_INDEX, likeExpression, true);
+                }
+                // It presumably contains some numbers and at least one '_' character ...
+                int firstWildcardChar = likeExpression.indexOf('_');
+                if (firstWildcardChar + 1 < likeExpression.length()) {
+                    // There's at least some characters after the first '_' ...
+                    int secondWildcardChar = likeExpression.indexOf('_', firstWildcardChar + 1);
+                    if (secondWildcardChar != -1) {
+                        // There are multiple '_' characters ...
+                        return findNodesLike(PathIndex.SNS_INDEX, likeExpression, true);
+                    }
+                }
+                // There's only one '_', so parse the lowermost value and uppermost value ...
+                String lowerExpression = likeExpression.replace('_', '0');
+                String upperExpression = likeExpression.replace('_', '9');
+                try {
+                    // This SNS is just a number ...
+                    int lowerSns = Integer.parseInt(lowerExpression);
+                    int upperSns = Integer.parseInt(upperExpression);
+                    return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, lowerSns, upperSns, true, true);
+                } catch (NumberFormatException e) {
+                    // It's not a number but it's in the SNS field, so there will be no results ...
+                    return new MatchNoneQuery();
+                }
+            }
+            if (likeExpression.indexOf('%') != -1) {
+                // It presumably contains some numbers and at least one '%' character ...
+                return findNodesLike(PathIndex.SNS_INDEX, likeExpression, true);
+            }
             // This is not a LIKE expression but an exact value specification and should be a number ...
             try {
                 // This SNS is just a number ...
                 int sns = Integer.parseInt(likeExpression);
-                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, sns, sns, true, false);
+                return NumericRangeQuery.newIntRange(PathIndex.SNS_INDEX, sns, sns, true, true);
             } catch (NumberFormatException e) {
                 // It's not a number but it's in the SNS field, so there will be no results ...
                 return new MatchNoneQuery();
@@ -1505,13 +1672,13 @@
      * 
      * @see DualIndexSearchProvider.DualIndexSession#findChildNodes(Path)
      */
-    protected static class UuidCollector extends Collector {
-        private final Set<UUID> uuids = new HashSet<UUID>();
-        private String[] uuidsByDocId;
+    protected static class IdCollector extends Collector {
+        private final Set<String> ids = new HashSet<String>();
+        private String[] idsByDocId;
 
         // private int baseDocId;
 
-        protected UuidCollector() {
+        protected IdCollector() {
         }
 
         /**
@@ -1519,8 +1686,8 @@
          * 
          * @return the set of UUIDs; never null
          */
-        public Set<UUID> getUuids() {
-            return uuids;
+        public Set<String> getIds() {
+            return ids;
         }
 
         /**
@@ -1551,9 +1718,9 @@
         @Override
         public void collect( int docId ) {
             assert docId >= 0;
-            String uuidString = uuidsByDocId[docId];
-            assert uuidString != null;
-            uuids.add(UUID.fromString(uuidString));
+            String idString = idsByDocId[docId];
+            assert idString != null;
+            ids.add(idString);
         }
 
         /**
@@ -1564,8 +1731,148 @@
         @Override
         public void setNextReader( IndexReader reader,
                                    int docBase ) throws IOException {
-            this.uuidsByDocId = FieldCache.DEFAULT.getStrings(reader, UUID_FIELD);
+            this.idsByDocId = FieldCache.DEFAULT.getStrings(reader, ContentIndex.ID); // same value as PathIndex.ID
             // this.baseDocId = docBase;
         }
     }
+
+    /**
+     * This collector is responsible for loading the value for each of the columns into each tuple array.
+     */
+    protected class DualIndexTupleCollector extends TupleCollector {
+        private final DualIndexSession session;
+        private final LinkedList<Object[]> tuples = new LinkedList<Object[]>();
+        private final Columns columns;
+        private final int numValues;
+        private final boolean recordScore;
+        private final int scoreIndex;
+        private final FieldSelector fieldSelector;
+        private final int locationIndex;
+        private Scorer scorer;
+        private IndexReader currentReader;
+        private int docOffset;
+        private boolean resolvedLocations = false;
+
+        protected DualIndexTupleCollector( DualIndexSession session,
+                                           Columns columns ) {
+            this.session = session;
+            this.columns = columns;
+            assert this.session != null;
+            assert this.columns != null;
+            this.numValues = this.columns.getTupleSize();
+            assert this.numValues >= 0;
+            assert this.columns.getSelectorNames().size() == 1;
+            final String selectorName = this.columns.getSelectorNames().get(0);
+            this.locationIndex = this.columns.getLocationIndex(selectorName);
+            this.recordScore = this.columns.hasFullTextSearchScores();
+            this.scoreIndex = this.recordScore ? this.columns.getFullTextSearchScoreIndexFor(selectorName) : -1;
+            final Set<String> columnNames = new HashSet<String>(this.columns.getColumnNames());
+            columnNames.add(ContentIndex.ID); // add the UUID, which we'll put into the Location ...
+            this.fieldSelector = new FieldSelector() {
+                private static final long serialVersionUID = 1L;
+
+                public FieldSelectorResult accept( String fieldName ) {
+                    return columnNames.contains(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
+                }
+            };
+        }
+
+        /**
+         * @return tuples
+         */
+        @Override
+        public LinkedList<Object[]> getTuples() {
+            resolveLocations();
+            return tuples;
+        }
+
+        protected void resolveLocations() {
+            if (resolvedLocations) return;
+            try {
+                // The Location field in the tuples all contain the ID of the document, so we need to replace these
+                // with the appropriate Location objects, using the content from the PathIndex ...
+                IndexReader pathReader = session.getPathsReader();
+                IndexSearcher pathSearcher = session.getPathsSearcher();
+                for (Object[] tuple : tuples) {
+                    String id = (String)tuple[locationIndex];
+                    assert id != null;
+                    Location location = session.getLocationForDocument(id, pathReader, pathSearcher);
+                    assert location != null;
+                    tuple[locationIndex] = location;
+                }
+                resolvedLocations = true;
+            } catch (IOException e) {
+                throw new LuceneException(e);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
+         */
+        @Override
+        public boolean acceptsDocsOutOfOrder() {
+            return true;
+        }
+
+        /**
+         * Get the location index.
+         * 
+         * @return locationIndex
+         */
+        public int getLocationIndex() {
+            return locationIndex;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public void setNextReader( IndexReader reader,
+                                   int docBase ) {
+            this.currentReader = reader;
+            this.docOffset = docBase;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
+         */
+        @Override
+        public void setScorer( Scorer scorer ) {
+            this.scorer = scorer;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Collector#collect(int)
+         */
+        @Override
+        public void collect( int doc ) throws IOException {
+            int docId = doc + docOffset;
+            Object[] tuple = new Object[numValues];
+            Document document = currentReader.document(docId, fieldSelector);
+            for (String columnName : columns.getColumnNames()) {
+                int index = columns.getColumnIndexForName(columnName);
+                // We just need to retrieve the first value if there is more than one ...
+                tuple[index] = document.get(columnName);
+            }
+
+            // Set the score column if required ...
+            if (recordScore) {
+                assert scorer != null;
+                tuple[scoreIndex] = scorer.score();
+            }
+
+            // Load the document ID (which is a stringified UUID) into the Location slot,
+            // which will be replaced later with a real Location ...
+            tuple[locationIndex] = document.get(ContentIndex.ID);
+            tuples.add(tuple);
+        }
+    }
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -339,7 +339,7 @@
      */
     public static Builder createBuilder( IndexRules initialRules ) {
         CheckArg.isNotNull(initialRules, "initialRules");
-        return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
+        return new Builder(new HashMap<Name, Rule>(initialRules.rulesByName)).defaultTo(initialRules.defaultRule);
     }
 
     /**

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -24,28 +24,17 @@
 package org.jboss.dna.search;
 
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Collector;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.PropertyType;
@@ -88,6 +77,7 @@
 import org.jboss.dna.graph.query.process.SelectComponent;
 import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
 import org.jboss.dna.search.DualIndexSearchProvider.ContentIndex;
+import org.jboss.dna.search.LuceneSession.TupleCollector;
 
 /**
  * 
@@ -210,7 +200,7 @@
         try {
             // Execute the query against the content indexes ...
             IndexSearcher searcher = session.getContentSearcher();
-            TupleCollector collector = new TupleCollector(columns, execContext.getValueFactories().getUuidFactory());
+            TupleCollector collector = session.createTupleCollector(columns);
             searcher.search(pushDownQuery, collector);
             tuples = collector.getTuples();
         } catch (IOException e) {
@@ -498,112 +488,4 @@
         assert false;
         return null;
     }
-
-    /**
-     * This collector is responsible for loading the value for each of the columns into each tuple array.
-     */
-    protected static class TupleCollector extends Collector {
-        private final LinkedList<Object[]> tuples = new LinkedList<Object[]>();
-        private final Columns columns;
-        private final int numValues;
-        private final boolean recordScore;
-        private final int scoreIndex;
-        private final FieldSelector fieldSelector;
-        private final int locationIndex;
-        private final ValueFactory<UUID> uuidFactory;
-        private Scorer scorer;
-        private IndexReader currentReader;
-        private int docOffset;
-
-        protected TupleCollector( Columns columns,
-                                  ValueFactory<UUID> uuidFactory ) {
-            this.columns = columns;
-            this.uuidFactory = uuidFactory;
-            assert this.columns != null;
-            assert this.uuidFactory != null;
-            this.numValues = this.columns.getTupleSize();
-            assert this.numValues >= 0;
-            assert this.columns.getSelectorNames().size() == 1;
-            final String selectorName = this.columns.getSelectorNames().get(0);
-            this.locationIndex = this.columns.getLocationIndex(selectorName);
-            this.recordScore = this.columns.hasFullTextSearchScores();
-            this.scoreIndex = this.recordScore ? this.columns.getFullTextSearchScoreIndexFor(selectorName) : -1;
-            final Set<String> columnNames = new HashSet<String>(this.columns.getColumnNames());
-            columnNames.add(ContentIndex.UUID); // add the UUID, which we'll put into the Location ...
-            this.fieldSelector = new FieldSelector() {
-                private static final long serialVersionUID = 1L;
-
-                public FieldSelectorResult accept( String fieldName ) {
-                    return columnNames.contains(fieldName) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
-                }
-            };
-        }
-
-        /**
-         * @return tuples
-         */
-        public LinkedList<Object[]> getTuples() {
-            return tuples;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#acceptsDocsOutOfOrder()
-         */
-        @Override
-        public boolean acceptsDocsOutOfOrder() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setNextReader(org.apache.lucene.index.IndexReader, int)
-         */
-        @Override
-        public void setNextReader( IndexReader reader,
-                                   int docBase ) {
-            this.currentReader = reader;
-            this.docOffset = docBase;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#setScorer(org.apache.lucene.search.Scorer)
-         */
-        @Override
-        public void setScorer( Scorer scorer ) {
-            this.scorer = scorer;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Collector#collect(int)
-         */
-        @Override
-        public void collect( int doc ) throws IOException {
-            int docId = doc + docOffset;
-            Object[] tuple = new Object[numValues];
-            Document document = currentReader.document(docId, fieldSelector);
-            for (String columnName : columns.getColumnNames()) {
-                int index = columns.getColumnIndexForName(columnName);
-                // We just need to retrieve the first value if there is more than one ...
-                tuple[index] = document.get(columnName);
-            }
-
-            // Set the score column if required ...
-            if (recordScore) {
-                assert scorer != null;
-                tuple[scoreIndex] = scorer.score();
-            }
-
-            // Load the UUID into a Location object ...
-            UUID uuid = uuidFactory.create(document.get(ContentIndex.UUID));
-            tuple[locationIndex] = Location.create(uuid);
-            tuples.add(tuple);
-        }
-    }
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneSession.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -26,16 +26,19 @@
 import java.io.IOException;
 import java.util.LinkedList;
 import java.util.Set;
-import java.util.UUID;
 import net.jcip.annotations.NotThreadSafe;
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.Collector;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.UuidFactory;
+import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryEngine;
@@ -76,6 +79,8 @@
     protected final ValueFactory<String> stringFactory;
     protected final DateTimeFactory dateFactory;
     protected final PathFactory pathFactory;
+    protected final UuidFactory uuidFactory;
+    protected final NameFactory nameFactory;
     private int changeCount;
     private QueryEngine queryEngine;
 
@@ -93,9 +98,12 @@
         this.overwrite = overwrite;
         this.readOnly = readOnly;
         this.analyzer = analyzer;
-        this.stringFactory = context.getValueFactories().getStringFactory();
-        this.dateFactory = context.getValueFactories().getDateFactory();
-        this.pathFactory = context.getValueFactories().getPathFactory();
+        ValueFactories factories = context.getValueFactories();
+        this.stringFactory = factories.getStringFactory();
+        this.dateFactory = factories.getDateFactory();
+        this.pathFactory = factories.getPathFactory();
+        this.uuidFactory = factories.getUuidFactory();
+        this.nameFactory = factories.getNameFactory();
         assert this.context != null;
         assert this.sourceName != null;
         assert this.workspaceName != null;
@@ -227,13 +235,21 @@
         throws IOException;
 
     /**
-     * Utility method to create a query to find all of the documents representing nodes with the supplied UUIDs.
+     * Create a {@link TupleCollector} instance that collects the results from the index(es).
      * 
-     * @param uuids the UUIDs of the nodes that are to be found; may not be null
+     * @param columns the column definitions; never null
+     * @return the collector; never null
+     */
+    public abstract TupleCollector createTupleCollector( Columns columns );
+
+    /**
+     * Utility method to create a query to find all of the documents representing nodes with the supplied IDs.
+     * 
+     * @param ids the IDs of the nodes that are to be found; may not be null
      * @return the query; never null
      * @throws IOException if there is a problem creating this query
      */
-    public abstract Query findAllNodesWithUuids( Set<UUID> uuids ) throws IOException;
+    public abstract Query findAllNodesWithIds( Set<String> ids ) throws IOException;
 
     public abstract Query findAllNodesBelow( Path ancestorPath ) throws IOException;
 
@@ -264,11 +280,13 @@
      * 
      * @param fieldName the name of the document field to search
      * @param likeExpression the JCR like expression
+     * @param caseSensitive true if the evaluation should be performed in a case sensitive manner, or false otherwise
      * @return the query; never null
      * @throws IOException if there is an error creating the query
      */
     public abstract Query findNodesLike( String fieldName,
-                                         String likeExpression ) throws IOException;
+                                         String likeExpression,
+                                         boolean caseSensitive ) throws IOException;
 
     public abstract Query findNodesWith( Length propertyLength,
                                          Operator operator,
@@ -320,49 +338,28 @@
 
     // public abstract Query createSnsIndexQuery( String likeExpression ) throws IOException;
 
-    /**
-     * Convert the JCR like expression to a Lucene wildcard expression. The JCR like expression uses '%' to match 0 or more
-     * characters, '_' to match any single character, '\x' to match the 'x' character, and all other characters to match
-     * themselves.
-     * 
-     * @param likeExpression the like expression; may not be null
-     * @return the expression that can be used with a WildcardQuery; never null
-     */
-    public String toWildcardExpression( String likeExpression ) {
-        assert likeExpression != null;
-        assert likeExpression.length() > 0;
-        return likeExpression.replace('%', '*').replace('_', '?').replaceAll("\\\\(.)", "$1");
-    }
-
-    /**
-     * Convert the JCR like expression to a regular expression. The JCR like expression uses '%' to match 0 or more characters,
-     * '_' to match any single character, '\x' to match the 'x' character, and all other characters to match themselves. Note that
-     * if any regex metacharacters appear in the like expression, they will be escaped within the resulting regular expression.
-     * 
-     * @param likeExpression the like expression; may not be null
-     * @return the expression that can be used with a WildcardQuery; never null
-     */
-    public String toRegularExpression( String likeExpression ) {
-        assert likeExpression != null;
-        assert likeExpression.length() > 0;
-        // Replace all '\x' with 'x' ...
-        String result = likeExpression.replaceAll("\\\\(.)", "$1");
-        // Escape characters used as metacharacters in regular expressions, including
-        // '[', '^', '\', '$', '.', '|', '?', '*', '+', '(', and ')'
-        result = result.replaceAll("([[^\\\\$.|?*+()])", "\\$1");
-        // Replace '%'->'[.]+' and '_'->'[.]
-        result = likeExpression.replace("%", "[.]+").replace("_", "[.]");
-        return result;
-    }
-
     public String pathAsString( Path path,
                                 ValueFactory<String> stringFactory ) {
         assert path != null;
         if (path.isRoot()) return "/";
-        String pathStr = stringFactory.create(path);
-        if (!pathStr.endsWith("]")) {
-            pathStr = pathStr + '[' + Path.DEFAULT_INDEX + ']';
+        StringBuilder sb = new StringBuilder();
+        for (Path.Segment segment : path) {
+            sb.append('/');
+            sb.append(stringFactory.create(segment.getName()));
+            sb.append('[');
+            sb.append(segment.getIndex());
+            sb.append(']');
         }
-        return pathStr;
+        return sb.toString();
     }
+
+    public static abstract class TupleCollector extends Collector {
+
+        /**
+         * Get the tuples.
+         * 
+         * @return the tuples; never null
+         */
+        public abstract LinkedList<Object[]> getTuples();
+    }
 }

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareQuery.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -96,7 +96,7 @@
      * @param fieldSelector the field selector that should load the fields needed to recover the value; may be null if the field
      *        selector should be generated automatically
      */
-    protected CompareQuery( String fieldName,
+    protected CompareQuery( final String fieldName,
                             ValueType constraintValue,
                             ValueFactory<ValueType> valueTypeFactory,
                             ValueFactory<String> stringFactory,
@@ -114,7 +114,7 @@
             private static final long serialVersionUID = 1L;
 
             public FieldSelectorResult accept( String fieldName ) {
-                return fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+                return CompareQuery.this.fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
             }
         };
     }
@@ -133,7 +133,7 @@
      */
     @Override
     public Weight createWeight( Searcher searcher ) {
-        return new NotWeight(searcher);
+        return new CompareWeight(searcher);
     }
 
     /**
@@ -149,11 +149,11 @@
     /**
      * Calculates query weights and builds query scores for our NOT queries.
      */
-    protected class NotWeight extends Weight {
+    protected class CompareWeight extends Weight {
         private static final long serialVersionUID = 1L;
         private final Searcher searcher;
 
-        protected NotWeight( Searcher searcher ) {
+        protected CompareWeight( Searcher searcher ) {
             this.searcher = searcher;
             assert this.searcher != null;
         }
@@ -217,7 +217,7 @@
                               boolean scoreDocsInOrder,
                               boolean topScorer ) {
             // Return a custom scorer ...
-            return new NotScorer(reader);
+            return new CompareScorer(reader);
         }
 
         /**
@@ -235,17 +235,17 @@
     /**
      * A scorer for the Path query.
      */
-    protected class NotScorer extends Scorer {
+    protected class CompareScorer extends Scorer {
         private int docId = -1;
         private final int maxDocId;
         private final IndexReader reader;
 
-        protected NotScorer( IndexReader reader ) {
+        protected CompareScorer( IndexReader reader ) {
             // We don't care which Similarity we have, because we don't use it. So get the default.
             super(Similarity.getDefault());
             this.reader = reader;
             assert this.reader != null;
-            this.maxDocId = this.reader.maxDoc();
+            this.maxDocId = this.reader.maxDoc() - 1;
         }
 
         /**

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/CompareStringQuery.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -24,10 +24,16 @@
 package org.jboss.dna.search.query;
 
 import java.io.IOException;
+import java.util.regex.Pattern;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.WildcardQuery;
+import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
+import org.apache.lucene.search.regex.RegexQuery;
 import org.jboss.dna.graph.property.ValueComparators;
 import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.ValueFactory;
@@ -42,6 +48,19 @@
 public class CompareStringQuery extends CompareQuery<String> {
 
     private static final long serialVersionUID = 1L;
+    protected static final Evaluator<String> EQUAL_TO = new Evaluator<String>() {
+        private static final long serialVersionUID = 1L;
+
+        public boolean satisfiesConstraint( String nodeValue,
+                                            String constraintValue ) {
+            return constraintValue.equals(nodeValue);
+        }
+
+        @Override
+        public String toString() {
+            return " = ";
+        }
+    };
     protected static final Evaluator<String> IS_LESS_THAN = new Evaluator<String>() {
         private static final long serialVersionUID = 1L;
 
@@ -96,6 +115,29 @@
     };
 
     /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is equal to the supplied
+     * constraint value.
+     * 
+     * @param constraintValue the constraint value; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static Query createQueryForNodesWithFieldEqualTo( String constraintValue,
+                                                             String fieldName,
+                                                             ValueFactories factories,
+                                                             boolean caseSensitive ) {
+        if (caseSensitive) {
+            // We can just do a normal TermQuery ...
+            return new TermQuery(new Term(fieldName, constraintValue));
+        }
+        return new CompareStringQuery(fieldName, constraintValue, factories.getStringFactory(), factories.getStringFactory(),
+                                      EQUAL_TO, caseSensitive);
+    }
+
+    /**
      * Construct a {@link Query} implementation that scores documents with a string field value that is greater than the supplied
      * constraint value.
      * 
@@ -171,6 +213,83 @@
                                       IS_LESS_THAN_OR_EQUAL_TO, caseSensitive);
     }
 
+    /**
+     * Construct a {@link Query} implementation that scores documents with a string field value that is LIKE the supplied
+     * constraint value.
+     * 
+     * @param likeExpression the LIKE expression; may not be null
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param factories the value factories that can be used during the scoring; may not be null
+     * @param caseSensitive true if the comparison should be done in a case-sensitive manner, or false if it is to be
+     *        case-insensitive
+     * @return the query; never null
+     */
+    public static Query createQueryForNodesWithFieldLike( String likeExpression,
+                                                          String fieldName,
+                                                          ValueFactories factories,
+                                                          boolean caseSensitive ) {
+        assert likeExpression != null;
+        assert likeExpression.length() > 0;
+        if (likeExpression.indexOf('%') == -1 && likeExpression.indexOf('_') == -1) {
+            // This is not a like expression, so just do an equals ...
+            return createQueryForNodesWithFieldEqualTo(likeExpression, fieldName, factories, caseSensitive);
+        }
+        if (caseSensitive) {
+            // We can just do a normal Wildcard or RegEx query ...
+
+            // '%' matches 0 or more characters
+            // '_' matches any single character
+            // '\x' matches 'x'
+            // all other characters match themselves
+
+            // Wildcard queries are a better match, but they can be slow and should not be used
+            // if the first character of the expression is a '%' or '_' ...
+            char firstChar = likeExpression.charAt(0);
+            if (firstChar != '%' && firstChar != '_') {
+                // Create a wildcard query ...
+                String expression = toWildcardExpression(likeExpression);
+                return new WildcardQuery(new Term(fieldName, expression));
+            }
+        }
+        // Create a regex query (which will be done using the correct case) ...
+        String regex = toRegularExpression(likeExpression);
+        RegexQuery query = new RegexQuery(new Term(fieldName, regex));
+        int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
+        query.setRegexImplementation(new JavaUtilRegexCapabilities(flags));
+        return query;
+    }
+
+    /**
+     * Convert the JCR like expression to a Lucene wildcard expression. The JCR like expression uses '%' to match 0 or more
+     * characters, '_' to match any single character, '\x' to match the 'x' character, and all other characters to match
+     * themselves.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    protected static String toWildcardExpression( String likeExpression ) {
+        return likeExpression.replace('%', '*').replace('_', '?').replaceAll("\\\\(.)", "$1");
+    }
+
+    /**
+     * Convert the JCR like expression to a regular expression. The JCR like expression uses '%' to match 0 or more characters,
+     * '_' to match any single character, '\x' to match the 'x' character, and all other characters to match themselves. Note that
+     * if any regex metacharacters appear in the like expression, they will be escaped within the resulting regular expression.
+     * 
+     * @param likeExpression the like expression; may not be null
+     * @return the expression that can be used with a WildcardQuery; never null
+     */
+    protected static String toRegularExpression( String likeExpression ) {
+        // Replace all '\x' with 'x' ...
+        String result = likeExpression.replaceAll("\\\\(.)", "$1");
+        // Escape characters used as metacharacters in regular expressions, including
+        // '[', '^', '\', '$', '.', '|', '?', '*', '+', '(', and ')'
+        result = result.replaceAll("([\\[^\\\\$.|?*+()])", "\\$1");
+        // Replace '%'->'[.]+' and '_'->'[.]
+        result = likeExpression.replace("%", ".+").replace("_", ".");
+        return result;
+    }
+
     private final boolean caseSensitive;
 
     /**
@@ -204,6 +323,7 @@
     protected String readFromDocument( IndexReader reader,
                                        int docId ) throws IOException {
         String result = super.readFromDocument(reader, docId);
+        if (result == null) return null;
         return caseSensitive ? result : result.toLowerCase();
     }
 

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/query/IdsQuery.java (from rev 1338, trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/IdsQuery.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/IdsQuery.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -0,0 +1,261 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.search.query;
+
+import java.io.IOException;
+import java.util.Set;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+
+/**
+ * A Lucene {@link Query} implementation that is used to score positively those documents that have a ID in the supplied set. This
+ * works for large sets of IDs; in smaller numbers, it may be more efficient to create a boolean query that checks for each of the
+ * IDs.
+ */
+public class IdsQuery extends Query {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The operand that is being negated by this query.
+     */
+    protected final Set<String> uuids;
+    protected final FieldSelector fieldSelector;
+    protected final String fieldName;
+
+    /**
+     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
+     * 
+     * @param fieldName the name of the document field containing the value; may not be null
+     * @param ids the set of ID values; may not be null
+     */
+    public IdsQuery( String fieldName,
+                     Set<String> ids ) {
+        this.fieldName = fieldName;
+        this.uuids = ids;
+        assert this.fieldName != null;
+        assert this.uuids != null;
+        this.fieldSelector = new FieldSelector() {
+            private static final long serialVersionUID = 1L;
+
+            public FieldSelectorResult accept( String fieldName ) {
+                return fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+            }
+        };
+    }
+
+    protected boolean includeDocument( IndexReader reader,
+                                       int docId ) throws IOException {
+        Document doc = reader.document(docId, fieldSelector);
+        String valueString = doc.get(fieldName);
+        return uuids.contains(valueString);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
+     */
+    @Override
+    public Weight createWeight( Searcher searcher ) {
+        return new IdSetWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.lucene.search.Query#toString(java.lang.String)
+     */
+    @Override
+    public String toString( String field ) {
+        return fieldName + " IN UUIDs";
+    }
+
+    /**
+     * Calculates query weights and builds query scores for our NOT queries.
+     */
+    protected class IdSetWeight extends Weight {
+        private static final long serialVersionUID = 1L;
+        private final Searcher searcher;
+
+        protected IdSetWeight( Searcher searcher ) {
+            this.searcher = searcher;
+            assert this.searcher != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#getQuery()
+         */
+        @Override
+        public Query getQuery() {
+            return IdsQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a weight factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#getValue()
+         */
+        @Override
+        public float getValue() {
+            return 1.0f; // weight factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always returns a normalization factor of 1.0.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
+         */
+        @Override
+        public float sumOfSquaredWeights() {
+            return 1.0f; // normalization factor of 1.0
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This implementation always does nothing, as there is nothing to normalize.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Weight#normalize(float)
+         */
+        @Override
+        public void normalize( float norm ) {
+            // No need to do anything here
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
+         */
+        @Override
+        public Scorer scorer( IndexReader reader,
+                              boolean scoreDocsInOrder,
+                              boolean topScorer ) {
+            // Return a custom scorer ...
+            return new IdScorer(reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
+         */
+        @Override
+        public Explanation explain( IndexReader reader,
+                                    int doc ) {
+            return new Explanation(getValue(), getQuery().toString());
+        }
+    }
+
+    /**
+     * A scorer for the Path query.
+     */
+    protected class IdScorer extends Scorer {
+        private int docId = -1;
+        private final int maxDocId;
+        private final IndexReader reader;
+
+        protected IdScorer( IndexReader reader ) {
+            // We don't care which Similarity we have, because we don't use it. So get the default.
+            super(Similarity.getDefault());
+            this.reader = reader;
+            assert this.reader != null;
+            this.maxDocId = this.reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#docID()
+         */
+        @Override
+        public int docID() {
+            return docId;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
+         */
+        @Override
+        public int nextDoc() throws IOException {
+            do {
+                ++docId;
+                if (reader.isDeleted(docId)) {
+                    // We should skip this document ...
+                    continue;
+                }
+                if (includeDocument(reader, docId)) return docId;
+            } while (docId < maxDocId);
+            return Scorer.NO_MORE_DOCS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
+         */
+        @Override
+        public int advance( int target ) throws IOException {
+            if (target == Scorer.NO_MORE_DOCS) return target;
+            while (true) {
+                int doc = nextDoc();
+                if (doc >= target) return doc;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p>
+         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
+         * scored by this scorer.
+         * </p>
+         * 
+         * @see org.apache.lucene.search.Scorer#score()
+         */
+        @Override
+        public float score() {
+            return 1.0f;
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/query/IdsQuery.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/query/UuidsQuery.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -1,268 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- * 
- * JBoss DNA 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.search.query;
-
-import java.io.IOException;
-import java.util.Set;
-import java.util.UUID;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Searcher;
-import org.apache.lucene.search.Similarity;
-import org.apache.lucene.search.Weight;
-import org.jboss.dna.graph.property.ValueFactory;
-
-/**
- * A Lucene {@link Query} implementation that is used to score positively those documents that have a UUID in the supplied set.
- * This works for large sets of UUIDs; in smaller numbers, it may be more efficient to create a boolean query that checks for each
- * fo the UUIDs.
- */
-public class UuidsQuery extends Query {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * The operand that is being negated by this query.
-     */
-    protected final Set<UUID> uuids;
-    protected final FieldSelector fieldSelector;
-    protected final String fieldName;
-    protected final ValueFactory<UUID> uuidFactory;
-
-    /**
-     * Construct a {@link Query} implementation that scores nodes according to the supplied comparator.
-     * 
-     * @param fieldName the name of the document field containing the value; may not be null
-     * @param uuids the set of UUID values; may not be null
-     * @param uuidFactory the factory to create UUID values; may not be null
-     */
-    public UuidsQuery( String fieldName,
-                       Set<UUID> uuids,
-                       ValueFactory<UUID> uuidFactory ) {
-        this.fieldName = fieldName;
-        this.uuids = uuids;
-        this.uuidFactory = uuidFactory;
-        assert this.fieldName != null;
-        assert this.uuids != null;
-        assert this.uuidFactory != null;
-        this.fieldSelector = new FieldSelector() {
-            private static final long serialVersionUID = 1L;
-
-            public FieldSelectorResult accept( String fieldName ) {
-                return fieldName.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
-            }
-        };
-    }
-
-    protected boolean includeDocument( IndexReader reader,
-                                       int docId ) throws IOException {
-        Document doc = reader.document(docId, fieldSelector);
-        String valueString = doc.get(fieldName);
-        return uuids.contains(uuidFactory.create(valueString));
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
-     */
-    @Override
-    public Weight createWeight( Searcher searcher ) {
-        return new UuidSetWeight(searcher);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.apache.lucene.search.Query#toString(java.lang.String)
-     */
-    @Override
-    public String toString( String field ) {
-        return fieldName + " IN UUIDs";
-    }
-
-    /**
-     * Calculates query weights and builds query scores for our NOT queries.
-     */
-    protected class UuidSetWeight extends Weight {
-        private static final long serialVersionUID = 1L;
-        private final Searcher searcher;
-
-        protected UuidSetWeight( Searcher searcher ) {
-            this.searcher = searcher;
-            assert this.searcher != null;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Weight#getQuery()
-         */
-        @Override
-        public Query getQuery() {
-            return UuidsQuery.this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * <p>
-         * This implementation always returns a weight factor of 1.0.
-         * </p>
-         * 
-         * @see org.apache.lucene.search.Weight#getValue()
-         */
-        @Override
-        public float getValue() {
-            return 1.0f; // weight factor of 1.0
-        }
-
-        /**
-         * {@inheritDoc}
-         * <p>
-         * This implementation always returns a normalization factor of 1.0.
-         * </p>
-         * 
-         * @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
-         */
-        @Override
-        public float sumOfSquaredWeights() {
-            return 1.0f; // normalization factor of 1.0
-        }
-
-        /**
-         * {@inheritDoc}
-         * <p>
-         * This implementation always does nothing, as there is nothing to normalize.
-         * </p>
-         * 
-         * @see org.apache.lucene.search.Weight#normalize(float)
-         */
-        @Override
-        public void normalize( float norm ) {
-            // No need to do anything here
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader, boolean, boolean)
-         */
-        @Override
-        public Scorer scorer( IndexReader reader,
-                              boolean scoreDocsInOrder,
-                              boolean topScorer ) {
-            // Return a custom scorer ...
-            return new UuidScorer(reader);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader, int)
-         */
-        @Override
-        public Explanation explain( IndexReader reader,
-                                    int doc ) {
-            return new Explanation(getValue(), getQuery().toString());
-        }
-    }
-
-    /**
-     * A scorer for the Path query.
-     */
-    protected class UuidScorer extends Scorer {
-        private int docId = -1;
-        private final int maxDocId;
-        private final IndexReader reader;
-
-        protected UuidScorer( IndexReader reader ) {
-            // We don't care which Similarity we have, because we don't use it. So get the default.
-            super(Similarity.getDefault());
-            this.reader = reader;
-            assert this.reader != null;
-            this.maxDocId = this.reader.maxDoc();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.DocIdSetIterator#docID()
-         */
-        @Override
-        public int docID() {
-            return docId;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.DocIdSetIterator#nextDoc()
-         */
-        @Override
-        public int nextDoc() throws IOException {
-            do {
-                ++docId;
-                if (reader.isDeleted(docId)) {
-                    // We should skip this document ...
-                    continue;
-                }
-                if (includeDocument(reader, docId)) return docId;
-            } while (docId < maxDocId);
-            return Scorer.NO_MORE_DOCS;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.lucene.search.DocIdSetIterator#advance(int)
-         */
-        @Override
-        public int advance( int target ) throws IOException {
-            if (target == Scorer.NO_MORE_DOCS) return target;
-            while (true) {
-                int doc = nextDoc();
-                if (doc >= target) return doc;
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * <p>
-         * This method always returns a score of 1.0 for the current document, since only those documents that satisfy the NOT are
-         * scored by this scorer.
-         * </p>
-         * 
-         * @see org.apache.lucene.search.Scorer#score()
-         */
-        @Override
-        public float score() {
-            return 1.0f;
-        }
-    }
-}

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 17:43:50 UTC (rev 1338)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 17:44:29 UTC (rev 1339)
@@ -26,6 +26,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.util.List;
 import org.jboss.dna.graph.ExecutionContext;
@@ -35,10 +36,18 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder;
 import org.jboss.dna.graph.search.SearchEngine;
 import org.jboss.dna.graph.search.SearchProvider;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.xml.sax.SAXException;
 
@@ -53,6 +62,8 @@
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
     private Graph content;
+    private Schemata schemata;
+    private SqlQueryParser sql;
 
     @Before
     public void beforeEach() throws Exception {
@@ -79,14 +90,32 @@
         };
 
         // Set up the provider and the search engine ...
-        IndexRules rules = DualIndexSearchProvider.DEFAULT_RULES;
+        IndexRules.Builder rulesBuilder = IndexRules.createBuilder(DualIndexSearchProvider.DEFAULT_RULES);
+        // rulesBuilder.analyzeAndStoreAndFullText(name("maker"));
+        IndexRules rules = rulesBuilder.build();
         LuceneConfiguration luceneConfig = LuceneConfigurations.inMemory();
         // LuceneConfiguration luceneConfig = LuceneConfigurations.using(new File("target/testIndexes"));
         provider = new DualIndexSearchProvider(luceneConfig, rules);
         engine = new SearchEngine(context, sourceName, connectionFactory, provider);
         loadContent();
+
+        // Create the schemata for the workspaces ...
+        Builder builder = ImmutableSchemata.createBuilder(context);
+        builder.addTable("__ALLNODES__", "maker", "model", "year");
+        schemata = builder.build();
+        schemata = ImmutableSchemata.createBuilder(context)
+                                    .addTable("__ALLNODES__", "maker", "model", "year", "msrp")
+                                    .makeSearchable("__ALLNODES__", "maker")
+                                    .build();
+
+        // And create the SQL parser ...
+        sql = new SqlQueryParser();
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
     protected Path path( String path ) {
         return context.getValueFactories().getPathFactory().create(path);
     }
@@ -167,6 +196,10 @@
         engine.index(workspaceName1, path("/Cars"), 10);
     }
 
+    // ----------------------------------------------------------------------------------------------------------------
+    // Full-text search
+    // ----------------------------------------------------------------------------------------------------------------
+
     @Test
     public void shouldFindNodesByFullTextSearch() {
         engine.index(workspaceName1, path("/"), 100);
@@ -190,4 +223,188 @@
         assertThat(results.size(), is(1));
         assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
     }
+
+    @Test
+    public void shouldFindNodesBySimpleXpathQuery() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(18));
+        System.out.println(results);
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Query
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldFindNodesBySimpleQuery() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(18));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithEqualityComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker = 'Toyota'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Ignore
+    @Test
+    public void shouldFindNodesBySimpleQueryWithGreaterThanComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE mpgHighway > 20", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithLowercaseEqualityComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) = 'toyota'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithUppercaseEqualityComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE UPPER(maker) = 'TOYOTA'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithLikeComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE 'Toyo%'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithLikeComparisonCriteriaWithLeadingWildcard() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE '%yota'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithLowercaseLikeComparisonCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) LIKE 'toyo%'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(2));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithFullTextSearchCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE CONTAINS(maker,'martin')", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(1));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithDepthCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE DEPTH() > 2", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(12));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithLocalNameCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOCALNAME() LIKE 'Toyota%' OR LOCALNAME() LIKE 'Land %'",
+                                            context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(4));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithNameCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE NAME() LIKE 'Toyota%[1]' OR NAME() LIKE 'Land %'",
+                                            context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(4));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithNameCriteriaThatMatchesNoNodes() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE NAME() LIKE 'Toyota%[2]'", context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(0));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithPathCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE PATH() LIKE '/Cars[%]/Hy%/Toyota%' OR PATH() LIKE '/Cars[1]/Utility[1]/%'",
+                                            context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(6));
+        System.out.println(results);
+    }
+
+    @Test
+    public void shouldFindNodesBySimpleQueryWithDescendantCriteria() {
+        engine.index(workspaceName1, path("/"), 100);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE ISDESCENDANTNODE('/Cars/Hybrid')",
+                                            context);
+        QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
+        assertThat(results, is(notNullValue()));
+        assertThat(results.getRowCount(), is(3));
+        System.out.println(results);
+    }
+
+    protected void assertNoErrors( QueryResults results ) {
+        if (results.getProblems().hasErrors()) {
+            fail("Found errors: " + results.getProblems());
+        }
+        assertThat(results.getProblems().hasErrors(), is(false));
+    }
+
 }


From dna-commits at lists.jboss.org  Tue Nov 24 12:46:39 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 12:46:39 -0500
Subject: [dna-commits] DNA SVN: r1340 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property/basic
	and 23 other directories.
Message-ID: <200911241746.nAOHkd6T023967@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-24 12:46:37 -0500 (Tue, 24 Nov 2009)
New Revision: 1340

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueTypeSystem.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/TypeSystem.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/DelegatingValueFactories.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueFactories.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java
   trunk/dna-jcr-tck/pom.xml
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Log:
DNA-467 Refactored the query functionality to minimize dependencies on the rest of 'dna-graph', meaning that we can probably extract it and that it would be useful for other projects. This impacted a fair bit of code, but it does clean up the assumptions and dependencies of the query engine.  Search is still not very decoupled.  But everything still works and all unit tests pass.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueFactories.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueFactories.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueFactories.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,6 +26,7 @@
 import java.math.BigDecimal;
 import java.net.URI;
 import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.query.model.TypeSystem;
 
 /**
  * The set of standard {@link ValueFactory} instances.
@@ -34,6 +35,13 @@
 public interface ValueFactories extends Iterable<ValueFactory<?>> {
 
     /**
+     * Get the type system associated with these factories.
+     * 
+     * @return the type system; never null
+     */
+    TypeSystem getTypeSystem();
+
+    /**
      * Get the value factory that creates values of the supplied {@link PropertyType type}.
      * 
      * @param type the type for the values

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueTypeSystem.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueTypeSystem.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueTypeSystem.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -0,0 +1,353 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.property;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.common.util.Base64;
+import org.jboss.dna.graph.query.model.TypeSystem;
+
+/**
+ * 
+ */
+public class ValueTypeSystem implements TypeSystem {
+
+    private final String defaultTypeName;
+    protected final ValueFactories valueFactories;
+    protected final ValueFactory<String> stringValueFactory;
+    private final Map<PropertyType, TypeFactory<?>> typeFactoriesByPropertyType;
+    private final Map<String, TypeFactory<?>> typeFactoriesByName;
+    private final TypeFactory<String> stringFactory;
+    private final TypeFactory<Boolean> booleanFactory;
+    private final TypeFactory<Long> longFactory;
+    private final TypeFactory<Double> doubleFactory;
+    private final TypeFactory<?> dateFactory;
+    private final TypeFactory<?> pathFactory;
+
+    /**
+     * Create a type system using the supplied value factories.
+     * 
+     * @param valueFactories the value factories;
+     * @throws IllegalArgumentException if the value factories are null
+     */
+    public ValueTypeSystem( ValueFactories valueFactories ) {
+        this.valueFactories = valueFactories;
+        this.defaultTypeName = PropertyType.STRING.getName().toUpperCase();
+        Map<PropertyType, TypeFactory<?>> factories = new HashMap<PropertyType, TypeFactory<?>>();
+        this.stringValueFactory = valueFactories.getStringFactory();
+        this.stringFactory = new Factory<String>(stringValueFactory) {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.property.ValueTypeSystem.Factory#asString(java.lang.Object)
+             */
+            @Override
+            public String asString( Object value ) {
+                return stringValueFactory.create(value);
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.property.ValueTypeSystem.Factory#asReadableString(java.lang.Object)
+             */
+            @Override
+            public String asReadableString( Object value ) {
+                return stringValueFactory.create(value);
+            }
+        };
+        this.booleanFactory = new Factory<Boolean>(valueFactories.getBooleanFactory());
+        this.longFactory = new Factory<Long>(valueFactories.getLongFactory());
+        this.doubleFactory = new Factory<Double>(valueFactories.getDoubleFactory());
+        this.dateFactory = new Factory<DateTime>(valueFactories.getDateFactory()) {
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.property.ValueTypeSystem.Factory#create(java.lang.String)
+             */
+            @Override
+            public DateTime create( String value ) throws ValueFormatException {
+                DateTime result = valueFactory.create(value);
+                // Convert the timestamp to UTC, since that's how everything should be queried ...
+                return result.toUtcTimeZone();
+            }
+        };
+        this.pathFactory = new Factory<Path>(valueFactories.getPathFactory());
+        factories.put(PropertyType.STRING, this.stringFactory);
+        factories.put(PropertyType.BOOLEAN, this.booleanFactory);
+        factories.put(PropertyType.DATE, this.dateFactory);
+        factories.put(PropertyType.DECIMAL, new Factory<BigDecimal>(valueFactories.getDecimalFactory()));
+        factories.put(PropertyType.DOUBLE, this.doubleFactory);
+        factories.put(PropertyType.LONG, this.longFactory);
+        factories.put(PropertyType.NAME, new Factory<Name>(valueFactories.getNameFactory()));
+        factories.put(PropertyType.OBJECT, new Factory<Object>(valueFactories.getObjectFactory()));
+        factories.put(PropertyType.PATH, this.pathFactory);
+        factories.put(PropertyType.REFERENCE, new Factory<Reference>(valueFactories.getReferenceFactory()));
+        factories.put(PropertyType.URI, new Factory<URI>(valueFactories.getUriFactory()));
+        factories.put(PropertyType.UUID, new Factory<UUID>(valueFactories.getUuidFactory()));
+        factories.put(PropertyType.BINARY, new Factory<Binary>(valueFactories.getBinaryFactory()) {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.property.ValueTypeSystem.Factory#asReadableString(java.lang.Object)
+             */
+            @Override
+            public String asReadableString( Object value ) {
+                Binary binary = this.valueFactory.create(value);
+                // Just print out the SHA-1 hash in Base64, plus length
+                return "(Binary,length=" + binary.getSize() + ",SHA1=" + Base64.encodeBytes(binary.getHash()) + ")";
+            }
+
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.property.ValueTypeSystem.Factory#length(java.lang.Object)
+             */
+            @Override
+            public long length( Object value ) {
+                Binary binary = this.valueFactory.create(value);
+                return binary != null ? binary.getSize() : 0;
+            }
+        });
+        this.typeFactoriesByPropertyType = Collections.unmodifiableMap(factories);
+        Map<String, TypeFactory<?>> byName = new HashMap<String, TypeFactory<?>>();
+        for (TypeFactory<?> factory : factories.values()) {
+            byName.put(factory.getTypeName(), factory);
+        }
+        this.typeFactoriesByName = Collections.unmodifiableMap(byName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#asString(java.lang.Object)
+     */
+    public String asString( Object value ) {
+        return stringValueFactory.create(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getBooleanFactory()
+     */
+    public TypeFactory<Boolean> getBooleanFactory() {
+        return booleanFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getStringFactory()
+     */
+    public TypeFactory<String> getStringFactory() {
+        return this.stringFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getDateTimeFactory()
+     */
+    public TypeFactory<?> getDateTimeFactory() {
+        return dateFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getDefaultType()
+     */
+    public String getDefaultType() {
+        return defaultTypeName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getDefaultComparator()
+     */
+    @SuppressWarnings( "unchecked" )
+    public Comparator<Object> getDefaultComparator() {
+        return (Comparator<Object>)PropertyType.OBJECT.getComparator();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getDoubleFactory()
+     */
+    public TypeFactory<Double> getDoubleFactory() {
+        return doubleFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getLongFactory()
+     */
+    public TypeFactory<Long> getLongFactory() {
+        return longFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getPathFactory()
+     */
+    public TypeFactory<?> getPathFactory() {
+        return pathFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getTypeFactory(java.lang.String)
+     */
+    public TypeFactory<?> getTypeFactory( String typeName ) {
+        if (typeName == null) return null;
+        return typeFactoriesByName.get(typeName.toUpperCase()); // may be null
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getTypeFactory(java.lang.Object)
+     */
+    public TypeFactory<?> getTypeFactory( Object prototype ) {
+        ValueFactory<?> valueFactory = valueFactories.getValueFactory(prototype);
+        if (valueFactory == null) return null;
+        PropertyType type = valueFactory.getPropertyType();
+        assert type != null;
+        return typeFactoriesByPropertyType.get(type);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.TypeSystem#getTypeNames()
+     */
+    public Set<String> getTypeNames() {
+        return typeFactoriesByName.keySet();
+    }
+
+    protected class Factory<T> implements TypeFactory<T> {
+        protected final PropertyType type;
+        protected final ValueFactory<T> valueFactory;
+        protected final String typeName;
+
+        protected Factory( ValueFactory<T> valueFactory ) {
+            this.valueFactory = valueFactory;
+            this.type = this.valueFactory.getPropertyType();
+            this.typeName = type.getName().toUpperCase();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#asReadableString(java.lang.Object)
+         */
+        public String asReadableString( Object value ) {
+            return asString(value);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#asString(java.lang.Object)
+         */
+        public String asString( Object value ) {
+            if (value instanceof String) {
+                // Convert to the typed value, then back to a string ...
+                value = valueFactory.create((String)value);
+            }
+            return stringValueFactory.create(value);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#create(java.lang.String)
+         */
+        public T create( String value ) throws ValueFormatException {
+            return valueFactory.create(value);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#create(java.lang.Object)
+         */
+        public T create( Object value ) throws ValueFormatException {
+            return valueFactory.create(value);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#getType()
+         */
+        @SuppressWarnings( "unchecked" )
+        public Class<T> getType() {
+            return (Class<T>)type.getValueClass();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#length(java.lang.Object)
+         */
+        public long length( Object value ) {
+            String str = asString(valueFactory.create(value));
+            return str != null ? str.length() : 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#getComparator()
+         */
+        @SuppressWarnings( "unchecked" )
+        public Comparator<T> getComparator() {
+            return (Comparator<T>)type.getComparator();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.TypeSystem.TypeFactory#getTypeName()
+         */
+        public String getTypeName() {
+            return typeName;
+        }
+
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/ValueTypeSystem.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/DelegatingValueFactories.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/DelegatingValueFactories.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/DelegatingValueFactories.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -1,103 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors. 
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- *
- * JBoss DNA 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.graph.property.basic;
-
-import java.math.BigDecimal;
-import java.net.URI;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.BinaryFactory;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.property.Reference;
-import org.jboss.dna.graph.property.UuidFactory;
-import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFactory;
-
-/**
- * A {@link ValueFactories} implementation that delegates to another instance, and that is often useful for subclassing.
- */
- at Immutable
-public class DelegatingValueFactories extends AbstractValueFactories {
-
-    private final ValueFactories delegate;
-
-    protected DelegatingValueFactories( ValueFactories delegate ) {
-        assert delegate != null;
-        this.delegate = delegate;
-    }
-
-    public BinaryFactory getBinaryFactory() {
-        return delegate.getBinaryFactory();
-    }
-
-    public ValueFactory<Boolean> getBooleanFactory() {
-        return delegate.getBooleanFactory();
-    }
-
-    public DateTimeFactory getDateFactory() {
-        return delegate.getDateFactory();
-    }
-
-    public ValueFactory<BigDecimal> getDecimalFactory() {
-        return delegate.getDecimalFactory();
-    }
-
-    public ValueFactory<Double> getDoubleFactory() {
-        return delegate.getDoubleFactory();
-    }
-
-    public ValueFactory<Long> getLongFactory() {
-        return delegate.getLongFactory();
-    }
-
-    public NameFactory getNameFactory() {
-        return delegate.getNameFactory();
-    }
-
-    public ValueFactory<Object> getObjectFactory() {
-        return delegate.getObjectFactory();
-    }
-
-    public PathFactory getPathFactory() {
-        return delegate.getPathFactory();
-    }
-
-    public ValueFactory<Reference> getReferenceFactory() {
-        return delegate.getReferenceFactory();
-    }
-
-    public ValueFactory<String> getStringFactory() {
-        return delegate.getStringFactory();
-    }
-
-    public ValueFactory<URI> getUriFactory() {
-        return delegate.getUriFactory();
-    }
-
-    public UuidFactory getUuidFactory() {
-        return delegate.getUuidFactory();
-    }
-
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -40,6 +40,8 @@
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.UuidFactory;
 import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueTypeSystem;
+import org.jboss.dna.graph.query.model.TypeSystem;
 
 /**
  * The standard set of {@link ValueFactory value factories}.
@@ -66,6 +68,8 @@
     private final TextDecoder decoder;
     private final TextEncoder encoder;
 
+    private final TypeSystem typeSystem;
+
     /**
      * Create a standard set of value factories, using the {@link ValueFactory#DEFAULT_DECODER default decoder}.
      * 
@@ -122,6 +126,8 @@
         this.uuidFactory = (UuidFactory)getFactory(factories, new UuidValueFactory(this.decoder, this.stringFactory));
         this.uriFactory = getFactory(factories, new UriValueFactory(this.namespaceRegistry, this.decoder, this.stringFactory));
         this.objectFactory = getFactory(factories, new ObjectValueFactory(this.decoder, this.stringFactory, this.binaryFactory));
+
+        this.typeSystem = new ValueTypeSystem(this);
     }
 
     @SuppressWarnings( "unchecked" )
@@ -137,6 +143,15 @@
     }
 
     /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.property.ValueFactories#getTypeSystem()
+     */
+    public TypeSystem getTypeSystem() {
+        return typeSystem;
+    }
+
+    /**
      * @return decoder
      */
     public TextDecoder getTextDecoder() {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -33,15 +33,12 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.Between;
@@ -85,6 +82,7 @@
 import org.jboss.dna.graph.query.model.SetQuery;
 import org.jboss.dna.graph.query.model.Source;
 import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.model.SetQuery.Operation;
@@ -232,7 +230,7 @@
 @NotThreadSafe
 public class QueryBuilder {
 
-    protected final ExecutionContext context;
+    protected final TypeSystem typeSystem;
     protected Source source = new AllNodes();
     protected Constraint constraint;
     protected List<Column> columns = new LinkedList<Column>();
@@ -249,9 +247,9 @@
      * @param context the execution context
      * @throws IllegalArgumentException if the context is null
      */
-    public QueryBuilder( ExecutionContext context ) {
+    public QueryBuilder( TypeSystem context ) {
         CheckArg.isNotNull(context, "context");
-        this.context = context;
+        this.typeSystem = context;
     }
 
     /**
@@ -312,38 +310,6 @@
     }
 
     /**
-     * Convenience method that creates a {@link Name} object given the supplied string. Leading and trailing whitespace are
-     * trimmed.
-     * 
-     * @param name the name; may not be null
-     * @return the name; never null
-     * @throws IllegalArgumentException if the supplied name is not a valid {@link Name} object
-     */
-    protected Name name( String name ) {
-        try {
-            return context.getValueFactories().getNameFactory().create(name.trim());
-        } catch (ValueFormatException e) {
-            throw new IllegalArgumentException(GraphI18n.expectingValidName.text(name));
-        }
-    }
-
-    /**
-     * Convenience method that creates a {@link Path} object given the supplied string. Leading and trailing whitespace are
-     * trimmed.
-     * 
-     * @param path the path; may not be null
-     * @return the path; never null
-     * @throws IllegalArgumentException if the supplied string is not a valid {@link Path} object
-     */
-    protected Path path( String path ) {
-        try {
-            return context.getValueFactories().getPathFactory().create(path.trim());
-        } catch (ValueFormatException e) {
-            throw new IllegalArgumentException(GraphI18n.expectingValidPath.text(path));
-        }
-    }
-
-    /**
      * Create a {@link Column} given the supplied expression. The expression has the form "<code>[tableName.]columnName</code>",
      * where "<code>tableName</code>" must be a valid table name or alias. If the table name/alias is not specified, then there is
      * expected to be a single FROM clause with a single named selector.
@@ -358,17 +324,17 @@
             parts[i] = parts[i].trim();
         }
         SelectorName name = null;
-        Name propertyName = null;
+        String propertyName = null;
         String columnName = null;
         if (parts.length == 2) {
             name = selector(parts[0]);
-            propertyName = name(parts[1]);
+            propertyName = parts[1];
             columnName = parts[1];
         } else {
             if (source instanceof Selector) {
                 Selector selector = (Selector)source;
                 name = selector.hasAlias() ? selector.getAlias() : selector.getName();
-                propertyName = name(parts[0]);
+                propertyName = parts[0];
                 columnName = parts[0];
             } else {
                 throw new IllegalArgumentException(GraphI18n.columnMustBeScoped.text(parts[0]));
@@ -1087,7 +1053,7 @@
          */
         public ConstraintBuilder isSameNode( String table,
                                              String asNodeAtPath ) {
-            return setConstraint(new SameNode(selector(table), QueryBuilder.this.path(asNodeAtPath)));
+            return setConstraint(new SameNode(selector(table), asNodeAtPath));
         }
 
         /**
@@ -1100,7 +1066,7 @@
          */
         public ConstraintBuilder isChild( String childTable,
                                           String parentPath ) {
-            return setConstraint(new ChildNode(selector(childTable), QueryBuilder.this.path(parentPath)));
+            return setConstraint(new ChildNode(selector(childTable), parentPath));
         }
 
         /**
@@ -1113,7 +1079,7 @@
          */
         public ConstraintBuilder isBelowPath( String descendantTable,
                                               String ancestorPath ) {
-            return setConstraint(new DescendantNode(selector(descendantTable), QueryBuilder.this.path(ancestorPath)));
+            return setConstraint(new DescendantNode(selector(descendantTable), ancestorPath));
         }
 
         /**
@@ -1126,7 +1092,7 @@
          */
         public ConstraintBuilder hasProperty( String table,
                                               String propertyName ) {
-            return setConstraint(new PropertyExistence(selector(table), name(propertyName)));
+            return setConstraint(new PropertyExistence(selector(table), propertyName));
         }
 
         /**
@@ -1156,7 +1122,7 @@
         public ConstraintBuilder search( String table,
                                          String propertyName,
                                          String searchExpression ) {
-            return setConstraint(new FullTextSearch(selector(table), name(propertyName), searchExpression));
+            return setConstraint(new FullTextSearch(selector(table), propertyName, searchExpression));
         }
 
         /**
@@ -1166,7 +1132,7 @@
          */
         public ComparisonBuilder length( String table,
                                          String property ) {
-            return new ComparisonBuilder(this, new Length(new PropertyValue(selector(table), name(property))));
+            return new ComparisonBuilder(this, new Length(new PropertyValue(selector(table), property)));
         }
 
         /**
@@ -1176,7 +1142,7 @@
          */
         public ComparisonBuilder propertyValue( String table,
                                                 String property ) {
-            return new ComparisonBuilder(this, new PropertyValue(selector(table), name(property)));
+            return new ComparisonBuilder(this, new PropertyValue(selector(table), property));
         }
 
         /**
@@ -1304,7 +1270,7 @@
          * @param type the property type; may not be null
          * @return the constraint builder; never null
          */
-        public abstract ReturnType as( PropertyType type );
+        public abstract ReturnType as( String type );
 
         /**
          * Define the right-hand side literal value cast as a {@link PropertyType#STRING}.
@@ -1312,7 +1278,7 @@
          * @return the constraint builder; never null
          */
         public ReturnType asString() {
-            return as(PropertyType.STRING);
+            return as(typeSystem.getStringFactory().getTypeName());
         }
 
         /**
@@ -1321,7 +1287,7 @@
          * @return the constraint builder; never null
          */
         public ReturnType asBoolean() {
-            return as(PropertyType.BOOLEAN);
+            return as(typeSystem.getBooleanFactory().getTypeName());
         }
 
         /**
@@ -1330,7 +1296,7 @@
          * @return the constraint builder; never null
          */
         public ReturnType asLong() {
-            return as(PropertyType.LONG);
+            return as(typeSystem.getLongFactory().getTypeName());
         }
 
         /**
@@ -1339,80 +1305,26 @@
          * @return the constraint builder; never null
          */
         public ReturnType asDouble() {
-            return as(PropertyType.DOUBLE);
+            return as(typeSystem.getDoubleFactory().getTypeName());
         }
 
         /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#DECIMAL}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asDecimal() {
-            return as(PropertyType.DECIMAL);
-        }
-
-        /**
          * Define the right-hand side literal value cast as a {@link PropertyType#DATE}.
          * 
          * @return the constraint builder; never null
          */
         public ReturnType asDate() {
-            return as(PropertyType.DATE);
+            return as(typeSystem.getDateTimeFactory().getTypeName());
         }
 
         /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#NAME}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asName() {
-            return as(PropertyType.NAME);
-        }
-
-        /**
          * Define the right-hand side literal value cast as a {@link PropertyType#PATH}.
          * 
          * @return the constraint builder; never null
          */
         public ReturnType asPath() {
-            return as(PropertyType.PATH);
+            return as(typeSystem.getPathFactory().getTypeName());
         }
-
-        /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#BINARY}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asBinary() {
-            return as(PropertyType.BINARY);
-        }
-
-        /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#REFERENCE}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asReference() {
-            return as(PropertyType.REFERENCE);
-        }
-
-        /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#URI}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asUri() {
-            return as(PropertyType.URI);
-        }
-
-        /**
-         * Define the right-hand side literal value cast as a {@link PropertyType#UUID}.
-         * 
-         * @return the constraint builder; never null
-         */
-        public ReturnType asUuid() {
-            return as(PropertyType.UUID);
-        }
     }
 
     public class CastAsRightHandSide extends CastAs<ConstraintBuilder> {
@@ -1424,10 +1336,6 @@
             this.rhs = rhs;
         }
 
-        private ValueFactories factories() {
-            return QueryBuilder.this.context.getValueFactories();
-        }
-
         /**
          * Define the right-hand side literal value cast as the specified type.
          * 
@@ -1435,8 +1343,8 @@
          * @return the constraint builder; never null
          */
         @Override
-        public ConstraintBuilder as( PropertyType type ) {
-            return rhs.comparisonBuilder.is(rhs.operator, factories().getValueFactory(type).create(value));
+        public ConstraintBuilder as( String type ) {
+            return rhs.comparisonBuilder.is(rhs.operator, typeSystem.getTypeFactory(type).create(value));
         }
     }
 
@@ -1449,10 +1357,6 @@
             this.upperBoundary = upperBoundary;
         }
 
-        private ValueFactories factories() {
-            return QueryBuilder.this.context.getValueFactories();
-        }
-
         /**
          * Define the right-hand side literal value cast as the specified type.
          * 
@@ -1460,9 +1364,8 @@
          * @return the constraint builder; never null
          */
         @Override
-        public ConstraintBuilder as( PropertyType type ) {
-            return upperBoundary.comparisonBuilder.isBetween(upperBoundary.lowerBound, factories().getValueFactory(type)
-                                                                                                  .create(value));
+        public ConstraintBuilder as( String type ) {
+            return upperBoundary.comparisonBuilder.isBetween(upperBoundary.lowerBound, typeSystem.getTypeFactory(type).create(value));
         }
     }
 
@@ -1475,10 +1378,6 @@
             this.builder = builder;
         }
 
-        private ValueFactories factories() {
-            return QueryBuilder.this.context.getValueFactories();
-        }
-
         /**
          * Define the left-hand side literal value cast as the specified type.
          * 
@@ -1486,8 +1385,8 @@
          * @return the builder to complete the constraint; never null
          */
         @Override
-        public AndBuilder<UpperBoundary> as( PropertyType type ) {
-            Object literal = factories().getValueFactory(type).create(value);
+        public AndBuilder<UpperBoundary> as( String type ) {
+            Object literal = typeSystem.getTypeFactory(type).create(value);
             return new AndBuilder<UpperBoundary>(new UpperBoundary(builder, new Literal(literal)));
         }
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,8 +30,8 @@
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.common.collection.SimpleProblems;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.BindVariableName;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.validate.Schemata;
 
@@ -41,7 +41,7 @@
  */
 @Immutable
 public class QueryContext {
-    private final ExecutionContext context;
+    private final TypeSystem typeSystem;
     private final PlanHints hints;
     private final Schemata schemata;
     private final Problems problems;
@@ -50,26 +50,26 @@
     /**
      * Create a new context for query execution.
      * 
-     * @param context the execution context
      * @param schemata the schemata
+     * @param typeSystem the types system
      * @param hints the hints, or null if there are no hints
      * @param problems the problems container, or null if a new problems container should be created
      * @param variables the mapping of variables and values, or null if there are no such variables
-     * @throws IllegalArgumentException if the context or schmata are null
+     * @throws IllegalArgumentException if the values or schmata are null
      */
-    public QueryContext( ExecutionContext context,
-                         Schemata schemata,
+    public QueryContext( Schemata schemata,
+                         TypeSystem typeSystem,
                          PlanHints hints,
                          Problems problems,
                          Map<String, Object> variables ) {
-        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(typeSystem, "typeSystem");
         CheckArg.isNotNull(schemata, "schemata");
-        this.context = context;
+        this.typeSystem = typeSystem;
         this.hints = hints != null ? hints : new PlanHints();
         this.schemata = schemata;
         this.problems = problems != null ? problems : new SimpleProblems();
         this.variables = variables != null ? Collections.<String, Object>unmodifiableMap(new HashMap<String, Object>(variables)) : Collections.<String, Object>emptyMap();
-        assert this.context != null;
+        assert this.typeSystem != null;
         assert this.hints != null;
         assert this.schemata != null;
         assert this.problems != null;
@@ -79,52 +79,52 @@
     /**
      * Create a new context for query execution.
      * 
-     * @param context the execution context
      * @param schemata the schemata
+     * @param typeSystem the types system
      * @param hints the hints, or null if there are no hints
      * @param problems the problems container, or null if a new problems container should be created
-     * @throws IllegalArgumentException if the context or schmata are null
+     * @throws IllegalArgumentException if the values or schmata are null
      */
-    public QueryContext( ExecutionContext context,
-                         Schemata schemata,
+    public QueryContext( Schemata schemata,
+                         TypeSystem typeSystem,
                          PlanHints hints,
                          Problems problems ) {
-        this(context, schemata, hints, problems, null);
+        this(schemata, typeSystem, hints, problems, null);
     }
 
     /**
      * Create a new context for query execution.
      * 
-     * @param context the execution context
      * @param schemata the schemata
+     * @param typeSystem the types system
      * @param hints the hints, or null if there are no hints
      * @throws IllegalArgumentException if the context or schmata are null
      */
-    public QueryContext( ExecutionContext context,
-                         Schemata schemata,
+    public QueryContext( Schemata schemata,
+                         TypeSystem typeSystem,
                          PlanHints hints ) {
-        this(context, schemata, hints, null, null);
+        this(schemata, typeSystem, hints, null, null);
     }
 
     /**
      * Create a new context for query execution.
      * 
-     * @param context the execution context
      * @param schemata the schemata
-     * @throws IllegalArgumentException if the context or schmata are null
+     * @param typeSystem the types system
+     * @throws IllegalArgumentException if the values or schmata are null
      */
-    public QueryContext( ExecutionContext context,
-                         Schemata schemata ) {
-        this(context, schemata, null, null, null);
+    public QueryContext( Schemata schemata,
+                         TypeSystem typeSystem ) {
+        this(schemata, typeSystem, null, null, null);
     }
 
     /**
-     * Get the execution context available to this query context.
+     * Get the interface for working with literal values and types.
      * 
-     * @return the execution context; never null
+     * @return the type system; never null
      */
-    public final ExecutionContext getExecutionContext() {
-        return context;
+    public TypeSystem getTypeSystem() {
+        return typeSystem;
     }
 
     /**
@@ -174,7 +174,7 @@
         if (obj == this) return true;
         if (obj instanceof QueryContext) {
             QueryContext that = (QueryContext)obj;
-            if (!this.context.equals(that.getExecutionContext())) return false;
+            if (!this.typeSystem.equals(that.getTypeSystem())) return false;
             if (!this.schemata.equals(that.getSchemata())) return false;
             if (!this.variables.equals(that.getVariables())) return false;
             return true;
@@ -183,15 +183,15 @@
     }
 
     /**
-     * Obtain a copy of this context, except that the copy uses the supplied execution context.
+     * Obtain a copy of this context, except that the copy uses the supplied type system.
      * 
-     * @param context the execution context that should be used in the new query context
+     * @param typeSystem the type system that should be used in the new query context
      * @return the new context; never null
      * @throws IllegalArgumentException if the execution context reference is null
      */
-    public QueryContext with( ExecutionContext context ) {
-        CheckArg.isNotNull(context, "context");
-        return new QueryContext(context, schemata, hints, problems, variables);
+    public QueryContext with( TypeSystem typeSystem ) {
+        CheckArg.isNotNull(typeSystem, "typeSystem");
+        return new QueryContext(schemata, typeSystem, hints, problems, variables);
     }
 
     /**
@@ -203,7 +203,7 @@
      */
     public QueryContext with( Schemata schemata ) {
         CheckArg.isNotNull(schemata, "schemata");
-        return new QueryContext(context, schemata, hints, problems, variables);
+        return new QueryContext(schemata, typeSystem, hints, problems, variables);
     }
 
     /**
@@ -215,7 +215,7 @@
      */
     public QueryContext with( PlanHints hints ) {
         CheckArg.isNotNull(hints, "hints");
-        return new QueryContext(context, schemata, hints, problems, variables);
+        return new QueryContext(schemata, typeSystem, hints, problems, variables);
     }
 
     /**
@@ -225,7 +225,7 @@
      * @return the new context; never null
      */
     public QueryContext with( Problems problems ) {
-        return new QueryContext(context, schemata, hints, problems, variables);
+        return new QueryContext(schemata, typeSystem, hints, problems, variables);
     }
 
     /**
@@ -235,7 +235,7 @@
      * @return the new context; never null
      */
     public QueryContext with( Map<String, Object> variables ) {
-        return new QueryContext(context, schemata, hints, problems, variables);
+        return new QueryContext(schemata, typeSystem, hints, problems, variables);
     }
 
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,11 +30,10 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 
 /**
  * The resulting output of a query.
@@ -43,11 +42,11 @@
 public interface QueryResults {
 
     /**
-     * Get the execution context in which the query was evaluated.
+     * Get the type system used to evaluate the query.
      * 
-     * @return the execution context; never null
+     * @return the type system; never null
      */
-    public ExecutionContext getContext();
+    public TypeSystem getTypeSystem();
 
     /**
      * Get the original query command.
@@ -280,7 +279,7 @@
          * @return the property name; never null
          * @throws IndexOutOfBoundsException if the column index is invalid
          */
-        public Name getPropertyNameForColumn( int columnIndex );
+        public String getPropertyNameForColumn( int columnIndex );
 
         /**
          * Get the name of the property that corresponds to the supplied column in each tuple.
@@ -289,7 +288,7 @@
          * @return the property name; never null
          * @throws NoSuchElementException if the column name is invalid or doesn't match an existing column
          */
-        public Name getPropertyNameForColumn( String columnName );
+        public String getPropertyNameForColumn( String columnName );
 
         /**
          * Get the index of the column given the column name.
@@ -310,7 +309,7 @@
          * @throws NoSuchElementException if the selector name or the property name are invalid
          */
         public int getColumnIndexForProperty( String selectorName,
-                                              Name propertyName );
+                                              String propertyName );
 
         /**
          * Get the index of the tuple value containing the full-text search score for the node taken from the named selector.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -47,14 +47,18 @@
     }
 
     /**
-     * @return left
+     * Get the constraint that is on the left-hand-side of the AND operation.
+     * 
+     * @return the left-hand-side constraint
      */
     public final Constraint getLeft() {
         return left;
     }
 
     /**
-     * @return right
+     * Get the constraint that is on the right-hand-side of the AND operation.
+     * 
+     * @return the right-hand-side constraint
      */
     public final Constraint getRight() {
         return right;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Between.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -91,14 +91,18 @@
     }
 
     /**
-     * @return lowerBound
+     * Get the lower bound operand.
+     * 
+     * @return the lower bound; never null
      */
     public StaticOperand getLowerBound() {
         return lowerBound;
     }
 
     /**
-     * @return upperBound
+     * Get the upper bound operand.
+     * 
+     * @return the upper bound; never null
      */
     public StaticOperand getUpperBound() {
         return upperBound;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Path;
 
 /**
  * A constraint requiring that the selected node is a child of the node reachable by the supplied absolute path
@@ -34,7 +33,7 @@
 @Immutable
 public class ChildNode extends Constraint {
     private final SelectorName selectorName;
-    private final Path parentPath;
+    private final String parentPath;
     private final int hc;
 
     /**
@@ -45,7 +44,7 @@
      * @param parentPath the absolute path to the parent
      */
     public ChildNode( SelectorName selectorName,
-                      Path parentPath ) {
+                      String parentPath ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(parentPath, "parentPath");
         this.selectorName = selectorName;
@@ -54,16 +53,20 @@
     }
 
     /**
-     * @return selectorName
+     * Get the name of the selector representing the child
+     * 
+     * @return the selector name; never null
      */
     public final SelectorName getSelectorName() {
         return selectorName;
     }
 
     /**
-     * @return parentPath
+     * Get the path of the parent.
+     * 
+     * @return the parent path; never null
      */
-    public final Path getParentPath() {
+    public final String getParentPath() {
         return parentPath;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -53,14 +53,18 @@
     }
 
     /**
-     * @return childSelectorName
+     * Get the name of the selector that represents the child.
+     * 
+     * @return the selector name of the child node; never null
      */
     public final SelectorName getChildSelectorName() {
         return childSelectorName;
     }
 
     /**
-     * @return parentSelectorName
+     * Get the name of the selector that represents the parent.
+     * 
+     * @return the selector name of the parent node; never null
      */
     public final SelectorName getParentSelectorName() {
         return parentSelectorName;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.ObjectUtil;
-import org.jboss.dna.graph.property.Name;
 
 /**
  * 
@@ -35,7 +34,7 @@
 public class Column implements LanguageObject {
 
     private final SelectorName selectorName;
-    private final Name propertyName;
+    private final String propertyName;
     private final String columnName;
 
     /**
@@ -58,7 +57,7 @@
      * @param columnName the name of the column
      */
     public Column( SelectorName selectorName,
-                   Name propertyName,
+                   String propertyName,
                    String columnName ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(propertyName, "propertyName");
@@ -69,21 +68,27 @@
     }
 
     /**
-     * @return selectorName
+     * Get the name of the selector for the node.
+     * 
+     * @return the selector name; never null
      */
     public final SelectorName getSelectorName() {
         return selectorName;
     }
 
     /**
-     * @return propertyName
+     * Get the name of the property that this column represents.
+     * 
+     * @return the property name; or null if this represents all selectable columns on the {@link #getSelectorName() selector}
      */
-    public final Name getPropertyName() {
+    public final String getPropertyName() {
         return propertyName;
     }
 
     /**
-     * @return columnName
+     * Get the name of the column.
+     * 
+     * @return the column name; or null if this represents all selectable columsn on the {@link #getSelectorName() selector}
      */
     public final String getColumnName() {
         return columnName;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -51,21 +51,27 @@
     }
 
     /**
-     * @return operand1
+     * Get the dynamic operand of this comparison.
+     * 
+     * @return the dynamic operand; never null
      */
     public final DynamicOperand getOperand1() {
         return operand1;
     }
 
     /**
-     * @return operand2
+     * Get the dynamic operand of this comparison.
+     * 
+     * @return the dynamic operand; never null
      */
     public final StaticOperand getOperand2() {
         return operand2;
     }
 
     /**
-     * @return operator
+     * Get the operator for this comparison
+     * 
+     * @return the operator; never null
      */
     public final Operator getOperator() {
         return operator;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -25,7 +25,6 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.property.Path;
 
 /**
  * A constraint requiring that the selected node is a descendant of the node reachable by the supplied absolute path
@@ -33,7 +32,7 @@
 @Immutable
 public class DescendantNode extends Constraint {
     private final SelectorName selectorName;
-    private final Path ancestorPath;
+    private final String ancestorPath;
 
     /**
      * Create a constraint requiring that the node identified by the selector is a descendant of the node reachable by the
@@ -43,7 +42,7 @@
      * @param ancestorPath the absolute path to the ancestor
      */
     public DescendantNode( SelectorName selectorName,
-                           Path ancestorPath ) {
+                           String ancestorPath ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(ancestorPath, "ancestorPath");
         this.selectorName = selectorName;
@@ -51,16 +50,20 @@
     }
 
     /**
-     * @return selectorName
+     * Get the name of the selector for the node.
+     * 
+     * @return the selector name; never null
      */
     public final SelectorName getSelectorName() {
         return selectorName;
     }
 
     /**
-     * @return ancestorPath
+     * Get the path of the node that is to be the ancestor of the target node.
+     * 
+     * @return the path of the ancestor node; never null
      */
-    public final Path getAncestorPath() {
+    public final String getAncestorPath() {
         return ancestorPath;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -53,14 +53,18 @@
     }
 
     /**
-     * @return descendantSelectorName
+     * Get the name of the selector for the descedant node.
+     * 
+     * @return the selector name of the descendant node; never null
      */
     public final SelectorName getDescendantSelectorName() {
         return descendantSelectorName;
     }
 
     /**
-     * @return ancestorSelectorName
+     * Get the name of the selector for the ancestor node.
+     * 
+     * @return the selector name of the ancestor node; never null
      */
     public final SelectorName getAncestorSelectorName() {
         return ancestorSelectorName;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Name;
 
 /**
  * A join condition that tests whether a property on a node is equal to a property on another node. A node-tuple satisfies the
@@ -40,15 +39,23 @@
 @Immutable
 public class EquiJoinCondition extends JoinCondition {
     private final SelectorName selector1Name;
-    private final Name property1Name;
+    private final String property1Name;
     private final SelectorName selector2Name;
-    private final Name property2Name;
+    private final String property2Name;
     private final int hc;
 
+    /**
+     * Create an equi-join condition, given the names of the selector and property for the left- and right-hand-side of the join.
+     * 
+     * @param selector1Name the selector name appearing on the left-side of the join; never null
+     * @param property1Name the property name for the left-side of the join; never null
+     * @param selector2Name the selector name appearing on the right-side of the join; never null
+     * @param property2Name the property name for the right-side of the join; never null
+     */
     public EquiJoinCondition( SelectorName selector1Name,
-                              Name property1Name,
+                              String property1Name,
                               SelectorName selector2Name,
-                              Name property2Name ) {
+                              String property2Name ) {
         CheckArg.isNotNull(selector1Name, "selector1Name");
         CheckArg.isNotNull(property1Name, "property1Name");
         CheckArg.isNotNull(selector2Name, "selector2Name");
@@ -60,36 +67,50 @@
         this.hc = HashCode.compute(this.selector1Name, this.property1Name, this.selector2Name, this.property2Name);
     }
 
+    /**
+     * Create an equi-join condition, given the columns.
+     * 
+     * @param column1 the column for the left-side of the join; never null
+     * @param column2 the column for the right-side of the join; never null
+     */
     public EquiJoinCondition( Column column1,
                               Column column2 ) {
         this(column1.getSelectorName(), column1.getPropertyName(), column2.getSelectorName(), column2.getPropertyName());
     }
 
     /**
-     * @return selector1Name
+     * Get the name of the selector that appears on the left-side of the join.
+     * 
+     * @return the selector name appearing on the left-side of the join; never null
      */
     public final SelectorName getSelector1Name() {
         return selector1Name;
     }
 
     /**
-     * @return property1Name
+     * Get the name of the property that appears on the left-side of the join.
+     * 
+     * @return the property name for the left-side of the join; never null
      */
-    public final Name getProperty1Name() {
+    public final String getProperty1Name() {
         return property1Name;
     }
 
     /**
-     * @return selector2Name
+     * Get the name of the selector that appears on the right-side of the join.
+     * 
+     * @return the selector name appearing on the right-side of the join; never null
      */
     public final SelectorName getSelector2Name() {
         return selector2Name;
     }
 
     /**
-     * @return property2Name
+     * Get the name of the property that appears on the left-side of the join.
+     * 
+     * @return the property name for the left-side of the join; never null
      */
-    public final Name getProperty2Name() {
+    public final String getProperty2Name() {
         return property2Name;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,7 +30,6 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.parse.FullTextSearchParser;
 
 /**
@@ -40,7 +39,7 @@
 @Immutable
 public class FullTextSearch extends Constraint {
     private final SelectorName selectorName;
-    private final Name propertyName;
+    private final String propertyName;
     private final String fullTextSearchExpression;
     private Term term;
     private final int hc;
@@ -54,7 +53,7 @@
      * @param term the term representation, if it is known; may be null
      */
     public FullTextSearch( SelectorName selectorName,
-                           Name propertyName,
+                           String propertyName,
                            String fullTextSearchExpression,
                            Term term ) {
         CheckArg.isNotNull(selectorName, "selectorName");
@@ -73,7 +72,7 @@
      * @param fullTextSearchExpression the search expression
      */
     public FullTextSearch( SelectorName selectorName,
-                           Name propertyName,
+                           String propertyName,
                            String fullTextSearchExpression ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
@@ -101,21 +100,27 @@
     }
 
     /**
-     * @return selectorName
+     * Get the name of the selector that is to be searched
+     * 
+     * @return the selector name; never null
      */
     public final SelectorName getSelectorName() {
         return selectorName;
     }
 
     /**
-     * @return propertyName
+     * Get the name of the property that is to be searched.
+     * 
+     * @return the property name; never null
      */
-    public final Name getPropertyName() {
+    public final String getPropertyName() {
         return propertyName;
     }
 
     /**
-     * @return fullTextSearchExpression
+     * Get the full-text search expression, as a string.
+     * 
+     * @return the search expression; never null
      */
     public final String getFullTextSearchExpression() {
         return fullTextSearchExpression;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -64,28 +64,36 @@
     }
 
     /**
-     * @return left
+     * Get the source that represents the left-hand-side of the join.
+     * 
+     * @return the left-side source; never null
      */
     public final Source getLeft() {
         return left;
     }
 
     /**
-     * @return right
+     * Get the source that represents the right-hand-side of the join.
+     * 
+     * @return the right-side source; never null
      */
     public final Source getRight() {
         return right;
     }
 
     /**
-     * @return type
+     * Get the type of join.
+     * 
+     * @return the join type; never null
      */
     public final JoinType getType() {
         return type;
     }
 
     /**
-     * @return joinCondition
+     * Get the join condition
+     * 
+     * @return the join condition; never null
      */
     public final JoinCondition getJoinCondition() {
         return joinCondition;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,7 +27,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 
 public enum JoinType implements Readable {
     INNER("INNER JOIN"),
@@ -92,9 +91,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+     * @see org.jboss.dna.graph.query.model.Readable#getString()
      */
-    public String getString( ExecutionContext context ) {
+    public String getString() {
         return getSymbol();
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -54,7 +54,9 @@
     }
 
     /**
-     * @return propertyValue
+     * Get the property value whose length is being constrained.
+     * 
+     * @return the property value being constrained; never null
      */
     public final PropertyValue getPropertyValue() {
         return propertyValue;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -37,12 +37,25 @@
     private final int offset;
     private final int rowLimit;
 
+    /**
+     * Create a limit on the number of rows.
+     * 
+     * @param rowLimit the maximum number of rows
+     * @throws IllegalArgumentException if the row limit is not positive
+     */
     public Limit( int rowLimit ) {
         CheckArg.isPositive(rowLimit, "rowLimit");
         this.rowLimit = rowLimit;
         this.offset = 0;
     }
 
+    /**
+     * Create a limit on the number of rows and the number of initial rows to skip.
+     * 
+     * @param rowLimit the maximum number of rows
+     * @param offset the number of rows to skip before beginning the results
+     * @throws IllegalArgumentException if the row limit is not positive, or if the offset is negative
+     */
     public Limit( int rowLimit,
                   int offset ) {
         CheckArg.isPositive(rowLimit, "rowLimit");
@@ -52,7 +65,9 @@
     }
 
     /**
-     * @return offset
+     * Get the number of rows skipped before the results begin.
+     * 
+     * @return the offset; always 0 or a positive number
      */
     public final int getOffset() {
         return offset;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -45,7 +45,9 @@
     }
 
     /**
-     * @return operand
+     * Get the dynamic operand that is to be lower-cased.
+     * 
+     * @return the dynamic operand; never null
      */
     public final DynamicOperand getOperand() {
         return operand;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -57,7 +57,9 @@
     }
 
     /**
-     * @return symbol
+     * Get the symbol for this operator
+     * 
+     * @return the symbolic representation; never null
      */
     public String getSymbol() {
         return symbol;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -24,7 +24,6 @@
 package org.jboss.dna.graph.query.model;
 
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 
 /**
  * 
@@ -40,7 +39,9 @@
     }
 
     /**
-     * @return symbol
+     * Get the symbolic representation of the order
+     * 
+     * @return the symbolic representation; never null
      */
     public String getSymbol() {
         return symbol;
@@ -73,9 +74,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+     * @see org.jboss.dna.graph.query.model.Readable#getString()
      */
-    public String getString( ExecutionContext context ) {
+    public String getString() {
         return getSymbol();
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Name;
 
 /**
  * A constraint that evaluates to true only when a named property exists on a node.
@@ -34,7 +33,7 @@
 @Immutable
 public class PropertyExistence extends Constraint {
     private final SelectorName selectorName;
-    private final Name propertyName;
+    private final String propertyName;
     private final int hc;
 
     /**
@@ -44,7 +43,7 @@
      * @param propertyName the name of the property that must exist
      */
     public PropertyExistence( SelectorName selectorName,
-                              Name propertyName ) {
+                              String propertyName ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
@@ -66,7 +65,7 @@
      * 
      * @return the property name; never null
      */
-    public final Name getPropertyName() {
+    public final String getPropertyName() {
         return propertyName;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Name;
 
 /**
  * A dynamic operand that evaluates to the value(s) of a property on a selector, used in a {@link Comparison} constraint.
@@ -34,7 +33,7 @@
 @Immutable
 public class PropertyValue extends DynamicOperand {
     private final SelectorName selectorName;
-    private final Name propertyName;
+    private final String propertyName;
     private final int hc;
 
     /**
@@ -45,7 +44,7 @@
      * @throws IllegalArgumentException if the selector name or property name are null
      */
     public PropertyValue( SelectorName selectorName,
-                          Name propertyName ) {
+                          String propertyName ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
@@ -68,7 +67,7 @@
      * 
      * @return the property name; never null
      */
-    public final Name getPropertyName() {
+    public final String getPropertyName() {
         return propertyName;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -126,34 +126,81 @@
         return distinct;
     }
 
+    /**
+     * Create a copy of this query, but one in which there are no duplicate rows in the results.
+     * 
+     * @return the copy of the query with no duplicate result rows; never null
+     */
     public Query distinct() {
         return new Query(source, constraint, getOrderings(), columns, getLimits(), true);
     }
 
+    /**
+     * Create a copy of this query, but one in which there may be duplicate rows in the results.
+     * 
+     * @return the copy of the query with potentially duplicate result rows; never null
+     */
     public Query noDistinct() {
         return new Query(source, constraint, getOrderings(), columns, getLimits(), false);
     }
 
+    /**
+     * Create a copy of this query, but one that uses the supplied constraint.
+     * 
+     * @param constraint the constraint that should be used; never null
+     * @return the copy of the query that uses the supplied constraint; never null
+     */
     public Query constrainedBy( Constraint constraint ) {
         return new Query(source, constraint, getOrderings(), columns, getLimits(), distinct);
     }
 
+    /**
+     * Create a copy of this query, but one whose results should be ordered by the supplied orderings.
+     * 
+     * @param orderings the result ordering specification that should be used; never null
+     * @return the copy of the query that uses the supplied ordering; never null
+     */
     public Query orderedBy( List<Ordering> orderings ) {
         return new Query(source, constraint, orderings, columns, getLimits(), distinct);
     }
 
+    /**
+     * Create a copy of this query, but one that uses the supplied limit on the number of result rows.
+     * 
+     * @param rowLimit the limit that should be used; must be a positive number
+     * @return the copy of the query that uses the supplied limit; never null
+     */
     public Query withLimit( int rowLimit ) {
         return new Query(source, constraint, getOrderings(), columns, getLimits().withRowLimit(rowLimit), distinct);
     }
 
+    /**
+     * Create a copy of this query, but one that uses the supplied offset.
+     * 
+     * @param offset the limit that should be used; may not be negative
+     * @return the copy of the query that uses the supplied offset; never null
+     */
     public Query withOffset( int offset ) {
         return new Query(source, constraint, getOrderings(), columns, getLimits().withOffset(offset), distinct);
     }
 
+    /**
+     * Create a copy of this query, but that returns results with the supplied columns.
+     * 
+     * @param columns the columns of the results; may not be null
+     * @return the copy of the query returning the supplied result columns; never null
+     */
     public Query returning( List<Column> columns ) {
         return new Query(source, constraint, getOrderings(), columns, getLimits(), distinct);
     }
 
+    /**
+     * Create a copy of this query, but that returns results that are ordered by the {@link #getOrderings() orderings} of this
+     * column as well as those supplied.
+     * 
+     * @param orderings the additional orderings of the result rows; may no be null
+     * @return the copy of the query returning the supplied result columns; never null
+     */
     public Query adding( Ordering... orderings ) {
         List<Ordering> newOrderings = null;
         if (this.getOrderings() != null) {
@@ -167,6 +214,13 @@
         return new Query(source, constraint, newOrderings, columns, getLimits(), distinct);
     }
 
+    /**
+     * Create a copy of this query, but that returns results that include the columns specified by this query as well as the
+     * supplied columns.
+     * 
+     * @param columns the additional columns that should be included in the the results; may not be null
+     * @return the copy of the query returning the supplied result columns; never null
+     */
     public Query adding( Column... columns ) {
         List<Column> newColumns = null;
         if (this.columns != null) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -23,7 +23,6 @@
  */
 package org.jboss.dna.graph.query.model;
 
-import org.jboss.dna.graph.ExecutionContext;
 
 /**
  * 
@@ -32,9 +31,8 @@
     /**
      * Get the string representation of this query object.
      * 
-     * @param context the execution context in which the conversion is to take place
      * @return the string representation; never null
      */
-    String getString( ExecutionContext context );
+    String getString();
 
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Path;
 
 /**
  * A constraint requiring that the selected node is reachable by the supplied absolute path
@@ -34,7 +33,7 @@
 @Immutable
 public class SameNode extends Constraint {
     private final SelectorName selectorName;
-    private final Path path;
+    private final String path;
     private final int hc;
 
     /**
@@ -45,7 +44,7 @@
      * @throws IllegalArgumentException if the selector name or path are null
      */
     public SameNode( SelectorName selectorName,
-                     Path path ) {
+                     String path ) {
         CheckArg.isNotNull(selectorName, "selectorName");
         CheckArg.isNotNull(path, "path");
         this.selectorName = selectorName;
@@ -67,7 +66,7 @@
      * 
      * @return the absolute path; never null
      */
-    public final Path getPath() {
+    public final String getPath() {
         return path;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,7 +27,6 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
-import org.jboss.dna.graph.property.Path;
 
 /**
  * A join condition that tests whether two nodes are the same nodes (that is, have the same identifier or have the same relative
@@ -37,7 +36,7 @@
 public class SameNodeJoinCondition extends JoinCondition {
     private final SelectorName selector1Name;
     private final SelectorName selector2Name;
-    private final Path selector2Path;
+    private final String selector2Path;
     private final int hc;
 
     /**
@@ -51,7 +50,7 @@
      */
     public SameNodeJoinCondition( SelectorName selector1Name,
                                   SelectorName selector2Name,
-                                  Path selector2Path ) {
+                                  String selector2Path ) {
         CheckArg.isNotNull(selector1Name, "selector1Name");
         CheckArg.isNotNull(selector2Name, "selector2Name");
         CheckArg.isNotNull(selector2Path, "selector2Path");
@@ -102,7 +101,7 @@
      * 
      * @return the relative path to the node; may be null if the second selector is the node being used
      */
-    public final Path getSelector2Path() {
+    public final String getSelector2Path() {
         return selector2Path;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -25,7 +25,6 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 
 /**
  * A representation of a qualified or expanded name.
@@ -52,9 +51,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.model.Readable#getString(ExecutionContext)
+     * @see org.jboss.dna.graph.query.model.Readable#getString()
      */
-    public String getString( ExecutionContext context ) {
+    public String getString() {
         return name;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -42,6 +42,7 @@
                         Collection<StaticOperand> setOperands ) {
         CheckArg.isNotNull(left, "left");
         CheckArg.isNotNull(setOperands, "setOperands");
+        CheckArg.isNotEmpty(setOperands, "setOperands");
         this.left = left;
         this.setOperands = setOperands;
     }
@@ -50,19 +51,24 @@
                         StaticOperand... setOperands ) {
         CheckArg.isNotNull(left, "left");
         CheckArg.isNotNull(setOperands, "setOperands");
+        CheckArg.isNotEmpty(setOperands, "setOperands");
         this.left = left;
         this.setOperands = Collections.unmodifiableList(Arrays.asList(setOperands));
     }
 
     /**
-     * @return operand1
+     * Get the dynamic operand to which the set constraint applies
+     * 
+     * @return the dynamic operand; never null
      */
     public final DynamicOperand getLeftOperand() {
         return left;
     }
 
     /**
-     * @return operand2
+     * Get the collection of static operands defining the constrained values.
+     * 
+     * @return the right-hand-side static operands; never null and never empty
      */
     public final Collection<StaticOperand> getRightOperands() {
         return setOperands;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -33,7 +33,6 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
-import org.jboss.dna.graph.ExecutionContext;
 
 /**
  * This object acts as a Set operator on multiple {@link QueryCommand queries}, such as performing UNION, INTERSECT, and EXCEPT
@@ -91,9 +90,9 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+         * @see org.jboss.dna.graph.query.model.Readable#getString()
          */
-        public String getString( ExecutionContext context ) {
+        public String getString() {
             return getSymbol();
         }
     }
@@ -104,6 +103,15 @@
     private final boolean all;
     private final int hc;
 
+    /**
+     * Create a set query involving the supplied left- and right-hand-side queries.
+     * 
+     * @param left the left-hand-side query being combined
+     * @param operation the set operation
+     * @param right the right-hand-side query being combined
+     * @param all true if all of the results should be included
+     * @throws IllegalArgumentException if the left-hand-side query, right-hand-side query, or operation are null
+     */
     public SetQuery( QueryCommand left,
                      Operation operation,
                      QueryCommand right,
@@ -119,6 +127,17 @@
         this.hc = HashCode.compute(this.left, this.right, this.operation);
     }
 
+    /**
+     * Create a set query involving the supplied left- and right-hand-side queries.
+     * 
+     * @param left the left-hand-side query being combined
+     * @param operation the set operation
+     * @param right the right-hand-side query being combined
+     * @param all true if all of the results should be included
+     * @param orderings the specification of the order of the result rows, or null if the results need not be ordered
+     * @param limit the limit for the result rows, or null if there are no limits
+     * @throws IllegalArgumentException if the left-hand-side query, right-hand-side query, or operation are null
+     */
     public SetQuery( QueryCommand left,
                      Operation operation,
                      QueryCommand right,

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/TypeSystem.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/TypeSystem.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/TypeSystem.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -0,0 +1,143 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.query.model;
+
+import java.util.Comparator;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.ValueFormatException;
+
+/**
+ * 
+ */
+ at Immutable
+public interface TypeSystem {
+
+    TypeFactory<?> getTypeFactory( String typeName );
+
+    TypeFactory<?> getTypeFactory( Object prototype );
+
+    TypeFactory<Boolean> getBooleanFactory();
+
+    TypeFactory<Long> getLongFactory();
+
+    TypeFactory<String> getStringFactory();
+
+    TypeFactory<Double> getDoubleFactory();
+
+    TypeFactory<?> getDateTimeFactory();
+
+    TypeFactory<?> getPathFactory();
+
+    String getDefaultType();
+
+    Comparator<Object> getDefaultComparator();
+
+    /**
+     * Get the names of the supported types.
+     * 
+     * @return the immutable set of the uppercase names of the types; never null
+     */
+    Set<String> getTypeNames();
+
+    /**
+     * Get the string representation of the supplied value, using the most appropriate factory given the value.
+     * 
+     * @param value the value
+     * @return the string representation; never null
+     */
+    String asString( Object value );
+
+    /**
+     * Factory interface for creating values from strings.
+     * 
+     * @param <T> the type of value object
+     */
+    public interface TypeFactory<T> {
+
+        /**
+         * Get the class representing the value type.
+         * 
+         * @return the value type; never null
+         */
+        Class<T> getType();
+
+        /**
+         * Get a comparator that can be used to store the values of this type.
+         * 
+         * @return the comparator; never null
+         */
+        Comparator<T> getComparator();
+
+        /**
+         * Get the name of the type created by this factory.
+         * 
+         * @return the type name; never null and never empty or blank
+         */
+        String getTypeName();
+
+        /**
+         * Create the typed representation of the value given the supplied string representation.
+         * 
+         * @param value the string representation of the value
+         * @return the typed representation, which will be an instance of the {@link #getType() type}
+         * @throws ValueFormatException if the string cannot be converted to a typed value
+         */
+        T create( String value ) throws ValueFormatException;
+
+        /**
+         * Create the typed representation of the value given the supplied object representation.
+         * 
+         * @param value the object representation of the value
+         * @return the typed representation, which will be an instance of the {@link #getType() type}
+         * @throws ValueFormatException if the object cannot be converted to a typed value
+         */
+        T create( Object value ) throws ValueFormatException;
+
+        /**
+         * Get the string representation of the supplied value.
+         * 
+         * @param value the value
+         * @return the string representation; never null
+         */
+        String asString( Object value );
+
+        /**
+         * Get the length of the supplied value.
+         * 
+         * @param value the value
+         * @return the length; never negative
+         */
+        long length( Object value );
+
+        /**
+         * Get a readable and potentially shorter string representation of the supplied value.
+         * 
+         * @param value the value
+         * @return the readable string representation; never null
+         */
+        String asReadableString( Object value );
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/TypeSystem.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,9 +26,14 @@
 import net.jcip.annotations.Immutable;
 
 /**
- * 
+ * An interface called by a visitor when that visitor is visiting the node.
  */
 @Immutable
 public interface Visitable {
+    /**
+     * Accept the supplied visitor, which should call back to the visitor to complete the double-dispatch operation.
+     * 
+     * @param visitor the visitor; never null
+     */
     void accept( Visitor visitor );
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -74,20 +74,8 @@
      * Using a visitor, obtain the readable string representation of the supplied {@link Visitable object}
      * 
      * @param visitable the visitable
-     * @param context the execution context in which the representation should be produced, or null if there is none
      * @return the string representation
      */
-    public static String readable( Visitable visitable,
-                                   ExecutionContext context ) {
-        return visit(visitable, new ReadableVisitor(context)).getString();
-    }
-
-    /**
-     * Using a visitor, obtain the readable string representation of the supplied {@link Visitable object}
-     * 
-     * @param visitable the visitable
-     * @return the string representation
-     */
     public static String readable( Visitable visitable ) {
         return visit(visitable, new ReadableVisitor()).getString();
     }
@@ -955,7 +943,7 @@
         }
 
         protected final ReadableVisitor append( SelectorName name ) {
-            sb.append(name.getString(context));
+            sb.append(name.getString());
             return this;
         }
 
@@ -1082,10 +1070,9 @@
             if (column.getPropertyName() == null) {
                 append(".*");
             } else {
-                Name propertyName = column.getPropertyName();
-                String propName = propertyName.getString(registry, null, null);
-                append('.').append(propName);
-                if (!propName.equals(column.getColumnName()) && !propertyName.getLocalName().equals(column.getColumnName())) {
+                String propertyName = column.getPropertyName();
+                append('.').append(propertyName);
+                if (!propertyName.equals(column.getColumnName()) && !propertyName.equals(column.getColumnName())) {
                     append(" AS ").append(column.getColumnName());
                 }
             }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,7 +27,6 @@
 import java.util.List;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
 import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
@@ -206,8 +205,8 @@
             EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
             SelectorName selector1 = joinCondition.getSelector1Name();
             SelectorName selector2 = joinCondition.getSelector2Name();
-            Name property1 = joinCondition.getProperty1Name();
-            Name property2 = joinCondition.getProperty2Name();
+            String property1 = joinCondition.getProperty1Name();
+            String property2 = joinCondition.getProperty2Name();
 
             // Create the Ordering for the first selector/property pair ...
             DynamicOperand operand1 = new PropertyValue(selector1, property1);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,9 +28,6 @@
 import java.util.LinkedList;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -75,8 +72,8 @@
                 EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
                 SelectorName selector1 = equiJoinCondition.getSelector1Name();
                 SelectorName selector2 = equiJoinCondition.getSelector2Name();
-                Name property1 = equiJoinCondition.getProperty1Name();
-                Name property2 = equiJoinCondition.getProperty2Name();
+                String property1 = equiJoinCondition.getProperty1Name();
+                String property2 = equiJoinCondition.getProperty2Name();
 
                 // Walk up the tree looking for SELECT nodes that apply to one of the sides ...
                 PlanNode node = join.getParent();
@@ -145,14 +142,14 @@
     protected PlanNode copySelectNode( QueryContext context,
                                        PlanNode selectNode,
                                        SelectorName selectorName,
-                                       Name propertyName,
+                                       String propertyName,
                                        SelectorName copySelectorName,
-                                       Name copyPropertyName ) {
+                                       String copyPropertyName ) {
         if (selectNode.isNot(Type.SELECT)) return null;
         if (selectNode.getSelectors().size() != 1 || !selectNode.getSelectors().contains(selectorName)) return null;
 
         Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-        Set<Column> columns = getColumnsReferencedBy(constraint, context.getExecutionContext());
+        Set<Column> columns = getColumnsReferencedBy(constraint);
         if (columns.size() != 1) return null;
         Column column = columns.iterator().next();
         if (!column.getSelectorName().equals(selectorName)) return null;
@@ -165,10 +162,8 @@
         PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
 
         // Copy the constraint, but change the references to the copy selector and property ...
-        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
         PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
-        mappings.map(stringFactory.create(propertyName), new Column(copySelectorName, copyPropertyName,
-                                                                    stringFactory.create(copyPropertyName)));
+        mappings.map(propertyName, new Column(copySelectorName, copyPropertyName, copyPropertyName));
         Constraint newCriteria = PlanUtil.replaceReferences(context, constraint, mappings, copy);
         copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
 
@@ -189,20 +184,17 @@
      * Get the set of Column objects that represent those columns referenced by the visitable object.
      * 
      * @param visitable the object to be visited
-     * @param context the context; may not be null
      * @return the set of Column objects, with column names that always are the string-form of the
      *         {@link Column#getPropertyName() property name}; never null
      */
-    public static Set<Column> getColumnsReferencedBy( Visitable visitable,
-                                                      ExecutionContext context ) {
+    public static Set<Column> getColumnsReferencedBy( Visitable visitable ) {
         if (visitable == null) return Collections.emptySet();
-        final ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
         final Set<Column> symbols = new HashSet<Column>();
         // Walk the entire structure, so only supply a StrategyVisitor (that does no navigation) ...
         Visitors.visitAll(visitable, new AbstractVisitor() {
             protected void addColumnFor( SelectorName selectorName,
-                                         Name property ) {
-                symbols.add(new Column(selectorName, property, stringFactory.create(property)));
+                                         String property ) {
+                symbols.add(new Column(selectorName, property, property));
             }
 
             @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,9 +28,6 @@
 import java.util.LinkedList;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -136,8 +133,8 @@
                 EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
                 SelectorName selector1 = equiJoinCondition.getSelector1Name();
                 SelectorName selector2 = equiJoinCondition.getSelector2Name();
-                Name property1 = equiJoinCondition.getProperty1Name();
-                Name property2 = equiJoinCondition.getProperty2Name();
+                String property1 = equiJoinCondition.getProperty1Name();
+                String property2 = equiJoinCondition.getProperty2Name();
 
                 // Walk up the tree looking for SELECT nodes that apply to one of the sides ...
                 PlanNode node = join.getParent();
@@ -167,14 +164,14 @@
     protected PlanNode copySelectNode( QueryContext context,
                                        PlanNode selectNode,
                                        SelectorName selectorName,
-                                       Name propertyName,
+                                       String propertyName,
                                        SelectorName copySelectorName,
-                                       Name copyPropertyName ) {
+                                       String copyPropertyName ) {
         if (selectNode.isNot(Type.SELECT)) return null;
         if (selectNode.getSelectors().size() != 1 || !selectNode.getSelectors().contains(selectorName)) return null;
 
         Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-        Set<Column> columns = getColumnsReferencedBy(constraint, context.getExecutionContext());
+        Set<Column> columns = getColumnsReferencedBy(constraint);
         if (columns.size() != 1) return null;
         Column column = columns.iterator().next();
         if (!column.getSelectorName().equals(selectorName)) return null;
@@ -187,10 +184,8 @@
         PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
 
         // Copy the constraint, but change the references to the copy selector and property ...
-        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
         PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
-        mappings.map(stringFactory.create(propertyName), new Column(copySelectorName, copyPropertyName,
-                                                                    stringFactory.create(copyPropertyName)));
+        mappings.map(propertyName, new Column(copySelectorName, copyPropertyName, copyPropertyName));
         Constraint newCriteria = PlanUtil.replaceReferences(context, constraint, mappings, copy);
         copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
 
@@ -211,20 +206,17 @@
      * Get the set of Column objects that represent those columns referenced by the visitable object.
      * 
      * @param visitable the object to be visited
-     * @param context the context; may not be null
      * @return the set of Column objects, with column names that always are the string-form of the
      *         {@link Column#getPropertyName() property name}; never null
      */
-    public static Set<Column> getColumnsReferencedBy( Visitable visitable,
-                                                      ExecutionContext context ) {
+    public static Set<Column> getColumnsReferencedBy( Visitable visitable ) {
         if (visitable == null) return Collections.emptySet();
-        final ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
         final Set<Column> symbols = new HashSet<Column>();
         // Walk the entire structure, so only supply a StrategyVisitor (that does no navigation) ...
         Visitors.visitAll(visitable, new AbstractVisitor() {
             protected void addColumnFor( SelectorName selectorName,
-                                         Name property ) {
-                symbols.add(new Column(selectorName, property, stringFactory.create(property)));
+                                         String property ) {
+                symbols.add(new Column(selectorName, property, property));
             }
 
             @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,7 +29,6 @@
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
@@ -118,9 +117,8 @@
                     context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
                     continue;
                 }
-                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-                String leftColumnName = stringFactory.create(equiJoin.getProperty1Name());
-                String rightColumnName = stringFactory.create(equiJoin.getProperty2Name());
+                String leftColumnName = equiJoin.getProperty1Name();
+                String rightColumnName = equiJoin.getProperty2Name();
                 Schemata.Column leftColumn = table.getColumn(leftColumnName);
                 Schemata.Column rightColumn = table.getColumn(rightColumnName);
                 if (leftColumn == null) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -25,8 +25,8 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.text.ParsingException;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 
 /**
  * The basic interface defining a component that is able to parse a string query into a {@link QueryCommand}.
@@ -45,12 +45,12 @@
      * Parse the supplied query from a string representation into a {@link QueryCommand}.
      * 
      * @param query the query in string form; may not be null
-     * @param context the context in which the query is being parsed
+     * @param typeSystem the type system used by the query; may not be null
      * @return the query command
      * @throws ParsingException if there is an error parsing the supplied query
      * @throws InvalidQueryException if the supplied query can be parsed but is invalid
      */
     QueryCommand parseQuery( String query,
-                             ExecutionContext context ) throws InvalidQueryException;
+                             TypeSystem typeSystem ) throws InvalidQueryException;
 
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -32,9 +32,9 @@
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 
 /**
  * A thread-safe collection of {@link QueryParser} implementations that can be used to parse queries by language.
@@ -155,7 +155,7 @@
     /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
-     * @param context the context in which the query should be executed
+     * @param typeSystem the type system that should be used
      * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
      *        languages}
      * @param query the query that is to be executed
@@ -164,17 +164,17 @@
      * @throws ParsingException if there is an error parsing the supplied query
      * @throws InvalidQueryException if the supplied query can be parsed but is invalid
      */
-    public QueryCommand parse( ExecutionContext context,
+    public QueryCommand parse( TypeSystem typeSystem,
                                String language,
                                String query ) {
         CheckArg.isNotNull(language, "language");
-        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(typeSystem, "typeSystem");
         CheckArg.isNotNull(query, "query");
         QueryParser parser = parsers.get(language.toLowerCase());
         if (parser == null) {
             throw new IllegalArgumentException(GraphI18n.unknownQueryLanguage.text(language));
         }
-        return parser.parseQuery(query, context);
+        return parser.parseQuery(query, typeSystem);
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -37,15 +37,7 @@
 import org.jboss.dna.common.text.TokenStream.Tokenizer;
 import org.jboss.dna.common.text.TokenStream.Tokens;
 import org.jboss.dna.common.xml.XmlCharacters;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.Between;
@@ -90,9 +82,11 @@
 import org.jboss.dna.graph.query.model.SetQuery;
 import org.jboss.dna.graph.query.model.Source;
 import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.FullTextSearch.Term;
 import org.jboss.dna.graph.query.model.SetQuery.Operation;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 
 /**
  * A {@link QueryParser} implementation that parses a subset of SQL select and set queries.
@@ -475,24 +469,24 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(String, ExecutionContext)
+     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(String, TypeSystem)
      */
     public QueryCommand parseQuery( String query,
-                                    ExecutionContext context ) {
+                                    TypeSystem typeSystem ) {
         Tokenizer tokenizer = new SqlTokenizer(false);
         TokenStream tokens = new TokenStream(query, tokenizer, false);
         tokens.start();
-        return parseQueryCommand(tokens, context);
+        return parseQueryCommand(tokens, typeSystem);
     }
 
     protected QueryCommand parseQueryCommand( TokenStream tokens,
-                                              ExecutionContext context ) {
+                                              TypeSystem typeSystem ) {
         QueryCommand command = null;
         if (tokens.matches("SELECT")) {
-            command = parseQuery(tokens, context);
+            command = parseQuery(tokens, typeSystem);
             while (tokens.hasNext()) {
                 if (tokens.matchesAnyOf("UNION", "INTERSECT", "EXCEPT")) {
-                    command = parseSetQuery(tokens, command, context);
+                    command = parseSetQuery(tokens, command, typeSystem);
                 } else {
                     Position pos = tokens.previousPosition();
                     String msg = GraphI18n.unexpectedToken.text(tokens.consume(), pos.getLine(), pos.getColumn());
@@ -504,21 +498,21 @@
     }
 
     protected Query parseQuery( TokenStream tokens,
-                                ExecutionContext context ) {
+                                TypeSystem typeSystem ) {
         AtomicBoolean isDistinct = new AtomicBoolean(false);
-        List<ColumnExpression> columnExpressions = parseSelect(tokens, isDistinct, context);
-        Source source = parseFrom(tokens, context);
-        Constraint constraint = parseWhere(tokens, context, source);
+        List<ColumnExpression> columnExpressions = parseSelect(tokens, isDistinct, typeSystem);
+        Source source = parseFrom(tokens, typeSystem);
+        Constraint constraint = parseWhere(tokens, typeSystem, source);
         // Parse the order by and limit (can be in any order) ...
-        List<Ordering> orderings = parseOrderBy(tokens, context, source);
+        List<Ordering> orderings = parseOrderBy(tokens, typeSystem, source);
         Limit limit = parseLimit(tokens);
-        if (orderings == null) parseOrderBy(tokens, context, source);
+        if (orderings == null) parseOrderBy(tokens, typeSystem, source);
 
         // Convert the column expressions to columns ...
         List<Column> columns = new ArrayList<Column>(columnExpressions.size());
         for (ColumnExpression expression : columnExpressions) {
             SelectorName selectorName = expression.getSelectorName();
-            Name propertyName = nameFrom(expression.getPropertyName(), expression.getPosition(), context);
+            String propertyName = expression.getPropertyName();
             if (selectorName == null) {
                 if (source instanceof Selector) {
                     selectorName = ((Selector)source).getName();
@@ -536,7 +530,7 @@
 
     protected SetQuery parseSetQuery( TokenStream tokens,
                                       QueryCommand leftHandSide,
-                                      ExecutionContext context ) {
+                                      TypeSystem typeSystem ) {
         Operation operation = null;
         if (tokens.canConsume("UNION")) {
             operation = Operation.UNION;
@@ -548,13 +542,13 @@
         }
         boolean all = tokens.canConsume("ALL");
         // Parse the next select
-        QueryCommand rightQuery = parseQuery(tokens, context);
+        QueryCommand rightQuery = parseQuery(tokens, typeSystem);
         return new SetQuery(leftHandSide, operation, rightQuery, all);
     }
 
     protected List<ColumnExpression> parseSelect( TokenStream tokens,
                                                   AtomicBoolean isDistinct,
-                                                  ExecutionContext context ) {
+                                                  TypeSystem typeSystem ) {
         tokens.consume("SELECT");
         if (tokens.canConsume("DISTINCT")) isDistinct.set(true);
         if (tokens.canConsume('*')) {
@@ -578,7 +572,7 @@
     }
 
     protected Source parseFrom( TokenStream tokens,
-                                ExecutionContext context ) {
+                                TypeSystem typeSystem ) {
         Source source = null;
         tokens.consume("FROM");
         source = parseNamedSelector(tokens);
@@ -600,7 +594,7 @@
             // Read the name of the selector on the right side of the join ...
             NamedSelector right = parseNamedSelector(tokens);
             // Read the join condition ...
-            JoinCondition joinCondition = parseJoinCondition(tokens, context);
+            JoinCondition joinCondition = parseJoinCondition(tokens, typeSystem);
             // Create the join ...
             source = new Join(source, joinType, right, joinCondition);
         }
@@ -608,14 +602,14 @@
     }
 
     protected JoinCondition parseJoinCondition( TokenStream tokens,
-                                                ExecutionContext context ) {
+                                                TypeSystem typeSystem ) {
         tokens.consume("ON");
         if (tokens.canConsume("ISSAMENODE", "(")) {
             SelectorName selector1Name = parseSelectorName(tokens);
             tokens.consume(',');
             SelectorName selector2Name = parseSelectorName(tokens);
             if (tokens.canConsume('.')) {
-                Path path = parsePath(tokens, context);
+                String path = parsePath(tokens, typeSystem);
                 tokens.consume(')');
                 return new SameNodeJoinCondition(selector1Name, selector2Name, path);
             }
@@ -638,52 +632,52 @@
         }
         SelectorName selector1 = parseSelectorName(tokens);
         tokens.consume('.');
-        Name property1 = parseName(tokens, context);
+        String property1 = parseName(tokens, typeSystem);
         tokens.consume('=');
         SelectorName selector2 = parseSelectorName(tokens);
         tokens.consume('.');
-        Name property2 = parseName(tokens, context);
+        String property2 = parseName(tokens, typeSystem);
         return new EquiJoinCondition(selector1, property1, selector2, property2);
     }
 
     protected Constraint parseWhere( TokenStream tokens,
-                                     ExecutionContext context,
+                                     TypeSystem typeSystem,
                                      Source source ) {
         if (tokens.canConsume("WHERE")) {
-            return parseConstraint(tokens, context, source);
+            return parseConstraint(tokens, typeSystem, source);
         }
         return null;
     }
 
     protected Constraint parseConstraint( TokenStream tokens,
-                                          ExecutionContext context,
+                                          TypeSystem typeSystem,
                                           Source source ) {
         Constraint constraint = null;
         Position pos = tokens.nextPosition();
         if (tokens.canConsume("(")) {
-            constraint = parseConstraint(tokens, context, source);
+            constraint = parseConstraint(tokens, typeSystem, source);
             tokens.consume(")");
         } else if (tokens.canConsume("NOT")) {
             tokens.canConsume('(');
-            constraint = new Not(parseConstraint(tokens, context, source));
+            constraint = new Not(parseConstraint(tokens, typeSystem, source));
             tokens.canConsume(')');
         } else if (tokens.canConsume("CONTAINS", "(")) {
             // Either 'selectorName.propertyName', or 'selectorName.*' or 'propertyName' ...
             String first = tokens.consume();
             SelectorName selectorName = null;
-            Name propertyName = null;
+            String propertyName = null;
             if (tokens.canConsume(".", "*")) {
                 selectorName = new SelectorName(removeBracketsAndQuotes(first));
             } else if (tokens.canConsume('.')) {
                 selectorName = new SelectorName(removeBracketsAndQuotes(first));
-                propertyName = parseName(tokens, context);
+                propertyName = parseName(tokens, typeSystem);
             } else {
                 if (!(source instanceof Selector)) {
                     String msg = GraphI18n.functionIsAmbiguous.text("CONTAINS()", pos.getLine(), pos.getColumn());
                     throw new ParsingException(pos, msg);
                 }
                 selectorName = ((Selector)source).getName();
-                propertyName = nameFrom(first, pos, context);
+                propertyName = first;
             }
             tokens.consume(',');
 
@@ -704,7 +698,7 @@
                 selectorName = parseSelectorName(tokens);
                 tokens.consume(',');
             }
-            Path path = parsePath(tokens, context);
+            String path = parsePath(tokens, typeSystem);
             tokens.consume(')');
             constraint = new SameNode(selectorName, path);
         } else if (tokens.canConsume("ISCHILDNODE", "(")) {
@@ -719,7 +713,7 @@
                 selectorName = parseSelectorName(tokens);
                 tokens.consume(',');
             }
-            Path path = parsePath(tokens, context);
+            String path = parsePath(tokens, typeSystem);
             tokens.consume(')');
             constraint = new ChildNode(selectorName, path);
         } else if (tokens.canConsume("ISDESCENDANTNODE", "(")) {
@@ -734,41 +728,41 @@
                 selectorName = parseSelectorName(tokens);
                 tokens.consume(',');
             }
-            Path path = parsePath(tokens, context);
+            String path = parsePath(tokens, typeSystem);
             tokens.consume(')');
             constraint = new DescendantNode(selectorName, path);
         } else {
             // First try a property existance ...
             Position pos2 = tokens.nextPosition();
-            constraint = parsePropertyExistance(tokens, context, source);
+            constraint = parsePropertyExistance(tokens, typeSystem, source);
             if (constraint == null) {
                 // Try to parse as a dynamic operand ...
-                DynamicOperand left = parseDynamicOperand(tokens, context, source);
+                DynamicOperand left = parseDynamicOperand(tokens, typeSystem, source);
                 if (left != null) {
                     if (tokens.matches('(') && left instanceof PropertyValue) {
                         // This was probably a bad function that we parsed as the start of a dynamic operation ...
-                        String name = ((PropertyValue)left).getPropertyName().getLocalName(); // this may be the function name
+                        String name = ((PropertyValue)left).getPropertyName(); // this may be the function name
                         String msg = GraphI18n.expectingConstraintCondition.text(name, pos2.getLine(), pos2.getColumn());
                         throw new ParsingException(pos, msg);
                     }
                     if (tokens.matches("IN", "(") || tokens.matches("NOT", "IN", "(")) {
                         boolean not = tokens.canConsume("NOT");
-                        Collection<StaticOperand> staticOperands = parseInClause(tokens, context);
+                        Collection<StaticOperand> staticOperands = parseInClause(tokens, typeSystem);
                         constraint = new SetCriteria(left, staticOperands);
                         if (not) constraint = new Not(constraint);
                     } else if (tokens.matches("BETWEEN") || tokens.matches("NOT", "BETWEEN")) {
                         boolean not = tokens.canConsume("NOT");
                         tokens.consume("BETWEEN");
-                        StaticOperand lowerBound = parseStaticOperand(tokens, context);
+                        StaticOperand lowerBound = parseStaticOperand(tokens, typeSystem);
                         boolean lowerInclusive = !tokens.canConsume("EXCLUSIVE");
                         tokens.consume("AND");
-                        StaticOperand upperBound = parseStaticOperand(tokens, context);
+                        StaticOperand upperBound = parseStaticOperand(tokens, typeSystem);
                         boolean upperInclusive = !tokens.canConsume("EXCLUSIVE");
                         constraint = new Between(left, lowerBound, upperBound, lowerInclusive, upperInclusive);
                         if (not) constraint = new Not(constraint);
                     } else {
                         Operator operator = parseComparisonOperator(tokens);
-                        StaticOperand right = parseStaticOperand(tokens, context);
+                        StaticOperand right = parseStaticOperand(tokens, typeSystem);
                         constraint = new Comparison(left, operator, right);
                     }
                 }
@@ -781,23 +775,23 @@
         }
         // AND has higher precedence than OR, so we need to evaluate it first ...
         while (tokens.canConsume("AND")) {
-            constraint = new And(constraint, parseConstraint(tokens, context, source));
+            constraint = new And(constraint, parseConstraint(tokens, typeSystem, source));
         }
         while (tokens.canConsume("OR")) {
-            constraint = new Or(constraint, parseConstraint(tokens, context, source));
+            constraint = new Or(constraint, parseConstraint(tokens, typeSystem, source));
         }
         return constraint;
     }
 
     protected List<StaticOperand> parseInClause( TokenStream tokens,
-                                                 ExecutionContext context ) {
+                                                 TypeSystem typeSystem ) {
         List<StaticOperand> result = new ArrayList<StaticOperand>();
         tokens.consume("IN");
         tokens.consume("(");
         if (!tokens.canConsume(")")) {
             // Not empty, so read the static operands ...
             do {
-                result.add(parseStaticOperand(tokens, context));
+                result.add(parseStaticOperand(tokens, typeSystem));
             } while (tokens.canConsume(','));
             tokens.consume(")");
         }
@@ -833,12 +827,12 @@
     }
 
     protected List<Ordering> parseOrderBy( TokenStream tokens,
-                                           ExecutionContext context,
+                                           TypeSystem typeSystem,
                                            Source source ) {
         if (tokens.canConsume("ORDER", "BY")) {
             List<Ordering> orderings = new ArrayList<Ordering>();
             do {
-                orderings.add(parseOrdering(tokens, context, source));
+                orderings.add(parseOrdering(tokens, typeSystem, source));
             } while (tokens.canConsume(','));
             return orderings;
         }
@@ -846,9 +840,9 @@
     }
 
     protected Ordering parseOrdering( TokenStream tokens,
-                                      ExecutionContext context,
+                                      TypeSystem typeSystem,
                                       Source source ) {
-        DynamicOperand operand = parseDynamicOperand(tokens, context, source);
+        DynamicOperand operand = parseDynamicOperand(tokens, typeSystem, source);
         Order order = Order.ASCENDING;
         if (tokens.canConsume("DESC")) order = Order.DESCENDING;
         if (tokens.canConsume("ASC")) order = Order.ASCENDING;
@@ -856,7 +850,7 @@
     }
 
     protected Constraint parsePropertyExistance( TokenStream tokens,
-                                                 ExecutionContext context,
+                                                 TypeSystem typeSystem,
                                                  Source source ) {
         if (tokens.matches(ANY_VALUE, ".", ANY_VALUE, "IS", "NOT", "NULL")
             || tokens.matches(ANY_VALUE, ".", ANY_VALUE, "IS", "NULL") || tokens.matches(ANY_VALUE, "IS", "NOT", "NULL")
@@ -864,11 +858,11 @@
             Position pos = tokens.nextPosition();
             String firstWord = tokens.consume();
             SelectorName selectorName = null;
-            Name propertyName = null;
+            String propertyName = null;
             if (tokens.canConsume('.')) {
                 // We actually read the selector name, so now read the property name ...
                 selectorName = new SelectorName(firstWord);
-                propertyName = parseName(tokens, context);
+                propertyName = parseName(tokens, typeSystem);
             } else {
                 // Otherwise the source should be a single named selector
                 if (!(source instanceof Selector)) {
@@ -876,7 +870,7 @@
                     throw new ParsingException(pos, msg);
                 }
                 selectorName = ((Selector)source).getName();
-                propertyName = nameFrom(firstWord, pos, context);
+                propertyName = firstWord;
             }
             if (tokens.canConsume("IS", "NOT", "NULL")) {
                 return new PropertyExistence(selectorName, propertyName);
@@ -888,7 +882,7 @@
     }
 
     protected StaticOperand parseStaticOperand( TokenStream tokens,
-                                                ExecutionContext context ) {
+                                                TypeSystem typeSystem ) {
         if (tokens.canConsume('$')) {
             // The variable name must conform to a valid prefix, which is defined as a valid NCName ...
             String value = tokens.consume();
@@ -899,66 +893,52 @@
             }
             return new BindVariableName(value);
         }
-        return parseLiteral(tokens, context);
+        return parseLiteral(tokens, typeSystem);
     }
 
     protected Literal parseLiteral( TokenStream tokens,
-                                    ExecutionContext context ) {
+                                    TypeSystem typeSystem ) {
         if (tokens.canConsume("CAST", "(")) {
             // Get the value that is to be cast ...
             Position pos = tokens.nextPosition();
-            String value = parseLiteralValue(tokens, context);
+            String value = parseLiteralValue(tokens, typeSystem);
             // Figure out the type we're supposed to cast to ...
-            ValueFactories factories = context.getValueFactories();
-            ValueFactory<?> factory = factories.getStringFactory();
             tokens.consume("AS");
-            if (tokens.canConsume("STRING")) factory = factories.getStringFactory();
-            else if (tokens.canConsume("BINARY")) factory = factories.getBinaryFactory();
-            else if (tokens.canConsume("DATE")) factory = factories.getDateFactory();
-            else if (tokens.canConsume("LONG")) factory = factories.getLongFactory();
-            else if (tokens.canConsume("DOUBLE")) factory = factories.getDoubleFactory();
-            else if (tokens.canConsume("DECIMAL")) factory = factories.getDecimalFactory();
-            else if (tokens.canConsume("BOOLEAN")) factory = factories.getBooleanFactory();
-            else if (tokens.canConsume("NAME")) factory = factories.getNameFactory();
-            else if (tokens.canConsume("PATH")) factory = factories.getPathFactory();
-            else if (tokens.canConsume("REFERENCE")) factory = factories.getReferenceFactory();
-            else if (tokens.canConsume("WEAKREFERENCE")) factory = factories.getPathFactory();
-            else if (tokens.canConsume("URI")) factory = factories.getUriFactory();
-            else {
-                Position typePos = tokens.nextPosition();
+            String typeName = tokens.consume();
+            TypeFactory<?> typeFactory = typeSystem.getTypeFactory(typeName);
+            if (typeFactory == null) {
+                Position typePos = tokens.previousPosition();
                 String msg = GraphI18n.invalidPropertyType.text(tokens.consume(), typePos.getLine(), typePos.getColumn());
                 throw new ParsingException(typePos, msg);
             }
             // Convert the supplied value to the desired value ...
             tokens.consume(')');
             try {
-                Object literal = factory.create(value);
-                if (literal instanceof DateTime) {
-                    // Convert the timestamp to UTC, since that's how everything should be stored ...
-                    literal = ((DateTime)literal).toUtcTimeZone();
-                }
+                Object literal = typeFactory.create(value);
                 return new Literal(literal);
             } catch (ValueFormatException e) {
                 String msg = GraphI18n.valueCannotBeCastToSpecifiedType.text(value,
                                                                              pos.getLine(),
                                                                              pos.getColumn(),
-                                                                             factory.getPropertyType().name(),
+                                                                             typeFactory.getTypeName(),
                                                                              e.getMessage());
                 throw new ParsingException(pos, msg);
             }
         }
         // Just create a literal out of the supplied value ...
-        return new Literal(parseLiteralValue(tokens, context));
+        return new Literal(parseLiteralValue(tokens, typeSystem));
     }
 
     protected String parseLiteralValue( TokenStream tokens,
-                                        ExecutionContext context ) {
+                                        TypeSystem typeSystem ) {
         if (tokens.matches(SqlTokenizer.QUOTED_STRING)) {
             return removeBracketsAndQuotes(tokens.consume());
         }
-        ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
-        if (tokens.canConsume("TRUE")) return stringFactory.create(Boolean.TRUE);
-        if (tokens.canConsume("FALSE")) return stringFactory.create(Boolean.FALSE);
+        TypeFactory<Boolean> booleanFactory = typeSystem.getBooleanFactory();
+        if (booleanFactory != null) {
+            if (tokens.canConsume("TRUE")) return booleanFactory.asString(Boolean.TRUE);
+            if (tokens.canConsume("FALSE")) return booleanFactory.asString(Boolean.FALSE);
+        }
 
         // Otherwise it is an unquoted literal value ...
         Position pos = tokens.nextPosition();
@@ -968,95 +948,100 @@
 
         // Try to parse this value as a number ...
         String integral = tokens.consume();
-        String decimal = null;
-        if (tokens.canConsume('.')) {
-            decimal = tokens.consume();
-            String value = sign + integral + "." + decimal;
-            if (decimal.endsWith("e") && (tokens.matches('+') || tokens.matches('-'))) {
-                // There's more to the number ...
-                value = value + tokens.consume() + tokens.consume(); // +/-EXP
+        TypeFactory<Double> doubleFactory = typeSystem.getDoubleFactory();
+        if (doubleFactory != null) {
+            String decimal = null;
+            if (tokens.canConsume('.')) {
+                decimal = tokens.consume();
+                String value = sign + integral + "." + decimal;
+                if (decimal.endsWith("e") && (tokens.matches('+') || tokens.matches('-'))) {
+                    // There's more to the number ...
+                    value = value + tokens.consume() + tokens.consume(); // +/-EXP
+                }
+                try {
+                    // Convert to a double and then back to a string to get canonical form ...
+                    return doubleFactory.asString(doubleFactory.create(value));
+                } catch (ValueFormatException e) {
+                    String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
+                    throw new ParsingException(pos, msg);
+                }
             }
-            try {
-                // Convert to a double and then back to a string to get canonical form ...
-                return stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
-            } catch (ValueFormatException e) {
-                String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
-                throw new ParsingException(pos, msg);
-            }
         }
-        if (tokens.canConsume('-')) {
-            // Looks like a date (see Section 3.6.4.3 of the JCR 2.0 specification) ...
-            // sYYYY-MM-DDThh:mm:ss.sssTZD
-            String year = integral;
-            String month = tokens.consume();
-            tokens.consume('-');
-            String dateAndHour = tokens.consume();
-            tokens.consume(':');
-            String minutes = tokens.consume();
-            tokens.consume(':');
-            String seconds = tokens.consume();
-            tokens.consume('.');
-            String subSeconds = tokens.consume(); // should contain 'T' separator and possibly the TZ name and (if no +/-)
-            // hours
-            String tzSign = "+";
-            String tzHours = "00";
-            String tzMinutes = "00";
-            String tzDelim = ":";
-            if (tokens.canConsume('+')) {
-                // the fractionalSeconds did NOT contain the tzHours ...
-                tzHours = tokens.consume();
-                if (tokens.canConsume(':')) tzMinutes = tokens.consume();
-            } else if (tokens.canConsume('-')) {
-                // the fractionalSeconds did NOT contain the tzHours ...
-                tzSign = "-";
-                tzHours = tokens.consume();
-                if (tokens.canConsume(':')) tzMinutes = tokens.consume();
-            } else if (tokens.canConsume(':')) {
-                // fractionalSeconds DID contain the TZ hours (without + or -)
-                tzHours = tzSign = "";
-                if (tokens.canConsume(':')) tzMinutes = tokens.consume();
-            } else if (subSeconds.endsWith("Z")) {
-                tzSign = tzMinutes = tzDelim = tzHours = "";
-            } else if (subSeconds.endsWith("UTC")) {
-                subSeconds = subSeconds.length() > 3 ? subSeconds.substring(0, subSeconds.length() - 3) : subSeconds;
+        TypeFactory<?> dateTimeFactory = typeSystem.getDateTimeFactory();
+        if (dateTimeFactory != null) {
+            if (tokens.canConsume('-')) {
+                // Looks like a date (see Section 3.6.4.3 of the JCR 2.0 specification) ...
+                // sYYYY-MM-DDThh:mm:ss.sssTZD
+                String year = integral;
+                String month = tokens.consume();
+                tokens.consume('-');
+                String dateAndHour = tokens.consume();
+                tokens.consume(':');
+                String minutes = tokens.consume();
+                tokens.consume(':');
+                String seconds = tokens.consume();
+                tokens.consume('.');
+                String subSeconds = tokens.consume(); // should contain 'T' separator and possibly the TZ name and (if no +/-)
+                // hours
+                String tzSign = "+";
+                String tzHours = "00";
+                String tzMinutes = "00";
+                String tzDelim = ":";
+                if (tokens.canConsume('+')) {
+                    // the fractionalSeconds did NOT contain the tzHours ...
+                    tzHours = tokens.consume();
+                    if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+                } else if (tokens.canConsume('-')) {
+                    // the fractionalSeconds did NOT contain the tzHours ...
+                    tzSign = "-";
+                    tzHours = tokens.consume();
+                    if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+                } else if (tokens.canConsume(':')) {
+                    // fractionalSeconds DID contain the TZ hours (without + or -)
+                    tzHours = tzSign = "";
+                    if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+                } else if (subSeconds.endsWith("Z")) {
+                    tzSign = tzMinutes = tzDelim = tzHours = "";
+                } else if (subSeconds.endsWith("UTC")) {
+                    subSeconds = subSeconds.length() > 3 ? subSeconds.substring(0, subSeconds.length() - 3) : subSeconds;
+                }
+                String value = sign + year + "-" + month + "-" + dateAndHour + ":" + minutes + ":" + seconds + "." + subSeconds
+                               + tzSign + tzHours + tzDelim + tzMinutes;
+                try {
+                    // Convert to a date and then back to a string to get canonical form ...
+                    Object dateTime = dateTimeFactory.create(value);
+                    return dateTimeFactory.asString(dateTime);
+                } catch (ValueFormatException e) {
+                    String msg = GraphI18n.expectingLiteralAndUnableToParseAsDate.text(value, pos.getLine(), pos.getColumn());
+                    throw new ParsingException(pos, msg);
+                }
             }
-            String value = sign + year + "-" + month + "-" + dateAndHour + ":" + minutes + ":" + seconds + "." + subSeconds
-                           + tzSign + tzHours + tzDelim + tzMinutes;
-            try {
-                // Convert to a date and then back to a string to get canonical form ...
-                DateTime dateTime = context.getValueFactories().getDateFactory().create(value);
-                dateTime = dateTime.toUtcTimeZone();
-                return stringFactory.create(dateTime);
-            } catch (ValueFormatException e) {
-                String msg = GraphI18n.expectingLiteralAndUnableToParseAsDate.text(value, pos.getLine(), pos.getColumn());
-                throw new ParsingException(pos, msg);
-            }
         }
+        TypeFactory<Long> longFactory = typeSystem.getLongFactory();
         // try to parse an a long ...
         String value = sign + integral;
         try {
             // Convert to a long and then back to a string to get canonical form ...
-            return stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+            return longFactory.asString(longFactory.create(value));
         } catch (ValueFormatException e) {
             String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());
             throw new ParsingException(pos, msg);
         }
-
     }
 
     protected DynamicOperand parseDynamicOperand( TokenStream tokens,
-                                                  ExecutionContext context,
+                                                  TypeSystem typeSystem,
                                                   Source source ) {
         DynamicOperand result = null;
         Position pos = tokens.nextPosition();
         if (tokens.canConsume("LENGTH", "(")) {
-            result = new Length(parsePropertyValue(tokens, context, source));
+            result = new Length(parsePropertyValue(tokens, typeSystem, source));
             tokens.consume(")");
         } else if (tokens.canConsume("LOWER", "(")) {
-            result = new LowerCase(parseDynamicOperand(tokens, context, source));
+            result = new LowerCase(parseDynamicOperand(tokens, typeSystem, source));
             tokens.consume(")");
         } else if (tokens.canConsume("UPPER", "(")) {
-            result = new UpperCase(parseDynamicOperand(tokens, context, source));
+            result = new UpperCase(parseDynamicOperand(tokens, typeSystem, source));
             tokens.consume(")");
         } else if (tokens.canConsume("NAME", "(")) {
             if (tokens.canConsume(")")) {
@@ -1109,13 +1094,13 @@
             result = new NodePath(parseSelectorName(tokens));
             tokens.consume(")");
         } else {
-            result = parsePropertyValue(tokens, context, source);
+            result = parsePropertyValue(tokens, typeSystem, source);
         }
         return result;
     }
 
     protected PropertyValue parsePropertyValue( TokenStream tokens,
-                                                ExecutionContext context,
+                                                TypeSystem typeSystem,
                                                 Source source ) {
         Position pos = tokens.nextPosition();
         String firstWord = removeBracketsAndQuotes(tokens.consume());
@@ -1123,13 +1108,13 @@
         if (tokens.canConsume('.')) {
             // We actually read the selector name, so now read the property name ...
             selectorName = new SelectorName(firstWord);
-            Name propertyName = parseName(tokens, context);
+            String propertyName = parseName(tokens, typeSystem);
             return new PropertyValue(selectorName, propertyName);
         }
         // Otherwise the source should be a single named selector
         if (source instanceof Selector) {
             selectorName = ((Selector)source).getName();
-            return new PropertyValue(selectorName, nameFrom(firstWord, pos, context));
+            return new PropertyValue(selectorName, firstWord);
         }
         String msg = GraphI18n.mustBeScopedAtLineAndColumn.text(firstWord, pos.getLine(), pos.getColumn());
         throw new ParsingException(pos, msg);
@@ -1196,40 +1181,16 @@
         return new SelectorName(removeBracketsAndQuotes(tokens.consume()));
     }
 
-    protected Path parsePath( TokenStream tokens,
-                              ExecutionContext context ) {
-        return pathFrom(removeBracketsAndQuotes(tokens.consume()), tokens.previousPosition(), context);
+    protected String parsePath( TokenStream tokens,
+                                TypeSystem typeSystem ) {
+        return removeBracketsAndQuotes(tokens.consume());
     }
 
-    protected Name parseName( TokenStream tokens,
-                              ExecutionContext context ) {
-        return nameFrom(removeBracketsAndQuotes(tokens.consume()), tokens.previousPosition(), context);
+    protected String parseName( TokenStream tokens,
+                                TypeSystem typeSystem ) {
+        return removeBracketsAndQuotes(tokens.consume());
     }
 
-    protected Path pathFrom( String name,
-                             Position position,
-                             ExecutionContext context ) {
-        PathFactory pathFactory = context.getValueFactories().getPathFactory();
-        try {
-            return pathFactory.create(name);
-        } catch (ValueFormatException e) {
-            String msg = GraphI18n.expectingValidPathAtLineAndColumn.text(name, position.getLine(), position.getColumn());
-            throw new ParsingException(position, msg);
-        }
-    }
-
-    protected Name nameFrom( String name,
-                             Position position,
-                             ExecutionContext context ) {
-        NameFactory nameFactory = context.getValueFactories().getNameFactory();
-        try {
-            return nameFactory.create(removeBracketsAndQuotes(name));
-        } catch (ValueFormatException e) {
-            String msg = GraphI18n.expectingValidNameAtLineAndColumn.text(name, position.getLine(), position.getColumn());
-            throw new ParsingException(position, msg);
-        }
-    }
-
     /**
      * A {@link TokenStream.Tokenizer} implementation that parses words, quoted phrases, comments, and symbols. Words are
      * delimited by whitespace and consist only of alpha-number characters plus the underscore character. Quoted phrases are

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,9 +29,6 @@
 import java.util.List;
 import java.util.Map;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.And;
@@ -223,8 +220,7 @@
                 }
                 node.setProperty(Property.SOURCE_COLUMNS, table.getColumns());
             } else {
-                context.getProblems().addError(GraphI18n.tableDoesNotExist,
-                                               selector.getName().getString(context.getExecutionContext()));
+                context.getProblems().addError(GraphI18n.tableDoesNotExist, selector.getName());
             }
             return node;
         }
@@ -395,20 +391,18 @@
         if (columns.isEmpty()) {
             columns = new LinkedList<Column>();
             // SELECT *, so find all of the columns that are available from all the sources ...
-            NameFactory nameFactory = context.getExecutionContext().getValueFactories().getNameFactory();
             for (Table table : selectors.values()) {
                 // Add the selector that is being used ...
                 projectNode.addSelector(table.getName());
                 // Compute the columns from this selector ...
                 for (Schemata.Column column : table.getColumns()) {
                     String columnName = column.getName();
-                    Name propertyName = nameFactory.create(columnName);
+                    String propertyName = columnName;
                     columns.add(new Column(table.getName(), propertyName, columnName));
                 }
             }
         } else {
             // Add the selector used by each column ...
-            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
             for (Column column : columns) {
                 SelectorName tableName = column.getSelectorName();
                 // Add the selector that is being used ...
@@ -417,16 +411,13 @@
                 // Verify that each column is available in the appropriate source ...
                 Table table = selectors.get(tableName);
                 if (table == null) {
-                    context.getProblems().addError(GraphI18n.tableDoesNotExist,
-                                                   tableName.getString(context.getExecutionContext()));
+                    context.getProblems().addError(GraphI18n.tableDoesNotExist, tableName);
                 } else {
                     // Make sure that the column is in the table ...
-                    Name columnName = column.getPropertyName();
-                    String name = stringFactory.create(columnName);
+                    String columnName = column.getPropertyName();
+                    String name = columnName;
                     if (table.getColumn(name) == null) {
-                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable,
-                                                       name,
-                                                       tableName.getString(context.getExecutionContext()));
+                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, name, tableName);
                     }
                 }
             }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -37,7 +37,6 @@
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.ObjectUtil;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
 import org.jboss.dna.graph.query.model.JoinCondition;
@@ -915,7 +914,7 @@
      */
     @Override
     public String toString() {
-        return getString(ExecutionContext.DEFAULT_CONTEXT);
+        return getString();
     }
 
     /**
@@ -980,30 +979,28 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+     * @see org.jboss.dna.graph.query.model.Readable#getString()
      */
-    public String getString( ExecutionContext context ) {
+    public String getString() {
         StringBuilder sb = new StringBuilder();
-        getRecursiveString(context, sb, 0);
+        getRecursiveString(sb, 0);
         return sb.toString();
     }
 
-    private void getRecursiveString( ExecutionContext context,
-                                     StringBuilder str,
+    private void getRecursiveString( StringBuilder str,
                                      int indentLevel ) {
         for (int i = 0; i < indentLevel; ++i) {
             str.append("  ");
         }
-        getNodeString(context, str).append('\n');
+        getNodeString(str).append('\n');
 
         // Recursively add children at one greater tab level
         for (PlanNode child : this) {
-            child.getRecursiveString(context, str, indentLevel + 1);
+            child.getRecursiveString(str, indentLevel + 1);
         }
     }
 
-    private StringBuilder getNodeString( ExecutionContext context,
-                                         StringBuilder str ) {
+    private StringBuilder getNodeString( StringBuilder str ) {
         str.append(this.type.getSymbol());
         if (!selectors.isEmpty()) {
             str.append(" [");
@@ -1024,7 +1021,7 @@
                 str.append(entry.getKey()).append('=');
                 Object value = entry.getValue();
                 if (value instanceof Visitable) {
-                    str.append(Visitors.readable((Visitable)value, context));
+                    str.append(Visitors.readable((Visitable)value));
                 } else if (value instanceof Collection) {
                     boolean firstItem = true;
                     str.append('[');
@@ -1032,7 +1029,7 @@
                         if (firstItem) firstItem = false;
                         else str.append(", ");
                         if (item instanceof Visitable) {
-                            str.append(Visitors.readable((Visitable)item, context));
+                            str.append(Visitors.readable((Visitable)item));
                         } else {
                             str.append(item);
                         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -31,9 +31,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.Between;
@@ -124,7 +121,7 @@
         }
 
         // Add the PROJECT columns first ...
-        RequiredColumnVisitor collectionVisitor = new RequiredColumnVisitor(context.getExecutionContext(), names);
+        RequiredColumnVisitor collectionVisitor = new RequiredColumnVisitor(names);
         if (columns != null) {
             for (Column projectedColumn : columns) {
                 collectionVisitor.visit(projectedColumn);
@@ -157,15 +154,12 @@
     }
 
     protected static class RequiredColumnVisitor extends AbstractVisitor {
-        private final ExecutionContext context;
         private final Set<SelectorName> names;
         private final List<Column> columns = new LinkedList<Column>();
-        private final Set<Name> requiredColumnNames = new HashSet<Name>();
+        private final Set<String> requiredColumnNames = new HashSet<String>();
 
-        protected RequiredColumnVisitor( ExecutionContext context,
-                                         Set<SelectorName> names ) {
+        protected RequiredColumnVisitor( Set<SelectorName> names ) {
             this.names = names;
-            this.context = context;
         }
 
         /**
@@ -210,11 +204,11 @@
         }
 
         protected void requireColumn( SelectorName selector,
-                                      Name propertyName ) {
+                                      String propertyName ) {
             if (names.contains(selector)) {
                 // The column is part of the table we're interested in ...
                 if (requiredColumnNames.add(propertyName)) {
-                    String alias = context.getValueFactories().getStringFactory().create(propertyName);
+                    String alias = propertyName;
                     columns.add(new Column(selector, propertyName, alias));
                 }
             }
@@ -496,7 +490,6 @@
 
         // Walk up the plan to change any references to the view columns into references to the source columns...
         PlanNode node = topOfViewInPlan;
-        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
         List<PlanNode> potentiallyRemovableSources = new LinkedList<PlanNode>();
         do {
             // Remove the view from the selectors ...
@@ -510,7 +503,7 @@
                                 Column column = columns.get(i);
                                 if (column.getSelectorName().equals(viewName)) {
                                     // This column references the view ...
-                                    String columnName = stringFactory.create(column.getPropertyName());
+                                    String columnName = column.getPropertyName();
                                     String columnAlias = column.getColumnName();
                                     // Find the source column that this view column corresponds to ...
                                     Column sourceColumn = mappings.getMappedColumn(columnName);
@@ -647,8 +640,7 @@
         if (constraint instanceof PropertyExistence) {
             PropertyExistence existence = (PropertyExistence)constraint;
             if (!mapping.getOriginalName().equals(existence.getSelectorName())) return existence;
-            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(existence.getPropertyName()));
+            Column sourceColumn = mapping.getMappedColumn(existence.getPropertyName());
             if (sourceColumn == null) return existence;
             node.addSelector(sourceColumn.getSelectorName());
             return new PropertyExistence(sourceColumn.getSelectorName(), sourceColumn.getPropertyName());
@@ -656,8 +648,7 @@
         if (constraint instanceof FullTextSearch) {
             FullTextSearch search = (FullTextSearch)constraint;
             if (!mapping.getOriginalName().equals(search.getSelectorName())) return search;
-            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(search.getPropertyName()));
+            Column sourceColumn = mapping.getMappedColumn(search.getPropertyName());
             if (sourceColumn == null) return search;
             node.addSelector(sourceColumn.getSelectorName());
             return new FullTextSearch(sourceColumn.getSelectorName(), sourceColumn.getPropertyName(),
@@ -722,8 +713,7 @@
         if (operand instanceof PropertyValue) {
             PropertyValue value = (PropertyValue)operand;
             if (!mapping.getOriginalName().equals(value.getSelectorName())) return value;
-            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(value.getPropertyName()));
+            Column sourceColumn = mapping.getMappedColumn(value.getPropertyName());
             if (sourceColumn == null) return value;
             node.addSelector(sourceColumn.getSelectorName());
             return new PropertyValue(sourceColumn.getSelectorName(), sourceColumn.getPropertyName());
@@ -753,19 +743,17 @@
             EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
             SelectorName replacement1 = condition.getSelector1Name();
             SelectorName replacement2 = condition.getSelector2Name();
-            Name property1 = condition.getProperty1Name();
-            Name property2 = condition.getProperty2Name();
+            String property1 = condition.getProperty1Name();
+            String property2 = condition.getProperty2Name();
             if (replacement1.equals(mapping.getOriginalName())) {
-                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-                Column sourceColumn = mapping.getMappedColumn(stringFactory.create(property1));
+                Column sourceColumn = mapping.getMappedColumn(property1);
                 if (sourceColumn != null) {
                     replacement1 = sourceColumn.getSelectorName();
                     property1 = sourceColumn.getPropertyName();
                 }
             }
             if (replacement2.equals(mapping.getOriginalName())) {
-                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-                Column sourceColumn = mapping.getMappedColumn(stringFactory.create(property2));
+                Column sourceColumn = mapping.getMappedColumn(property2);
                 if (sourceColumn != null) {
                     replacement2 = sourceColumn.getSelectorName();
                     property2 = sourceColumn.getPropertyName();

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,8 +27,6 @@
 import java.util.Collections;
 import java.util.List;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.AllNodes;
@@ -80,11 +78,10 @@
                 // Get the columns from the source columns ...
                 List<Schemata.Column> schemataColumns = source.getPropertyAsList(Property.SOURCE_COLUMNS, Schemata.Column.class);
                 this.projectedColumns = new ArrayList<Column>(schemataColumns.size());
-                NameFactory nameFactory = context.getExecutionContext().getValueFactories().getNameFactory();
                 for (Schemata.Column schemataColumn : schemataColumns) {
                     String columnName = schemataColumn.getName();
                     // PropertyType type = schemataColumn.getPropertyType();
-                    Name propertyName = nameFactory.create(columnName);
+                    String propertyName = columnName;
                     Column column = new Column(sourceName, propertyName, columnName);
                     this.projectedColumns.add(column);
                 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,11 +27,8 @@
 import java.util.Comparator;
 import java.util.List;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.property.ValueComparators;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
@@ -42,6 +39,8 @@
 import org.jboss.dna.graph.query.model.JoinType;
 import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.TypeSystem;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
@@ -166,8 +165,7 @@
                 /**
                  * {@inheritDoc}
                  * 
-                 * @see org.jboss.dna.graph.query.process.JoinComponent.TupleMerger#merge(java.lang.Object[],
-                 *      java.lang.Object[])
+                 * @see org.jboss.dna.graph.query.process.JoinComponent.TupleMerger#merge(java.lang.Object[], java.lang.Object[])
                  */
                 public Object[] merge( Object[] leftTuple,
                                        Object[] rightTuple ) {
@@ -275,12 +273,12 @@
         } else if (condition instanceof EquiJoinCondition) {
             EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
             SelectorName selector1Name = joinCondition.getSelector1Name();
-            Name propName1 = joinCondition.getProperty1Name();
+            String propName1 = joinCondition.getProperty1Name();
             if (source.getColumns().hasSelector(selector1Name.getName())) {
                 return selectValue(source, selector1Name, propName1);
             }
             SelectorName selector2Name = joinCondition.getSelector2Name();
-            Name propName2 = joinCondition.getProperty2Name();
+            String propName2 = joinCondition.getProperty2Name();
             return selectValue(source, selector2Name, propName2);
         }
         throw new IllegalArgumentException();
@@ -299,7 +297,7 @@
 
     private static ValueSelector selectValue( ProcessingComponent component,
                                               SelectorName selectorName,
-                                              Name propertyName ) {
+                                              String propertyName ) {
         final int index = component.getColumns().getColumnIndexForProperty(selectorName.getName(), propertyName);
         return new ValueSelector() {
             public Object evaluate( Object[] tuple ) {
@@ -457,20 +455,26 @@
             EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
             SelectorName leftSelectorName = joinCondition.getSelector1Name();
             SelectorName rightSelectorName = joinCondition.getSelector2Name();
-            Name leftPropertyName = joinCondition.getProperty1Name();
-            Name rightPropertyName = joinCondition.getProperty2Name();
+            String leftPropertyName = joinCondition.getProperty1Name();
+            String rightPropertyName = joinCondition.getProperty2Name();
 
-            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
             Schemata schemata = context.getSchemata();
             Schemata.Table leftTable = schemata.getTable(leftSelectorName);
-            Schemata.Column leftColumn = leftTable.getColumn(stringFactory.create(leftPropertyName));
-            PropertyType leftType = leftColumn.getPropertyType();
+            Schemata.Column leftColumn = leftTable.getColumn(leftPropertyName);
+            String leftType = leftColumn.getPropertyType();
 
             Schemata.Table rightTable = schemata.getTable(rightSelectorName);
-            Schemata.Column rightColumn = rightTable.getColumn(stringFactory.create(rightPropertyName));
-            PropertyType rightType = rightColumn.getPropertyType();
+            Schemata.Column rightColumn = rightTable.getColumn(rightPropertyName);
+            String rightType = rightColumn.getPropertyType();
 
-            return leftType == rightType ? (Comparator<Object>)leftType.getComparator() : ValueComparators.OBJECT_COMPARATOR;
+            TypeSystem typeSystem = context.getTypeSystem();
+            if (leftType.equals(rightType)) {
+                TypeFactory<?> typeFactory = typeSystem.getTypeFactory(leftType);
+                if (typeFactory != null) {
+                    return (Comparator<Object>)typeFactory.getComparator();
+                }
+            }
+            return typeSystem.getDefaultComparator();
         }
         throw new IllegalArgumentException();
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,11 +29,7 @@
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.DynamicOperand;
@@ -45,7 +41,9 @@
 import org.jboss.dna.graph.query.model.NodeName;
 import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.UpperCase;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 import org.jboss.dna.graph.query.validate.Schemata.Column;
 import org.jboss.dna.graph.query.validate.Schemata.Table;
 
@@ -126,7 +124,7 @@
          * 
          * @return the property type; never null
          */
-        PropertyType getExpectedType();
+        String getExpectedType();
 
         /**
          * Perform the dynamic evaluation to obtain the desired result.
@@ -151,18 +149,17 @@
         assert operand != null;
         assert columns != null;
         assert context != null;
-        final ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
         if (operand instanceof PropertyValue) {
             PropertyValue propValue = (PropertyValue)operand;
-            Name propertyName = propValue.getPropertyName();
+            String propertyName = propValue.getPropertyName();
             String selectorName = propValue.getSelectorName().getName();
             final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
             // Find the expected property type of the value ...
             Table table = context.getSchemata().getTable(propValue.getSelectorName());
-            Column schemaColumn = table.getColumn(stringFactory.create(propertyName));
-            final PropertyType expectedType = schemaColumn.getPropertyType();
+            Column schemaColumn = table.getColumn(propertyName);
+            final String expectedType = schemaColumn.getPropertyType();
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
+                public String getExpectedType() {
                     return expectedType;
                 }
 
@@ -171,25 +168,27 @@
                 }
             };
         }
+        final TypeSystem typeSystem = context.getTypeSystem();
+        final TypeFactory<String> stringFactory = typeSystem.getStringFactory();
         if (operand instanceof Length) {
             Length length = (Length)operand;
             PropertyValue value = length.getPropertyValue();
-            Name propertyName = value.getPropertyName();
+            String propertyName = value.getPropertyName();
             String selectorName = value.getSelectorName().getName();
             final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
+            // Find the expected property type of the value ...
+            Table table = context.getSchemata().getTable(value.getSelectorName());
+            Column schemaColumn = table.getColumn(propertyName);
+            final String expectedType = schemaColumn.getPropertyType();
+            final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.LONG; // length is always LONG
+                public String getExpectedType() {
+                    return typeSystem.getLongFactory().getTypeName(); // length is always LONG
                 }
 
                 public Object evaluate( Object[] tuple ) {
                     Object value = tuple[index];
-                    // Determine the length ...
-                    if (value instanceof Binary) {
-                        return ((Binary)value).getStream();
-                    }
-                    String result = stringFactory.create(value);
-                    return result != null ? result.length() : null;
+                    return typeFactory.length(typeFactory.create(value));
                 }
             };
         }
@@ -197,8 +196,8 @@
             LowerCase lowerCase = (LowerCase)operand;
             final DynamicOperation delegate = createDynamicOperation(context, columns, lowerCase.getOperand());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.STRING;
+                public String getExpectedType() {
+                    return stringFactory.getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {
@@ -211,8 +210,8 @@
             UpperCase upperCase = (UpperCase)operand;
             final DynamicOperation delegate = createDynamicOperation(context, columns, upperCase.getOperand());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.STRING;
+                public String getExpectedType() {
+                    return stringFactory.getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {
@@ -225,8 +224,8 @@
             NodeDepth nodeDepth = (NodeDepth)operand;
             final int locationIndex = columns.getLocationIndex(nodeDepth.getSelectorName().getName());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.LONG;
+                public String getExpectedType() {
+                    return typeSystem.getLongFactory().getTypeName(); // depth is always LONG
                 }
 
                 public Object evaluate( Object[] tuple ) {
@@ -242,16 +241,15 @@
             NodePath nodePath = (NodePath)operand;
             final int locationIndex = columns.getLocationIndex(nodePath.getSelectorName().getName());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.PATH;
+                public String getExpectedType() {
+                    return stringFactory.getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {
                     Location location = (Location)tuple[locationIndex];
                     if (location == null) return null;
-                    Path path = location.getPath();
-                    assert path != null;
-                    return path;
+                    assert location.getPath() != null;
+                    return stringFactory.create(location.getPath());
                 }
             };
         }
@@ -259,8 +257,8 @@
             NodeName nodeName = (NodeName)operand;
             final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.STRING;
+                public String getExpectedType() {
+                    return stringFactory.getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {
@@ -276,8 +274,8 @@
             NodeLocalName nodeName = (NodeLocalName)operand;
             final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.STRING;
+                public String getExpectedType() {
+                    return stringFactory.getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {
@@ -296,8 +294,8 @@
             if (index < 0) {
                 // No full-text search score for this selector, so return 0.0d;
                 return new DynamicOperation() {
-                    public PropertyType getExpectedType() {
-                        return PropertyType.DOUBLE;
+                    public String getExpectedType() {
+                        return typeSystem.getDoubleFactory().getTypeName();
                     }
 
                     public Object evaluate( Object[] tuple ) {
@@ -306,8 +304,8 @@
                 };
             }
             return new DynamicOperation() {
-                public PropertyType getExpectedType() {
-                    return PropertyType.DOUBLE;
+                public String getExpectedType() {
+                    return typeSystem.getDoubleFactory().getTypeName();
                 }
 
                 public Object evaluate( Object[] tuple ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -35,7 +35,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Column;
 
@@ -66,7 +65,7 @@
     private final Map<String, Integer> locationIndexBySelectorName;
     private final Map<String, Integer> locationIndexByColumnName;
     private final Map<Integer, Integer> locationIndexByColumnIndex;
-    private final Map<String, Map<Name, Integer>> columnIndexByPropertyNameBySelectorName;
+    private final Map<String, Map<String, Integer>> columnIndexByPropertyNameBySelectorName;
     private final Map<String, Integer> fullTextSearchScoreIndexBySelectorName;
 
     /**
@@ -88,7 +87,7 @@
         this.locationIndexBySelectorName = new HashMap<String, Integer>();
         this.locationIndexByColumnIndex = new HashMap<Integer, Integer>();
         this.locationIndexByColumnName = new HashMap<String, Integer>();
-        this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<Name, Integer>>();
+        this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<String, Integer>>();
         List<String> selectorNames = new ArrayList<String>(columnCount);
         List<String> names = new ArrayList<String>(columnCount);
         for (int i = 0, max = this.columns.size(); i != max; ++i) {
@@ -110,9 +109,9 @@
             locationIndexByColumnIndex.put(new Integer(i), selectorIndex);
             locationIndexByColumnName.put(columnName, selectorIndex);
             // Insert the entry by selector name and property name ...
-            Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+            Map<String, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
             if (byPropertyName == null) {
-                byPropertyName = new HashMap<Name, Integer>();
+                byPropertyName = new HashMap<String, Integer>();
                 columnIndexByPropertyNameBySelectorName.put(selectorName, byPropertyName);
             }
             byPropertyName.put(column.getPropertyName(), new Integer(i));
@@ -159,7 +158,7 @@
         this.locationIndexBySelectorName = new HashMap<String, Integer>();
         this.locationIndexByColumnIndex = new HashMap<Integer, Integer>();
         this.locationIndexByColumnName = new HashMap<String, Integer>();
-        this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<Name, Integer>>();
+        this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<String, Integer>>();
         this.selectorNames = new ArrayList<String>(columns.size());
         List<String> names = new ArrayList<String>(columns.size());
         for (int i = 0, max = this.columns.size(); i != max; ++i) {
@@ -180,9 +179,9 @@
             locationIndexByColumnIndex.put(new Integer(0), selectorIndex);
             locationIndexByColumnName.put(columnName, selectorIndex);
             // Insert the entry by selector name and property name ...
-            Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+            Map<String, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
             if (byPropertyName == null) {
-                byPropertyName = new HashMap<Name, Integer>();
+                byPropertyName = new HashMap<String, Integer>();
                 columnIndexByPropertyNameBySelectorName.put(selectorName, byPropertyName);
             }
             byPropertyName.put(column.getPropertyName(), columnIndex);
@@ -336,7 +335,7 @@
      * 
      * @see org.jboss.dna.graph.query.QueryResults.Columns#getPropertyNameForColumn(int)
      */
-    public Name getPropertyNameForColumn( int columnIndex ) {
+    public String getPropertyNameForColumn( int columnIndex ) {
         return columns.get(columnIndex).getPropertyName();
     }
 
@@ -345,7 +344,7 @@
      * 
      * @see org.jboss.dna.graph.query.QueryResults.Columns#getPropertyNameForColumn(java.lang.String)
      */
-    public Name getPropertyNameForColumn( String columnName ) {
+    public String getPropertyNameForColumn( String columnName ) {
         Column result = columnsByName.get(columnName);
         if (result == null) {
             throw new NoSuchElementException(GraphI18n.columnDoesNotExistInQuery.text(columnName));
@@ -369,12 +368,11 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnIndexForProperty(java.lang.String,
-     *      org.jboss.dna.graph.property.Name)
+     * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnIndexForProperty(java.lang.String, java.lang.String)
      */
     public int getColumnIndexForProperty( String selectorName,
-                                          Name propertyName ) {
-        Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+                                          String propertyName ) {
+        Map<String, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
         if (byPropertyName == null) {
             throw new NoSuchElementException(GraphI18n.selectorDoesNotExistInQuery.text(selectorName));
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,14 +29,12 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.common.util.StringUtil;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
+import org.jboss.dna.graph.query.model.TypeSystem;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 
 /**
  * The resulting output of a query.
@@ -93,10 +91,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.QueryResults#getContext()
+     * @see org.jboss.dna.graph.query.QueryResults#getTypeSystem()
      */
-    public ExecutionContext getContext() {
-        return context.getExecutionContext();
+    public TypeSystem getTypeSystem() {
+        return context.getTypeSystem();
     }
 
     /**
@@ -219,12 +217,11 @@
      */
     public void toString( StringBuilder sb,
                           int maxTuples ) {
-        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-        int[] columnWidths = determineColumnWidths(Integer.MAX_VALUE, true, stringFactory);
+        int[] columnWidths = determineColumnWidths(Integer.MAX_VALUE, true);
         printDelimiterLine(sb, columnWidths, true);
         printHeader(sb, columnWidths);
         printDelimiterLine(sb, columnWidths, true);
-        printLines(sb, columnWidths, stringFactory, maxTuples);
+        printLines(sb, columnWidths, maxTuples);
         printDelimiterLine(sb, columnWidths, false);
     }
 
@@ -233,12 +230,10 @@
      * 
      * @param maxWidth the maximum width; must be positive
      * @param useData true if the data should be used to compute the length, or false if just the column names should be used
-     * @param stringFactory the value factory for creating strings; may not be null
      * @return the array of widths for each column, excluding any decorating characters; never null
      */
     protected int[] determineColumnWidths( int maxWidth,
-                                           boolean useData,
-                                           ValueFactory<String> stringFactory ) {
+                                           boolean useData ) {
         assert maxWidth > 0;
         int tupleLength = columns.getTupleSize();
         int[] columnWidths = new int[tupleLength + 1]; // +1 for the row number column
@@ -246,7 +241,7 @@
             columnWidths[i] = 0;
         }
         // Determine the width of the first column that shows the row number ...
-        String rowNumber = stringFactory.create(getTuples().size());
+        String rowNumber = Integer.toString(getTuples().size());
         columnWidths[0] = rowNumber.length();
 
         // Compute the column names ...
@@ -259,7 +254,7 @@
         if (useData) {
             for (Object[] tuple : getTuples()) {
                 for (int i = 0, j = 1; i != tupleLength; ++i, ++j) {
-                    String valueStr = stringOf(tuple[i], stringFactory);
+                    String valueStr = stringOf(tuple[i]);
                     if (valueStr == null) continue;
                     columnWidths[j] = Math.max(Math.min(maxWidth, valueStr.length()), columnWidths[j]);
                 }
@@ -268,14 +263,10 @@
         return columnWidths;
     }
 
-    protected String stringOf( Object value,
-                               ValueFactory<String> stringFactory ) {
-        if (value instanceof Binary) {
-            // Just print out the SHA-1 hash in Base64, plus length
-            Binary binary = (Binary)value;
-            return "(Binary,length=" + binary.getSize() + ",SHA1=" + Base64.encode(binary.getHash()) + ")";
-        }
-        return stringFactory.create(value);
+    protected String stringOf( Object value ) {
+        if (value == null) return null;
+        TypeFactory<?> typeFactory = context.getTypeSystem().getTypeFactory(value);
+        return typeFactory.asReadableString(value);
     }
 
     protected void printHeader( StringBuilder sb,
@@ -296,7 +287,6 @@
 
     protected void printLines( StringBuilder sb,
                                int[] columnWidths,
-                               ValueFactory<String> stringFactory,
                                int maxRowsToPrint ) {
         int rowNumber = 1;
         int tupleLength = columns.getTupleSize();
@@ -304,13 +294,13 @@
         if (maxRowsToPrint > tuples.size()) {
             // Print all tuples ...
             for (Object[] tuple : getTuples()) {
-                printTuple(sb, columnWidths, stringFactory, rowNumber, tupleLength, tuple);
+                printTuple(sb, columnWidths, rowNumber, tupleLength, tuple);
                 ++rowNumber;
             }
         } else {
             // Print max number of rows ...
             for (Object[] tuple : getTuples()) {
-                printTuple(sb, columnWidths, stringFactory, rowNumber, tupleLength, tuple);
+                printTuple(sb, columnWidths, rowNumber, tupleLength, tuple);
                 if (rowNumber >= maxRowsToPrint) break;
                 ++rowNumber;
             }
@@ -321,14 +311,12 @@
     /**
      * @param sb
      * @param columnWidths
-     * @param stringFactory
      * @param rowNumber
      * @param tupleLength
      * @param tuple
      */
     private final void printTuple( StringBuilder sb,
                                    int[] columnWidths,
-                                   ValueFactory<String> stringFactory,
                                    int rowNumber,
                                    int tupleLength,
                                    Object[] tuple ) {
@@ -336,7 +324,7 @@
         sb.append("| ").append(StringUtil.justifyLeft(Integer.toString(rowNumber), columnWidths[0], ' ')).append(' ');
         // Print the remaining columns ...
         for (int i = 0, j = 1; i != tupleLength; ++i, ++j) {
-            String valueStr = stringOf(tuple[i], stringFactory);
+            String valueStr = stringOf(tuple[i]);
             valueStr = StringUtil.justifyLeft(valueStr, columnWidths[j], ' ');
             sb.append('|').append(' ').append(valueStr).append(' ');
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,10 +30,6 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.And;
@@ -50,6 +46,8 @@
 import org.jboss.dna.graph.query.model.PropertyExistence;
 import org.jboss.dna.graph.query.model.SameNode;
 import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.TypeSystem;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 
 /**
  */
@@ -147,6 +145,9 @@
      * @see SelectComponent#SelectComponent(ProcessingComponent, Constraint, Map, Analyzer)
      */
     public static interface Analyzer {
+
+        int length( Object value );
+
         /**
          * Determine whether the node specified by the location is the same node as that supplied by the path. This determines if
          * the nodes at the supplied location and path are the same node.
@@ -156,9 +157,19 @@
          * @return true if the node given by the {@link Location} is also accessible at the supplied path, or false otherwise
          */
         boolean isSameNode( Location location,
-                            Path accessibleAtPath );
+                            String accessibleAtPath );
 
         /**
+         * Determine whether the node specified by the location is a descendant of that supplied by the path.
+         * 
+         * @param location the location of the node; never null
+         * @param ancestorPath the path of the ancestor node
+         * @return true if the node given by the {@link Location} is also accessible at the supplied path, or false otherwise
+         */
+        boolean isDescendantOf( Location location,
+                                String ancestorPath );
+
+        /**
          * Determine whether the node at the supplied location has the named property.
          * 
          * @param location the location of the node; never null
@@ -166,7 +177,7 @@
          * @return true if the node at the supplied {@link Location} does contain the property, or false if it does not
          */
         boolean hasProperty( Location location,
-                             Name propertyName );
+                             String propertyName );
 
         /**
          * Determine whether the node at the supplied location satisfies the supplied full-text query.
@@ -187,7 +198,7 @@
          * @return the full-text search score of the node, or 0.0d if the node does not satisfy the full-text query
          */
         double hasFullText( Location location,
-                            Name propertyName,
+                            String propertyName,
                             String fullTextQuery );
     }
 
@@ -258,7 +269,7 @@
         if (constraint instanceof ChildNode) {
             ChildNode childConstraint = (ChildNode)constraint;
             final int locationIndex = columns.getLocationIndex(childConstraint.getSelectorName().getName());
-            final Path parentPath = childConstraint.getParentPath();
+            final String parentPath = childConstraint.getParentPath();
             return new ConstraintChecker() {
                 public boolean satisfiesConstraints( Object[] tuple ) {
                     Location location = (Location)tuple[locationIndex];
@@ -270,19 +281,19 @@
         if (constraint instanceof DescendantNode) {
             DescendantNode descendantNode = (DescendantNode)constraint;
             final int locationIndex = columns.getLocationIndex(descendantNode.getSelectorName().getName());
-            final Path ancestorPath = descendantNode.getAncestorPath();
+            final String ancestorPath = descendantNode.getAncestorPath();
             return new ConstraintChecker() {
                 public boolean satisfiesConstraints( Object[] tuple ) {
                     Location location = (Location)tuple[locationIndex];
                     assert location.hasPath();
-                    return location.getPath().isDecendantOf(ancestorPath);
+                    return analyzer.isDescendantOf(location, ancestorPath);
                 }
             };
         }
         if (constraint instanceof SameNode) {
             SameNode sameNode = (SameNode)constraint;
             final int locationIndex = columns.getLocationIndex(sameNode.getSelectorName().getName());
-            final Path path = sameNode.getPath();
+            final String path = sameNode.getPath();
             if (analyzer != null) {
                 return new ConstraintChecker() {
                     public boolean satisfiesConstraints( Object[] tuple ) {
@@ -295,14 +306,14 @@
                 public boolean satisfiesConstraints( Object[] tuple ) {
                     Location location = (Location)tuple[locationIndex];
                     assert location.hasPath();
-                    return location.getPath().isSameAs(path);
+                    return location.toString().equals(path);
                 }
             };
         }
         if (constraint instanceof PropertyExistence) {
             PropertyExistence propertyExistance = (PropertyExistence)constraint;
             String selectorName = propertyExistance.getSelectorName().getName();
-            final Name propertyName = propertyExistance.getPropertyName();
+            final String propertyName = propertyExistance.getPropertyName();
             if (analyzer != null) {
                 final int locationIndex = columns.getLocationIndex(selectorName);
                 return new ConstraintChecker() {
@@ -332,7 +343,7 @@
                         }
                     };
                 }
-                final Name propertyName = search.getPropertyName(); // may be null
+                final String propertyName = search.getPropertyName(); // may be null
                 final int scoreIndex = columns.getFullTextSearchScoreIndexFor(selectorName);
                 assert scoreIndex >= 0 : "Columns do not have room for the search scores";
                 if (propertyName != null) {
@@ -373,12 +384,11 @@
             };
         }
         if (constraint instanceof Comparison) {
-            final ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
             Comparison comparison = (Comparison)constraint;
 
             // Create the correct dynamic operation ...
             final DynamicOperation dynamicOperation = createDynamicOperation(context, columns, comparison.getOperand1());
-            final PropertyType expectedType = dynamicOperation.getExpectedType();
+            final String expectedType = dynamicOperation.getExpectedType();
 
             // Determine the literal value ...
             StaticOperand staticOperand = comparison.getOperand2();
@@ -392,9 +402,13 @@
                 literalValue = literal.getValue();
             }
             // Create the correct comparator ...
-            final Comparator<Object> comparator = (Comparator<Object>)expectedType.getComparator();
+            final TypeSystem typeSystem = context.getTypeSystem();
+            final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
+            assert typeFactory != null;
+            final Comparator<Object> comparator = (Comparator<Object>)typeFactory.getComparator();
+            assert comparator != null;
             // Create the correct operation ...
-            ValueFactory<?> literalFactory = context.getExecutionContext().getValueFactories().getValueFactory(expectedType);
+            final TypeFactory<?> literalFactory = typeSystem.getTypeFactory(expectedType);
             final Object rhs = literalFactory.create(literalValue);
             switch (comparison.getOperator()) {
                 case EQUAL_TO:
@@ -435,12 +449,12 @@
                     };
                 case LIKE:
                     // Convert the LIKE expression to a regular expression
-                    final Pattern pattern = createRegexFromLikeExpression(stringFactory.create(rhs));
+                    final Pattern pattern = createRegexFromLikeExpression(typeSystem.asString(rhs));
                     return new ConstraintChecker() {
                         public boolean satisfiesConstraints( Object[] tuples ) {
                             Object tupleValue = dynamicOperation.evaluate(tuples);
                             if (tupleValue == null) return false;
-                            String value = stringFactory.create(tupleValue);
+                            String value = typeSystem.asString(tupleValue);
                             return pattern.matcher(value).matches();
                         }
                     };

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -31,6 +31,8 @@
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Order;
 import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.TypeSystem;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 
 /**
@@ -105,7 +107,11 @@
         assert context != null;
         assert ordering != null;
         final DynamicOperation operation = createDynamicOperation(context, columns, ordering.getOperand());
-        final Comparator<Object> typeComparator = (Comparator<Object>)operation.getExpectedType().getComparator();
+        final TypeSystem typeSystem = context.getTypeSystem();
+        final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(operation.getExpectedType());
+        assert typeFactory != null;
+        final Comparator<Object> typeComparator = (Comparator<Object>)typeFactory.getComparator();
+        assert typeComparator != null;
         if (ordering.getOrder() == Order.DESCENDING) {
             return new Comparator<Object[]>() {
                 public int compare( Object[] tuple1,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -24,7 +24,6 @@
 package org.jboss.dna.graph.query.validate;
 
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.validate.Schemata.Column;
 
 @Immutable
@@ -34,19 +33,21 @@
 
     private final boolean fullTextSearchable;
     private final String name;
-    private final PropertyType type;
+    private final String type;
 
     protected ImmutableColumn( String name,
-                               PropertyType type ) {
+                               String type ) {
         this(name, type, DEFAULT_FULL_TEXT_SEARCHABLE);
     }
 
     protected ImmutableColumn( String name,
-                               PropertyType type,
+                               String type,
                                boolean fullTextSearchable ) {
         this.name = name;
-        this.type = type != null ? type : PropertyType.STRING;
+        this.type = type;
         this.fullTextSearchable = fullTextSearchable;
+        assert this.name != null;
+        assert this.type != null;
     }
 
     /**
@@ -63,7 +64,7 @@
      * 
      * @see org.jboss.dna.graph.query.validate.Schemata.Column#getPropertyType()
      */
-    public PropertyType getPropertyType() {
+    public String getPropertyType() {
         return type;
     }
 
@@ -83,6 +84,6 @@
      */
     @Override
     public String toString() {
-        return this.name + "(" + type.getName() + ")";
+        return this.name + "(" + type + ")";
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -34,13 +34,11 @@
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.parse.InvalidQueryException;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
@@ -58,13 +56,13 @@
     /**
      * Obtain a new instance for building Schemata objects.
      * 
-     * @param context the execution context that can be used when building the schema
+     * @param typeSystem the type system that this schemata should use
      * @return the new builder; never null
      * @throws IllegalArgumentException if the context is null
      */
-    public static Builder createBuilder( ExecutionContext context ) {
-        CheckArg.isNotNull(context, "context");
-        return new Builder(context);
+    public static Builder createBuilder( TypeSystem typeSystem ) {
+        CheckArg.isNotNull(typeSystem, "typeSystem");
+        return new Builder(typeSystem);
     }
 
     /**
@@ -73,17 +71,17 @@
     @NotThreadSafe
     public static class Builder {
 
-        private final ExecutionContext context;
+        private final TypeSystem typeSystem;
         private final Map<SelectorName, ImmutableTable> tables = new HashMap<SelectorName, ImmutableTable>();
         private final Map<SelectorName, QueryCommand> viewDefinitions = new HashMap<SelectorName, QueryCommand>();
 
-        protected Builder( ExecutionContext context ) {
-            this.context = context;
+        protected Builder( TypeSystem typeSystem ) {
+            this.typeSystem = typeSystem;
         }
 
         /**
-         * Add a table with the supplied name and column names. Each column will be given a type of {@link PropertyType#STRING}.
-         * The table will also overwrite any existing table definition with the same name.
+         * Add a table with the supplied name and column names. Each column will be given a default type. The table will also
+         * overwrite any existing table definition with the same name.
          * 
          * @param name the name of the new table
          * @param columnNames the names of the columns.
@@ -99,7 +97,7 @@
             int i = 0;
             for (String columnName : columnNames) {
                 CheckArg.isNotEmpty(columnName, "columnName[" + (i++) + "]");
-                columns.add(new ImmutableColumn(columnName, PropertyType.STRING));
+                columns.add(new ImmutableColumn(columnName, typeSystem.getDefaultType()));
             }
             ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
             tables.put(table.getName(), table);
@@ -119,7 +117,7 @@
          */
         public Builder addTable( String name,
                                  String[] columnNames,
-                                 PropertyType[] types ) {
+                                 String[] types ) {
             CheckArg.isNotEmpty(name, "name");
             CheckArg.isNotEmpty(columnNames, "columnNames");
             CheckArg.isNotEmpty(types, "types");
@@ -137,144 +135,6 @@
         }
 
         /**
-         * Add a table with the supplied name and single column name and type. The table will also overwrite any existing table
-         * definition with the same name.
-         * 
-         * @param name the name of the new table
-         * @param column1Name the name of the single column
-         * @param column1Type the type for the single column
-         * @return this builder, for convenience in method chaining; never null
-         * @throws IllegalArgumentException if the table name is null or empty, the column name is null or empty, or if the type
-         *         is null
-         */
-        public Builder addTable( String name,
-                                 String column1Name,
-                                 PropertyType column1Type ) {
-            CheckArg.isNotEmpty(name, "name");
-            CheckArg.isNotNull(column1Name, "column1Name");
-            CheckArg.isNotNull(column1Type, "column1Type");
-            List<Column> columns = new ArrayList<Column>();
-            columns.add(new ImmutableColumn(column1Name, column1Type));
-            ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
-            tables.put(table.getName(), table);
-            return this;
-        }
-
-        /**
-         * Add a table with the supplied name and two column names and types. The table will also overwrite any existing table
-         * definition with the same name.
-         * 
-         * @param name the name of the new table
-         * @param column1Name the name of the first column
-         * @param column1Type the type for the first column
-         * @param column2Name the name of the second column
-         * @param column2Type the type for the second column
-         * @return this builder, for convenience in method chaining; never null
-         * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
-         *         types is null
-         */
-        public Builder addTable( String name,
-                                 String column1Name,
-                                 PropertyType column1Type,
-                                 String column2Name,
-                                 PropertyType column2Type ) {
-            CheckArg.isNotEmpty(name, "name");
-            CheckArg.isNotNull(column1Name, "column1Name");
-            CheckArg.isNotNull(column1Type, "column1Type");
-            CheckArg.isNotNull(column2Name, "column2Name");
-            CheckArg.isNotNull(column2Type, "column2Type");
-            List<Column> columns = new ArrayList<Column>();
-            columns.add(new ImmutableColumn(column1Name, column1Type));
-            columns.add(new ImmutableColumn(column2Name, column2Type));
-            ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
-            tables.put(table.getName(), table);
-            return this;
-        }
-
-        /**
-         * Add a table with the supplied name and three column names and types. The table will also overwrite any existing table
-         * definition with the same name.
-         * 
-         * @param name the name of the new table
-         * @param column1Name the name of the first column
-         * @param column1Type the type for the first column
-         * @param column2Name the name of the second column
-         * @param column2Type the type for the second column
-         * @param column3Name the name of the third column
-         * @param column3Type the type for the third column
-         * @return this builder, for convenience in method chaining; never null
-         * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
-         *         types is null
-         */
-        public Builder addTable( String name,
-                                 String column1Name,
-                                 PropertyType column1Type,
-                                 String column2Name,
-                                 PropertyType column2Type,
-                                 String column3Name,
-                                 PropertyType column3Type ) {
-            CheckArg.isNotEmpty(name, "name");
-            CheckArg.isNotNull(column1Name, "column1Name");
-            CheckArg.isNotNull(column1Type, "column1Type");
-            CheckArg.isNotNull(column2Name, "column2Name");
-            CheckArg.isNotNull(column2Type, "column2Type");
-            CheckArg.isNotNull(column3Name, "column3Name");
-            CheckArg.isNotNull(column3Type, "column3Type");
-            List<Column> columns = new ArrayList<Column>();
-            columns.add(new ImmutableColumn(column1Name, column1Type));
-            columns.add(new ImmutableColumn(column2Name, column2Type));
-            columns.add(new ImmutableColumn(column3Name, column3Type));
-            ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
-            tables.put(table.getName(), table);
-            return this;
-        }
-
-        /**
-         * Add a table with the supplied name and four column names and types. The table will also overwrite any existing table
-         * definition with the same name.
-         * 
-         * @param name the name of the new table
-         * @param column1Name the name of the first column
-         * @param column1Type the type for the first column
-         * @param column2Name the name of the second column
-         * @param column2Type the type for the second column
-         * @param column3Name the name of the third column
-         * @param column3Type the type for the third column
-         * @param column4Name the name of the fourth column
-         * @param column4Type the type for the fourth column
-         * @return this builder, for convenience in method chaining; never null
-         * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
-         *         types is null
-         */
-        public Builder addTable( String name,
-                                 String column1Name,
-                                 PropertyType column1Type,
-                                 String column2Name,
-                                 PropertyType column2Type,
-                                 String column3Name,
-                                 PropertyType column3Type,
-                                 String column4Name,
-                                 PropertyType column4Type ) {
-            CheckArg.isNotEmpty(name, "name");
-            CheckArg.isNotNull(column1Name, "column1Name");
-            CheckArg.isNotNull(column1Type, "column1Type");
-            CheckArg.isNotNull(column2Name, "column2Name");
-            CheckArg.isNotNull(column2Type, "column2Type");
-            CheckArg.isNotNull(column3Name, "column3Name");
-            CheckArg.isNotNull(column3Type, "column3Type");
-            CheckArg.isNotNull(column4Name, "column4Name");
-            CheckArg.isNotNull(column4Type, "column4Type");
-            List<Column> columns = new ArrayList<Column>();
-            columns.add(new ImmutableColumn(column1Name, column1Type));
-            columns.add(new ImmutableColumn(column2Name, column2Type));
-            columns.add(new ImmutableColumn(column3Name, column3Type));
-            columns.add(new ImmutableColumn(column4Name, column4Type));
-            ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
-            tables.put(table.getName(), table);
-            return this;
-        }
-
-        /**
          * Add a view with the supplied name and SQL string definition. The column names and types will be inferred from the
          * source table(s) and views(s) used in the definition.
          * 
@@ -289,7 +149,7 @@
             CheckArg.isNotEmpty(name, "name");
             CheckArg.isNotEmpty(definition, "definition");
             SqlQueryParser parser = new SqlQueryParser();
-            QueryCommand command = parser.parseQuery(definition, context);
+            QueryCommand command = parser.parseQuery(definition, typeSystem);
             this.viewDefinitions.put(new SelectorName(name), command);
             return this;
         }
@@ -324,7 +184,7 @@
          */
         public Builder addColumn( String tableName,
                                   String columnName,
-                                  PropertyType type ) {
+                                  String type ) {
             CheckArg.isNotEmpty(tableName, "tableName");
             CheckArg.isNotEmpty(columnName, "columnName");
             CheckArg.isNotNull(type, "type");
@@ -345,7 +205,7 @@
          */
         public Builder addColumn( String tableName,
                                   String columnName,
-                                  PropertyType type,
+                                  String type,
                                   boolean fullTextSearchable ) {
             CheckArg.isNotEmpty(tableName, "tableName");
             CheckArg.isNotEmpty(columnName, "columnName");
@@ -381,11 +241,11 @@
             ImmutableTable table = null;
             if (existing == null) {
                 List<Column> columns = new ArrayList<Column>();
-                columns.add(new ImmutableColumn(columnName, PropertyType.STRING, true));
+                columns.add(new ImmutableColumn(columnName, typeSystem.getDefaultType(), true));
                 table = new ImmutableTable(selector, columns);
             } else {
                 Column column = existing.getColumn(columnName);
-                PropertyType type = PropertyType.STRING;
+                String type = typeSystem.getDefaultType();
                 if (column != null) {
                     type = column.getPropertyType();
                 }
@@ -438,7 +298,6 @@
 
             // Make a copy of the view definitions, and create the views ...
             Map<SelectorName, QueryCommand> definitions = new HashMap<SelectorName, QueryCommand>(viewDefinitions);
-            ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
             boolean added = false;
             do {
                 added = false;
@@ -446,7 +305,7 @@
                 for (SelectorName name : viewNames) {
                     QueryCommand command = definitions.get(name);
                     // Create the canonical plan for the definition ...
-                    QueryContext queryContext = new QueryContext(context, schemata);
+                    QueryContext queryContext = new QueryContext(schemata, typeSystem);
                     CanonicalPlanner planner = new CanonicalPlanner();
                     PlanNode plan = planner.createPlan(queryContext, command);
                     if (queryContext.getProblems().hasErrors()) continue;
@@ -465,7 +324,7 @@
                         Table source = schemata.getTable(column.getSelectorName());
                         if (source == null) break;
                         String viewColumnName = column.getColumnName();
-                        String sourceColumnName = stringFactory.create(column.getPropertyName()); // getColumnName() returns alias
+                        String sourceColumnName = column.getPropertyName(); // getColumnName() returns alias
                         Column sourceColumn = source.getColumn(sourceColumnName);
                         if (sourceColumn == null) {
                             throw new InvalidQueryException(Visitors.readable(command),

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -33,7 +33,6 @@
 import java.util.Map;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.validate.Schemata.Column;
 import org.jboss.dna.graph.query.validate.Schemata.Key;
@@ -181,14 +180,14 @@
     }
 
     public ImmutableTable withColumn( String name,
-                                      PropertyType type ) {
+                                      String type ) {
         List<Column> newColumns = new LinkedList<Column>(columns);
         newColumns.add(new ImmutableColumn(name, type));
         return new ImmutableTable(getName(), newColumns);
     }
 
     public ImmutableTable withColumn( String name,
-                                      PropertyType type,
+                                      String type,
                                       boolean fullTextSearchable ) {
         List<Column> newColumns = new LinkedList<Column>(columns);
         newColumns.add(new ImmutableColumn(name, type, fullTextSearchable));

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,7 +28,6 @@
 import java.util.Map;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
 
@@ -159,7 +158,7 @@
          * 
          * @return the property type; never null
          */
-        PropertyType getPropertyType();
+        String getPropertyType();
 
         /**
          * Get whether the column can be used in a full-text search.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Validator.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -26,7 +26,6 @@
 import java.util.Map;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.ChildNode;
@@ -302,10 +301,6 @@
         verify(obj.getSelector2Name());
     }
 
-    protected String string( Name name ) {
-        return context.getExecutionContext().getValueFactories().getStringFactory().create(name);
-    }
-
     protected Table verify( SelectorName selectorName ) {
         Table table = selectorsByName.get(selectorName);
         if (table == null) {
@@ -323,15 +318,15 @@
     }
 
     protected Schemata.Column verify( SelectorName selectorName,
-                                      Name propertyName ) {
+                                      String propertyName ) {
         Table table = selectorsByName.get(selectorName);
         if (table == null) {
             problems.addError(GraphI18n.tableDoesNotExist, selectorName.getName());
             return null;
         }
-        Schemata.Column column = table.getColumn(string(propertyName));
+        Schemata.Column column = table.getColumn(propertyName);
         if (column == null) {
-            problems.addError(GraphI18n.columnDoesNotExistOnTable, string(propertyName), selectorName.getName());
+            problems.addError(GraphI18n.columnDoesNotExistOnTable, propertyName, selectorName.getName());
         }
         return column;
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/search/SearchEngine.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -366,7 +366,7 @@
         CheckArg.isNotNull(workspaceName, "workspaceName");
         CheckArg.isNotNull(query, "query");
         CheckArg.isNotNull(schemata, "schemata");
-        QueryContext queryContext = new QueryContext(context, schemata);
+        QueryContext queryContext = new QueryContext(schemata, context.getValueFactories().getTypeSystem());
         Query queryActivity = queryContent(queryContext, query);
         getWorkspace(workspaceName).execute(false, queryActivity);
         return queryActivity.getResults();

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -37,19 +37,17 @@
  */
 public class QueryBuilderTest {
 
-    private ExecutionContext context;
     private QueryBuilder builder;
     private QueryCommand query;
 
     @Before
     public void beforeEach() {
-        context = new ExecutionContext();
-        builder = new QueryBuilder(context);
+        builder = new QueryBuilder(new ExecutionContext().getValueFactories().getTypeSystem());
     }
 
     protected void assertThatSql( QueryCommand query,
                                   Matcher<String> expected ) {
-        assertThat(Visitors.readable(query, context), expected);
+        assertThat(Visitors.readable(query), expected);
     }
 
     @Test
@@ -332,19 +330,19 @@
     @Test
     public void shouldBuildQueryWithChildConstraint() {
         query = builder.selectStar().from("table AS nodes").where().isChild("nodes", "/parent/path").end().query();
-        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISCHILDNODE(nodes,'/parent/path')"));
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISCHILDNODE(nodes,/parent/path)"));
     }
 
     @Test
     public void shouldBuildQueryWithDescendantConstraint() {
         query = builder.selectStar().from("table AS nodes").where().isBelowPath("nodes", "/parent/path").end().query();
-        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISDESCENDANTNODE(nodes,'/parent/path')"));
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISDESCENDANTNODE(nodes,/parent/path)"));
     }
 
     @Test
     public void shouldBuildQueryWithSameNodeConstraint() {
         query = builder.selectStar().from("table AS nodes").where().isSameNode("nodes", "/other/path").end().query();
-        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISSAMENODE(nodes,'/other/path')"));
+        assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISSAMENODE(nodes,/other/path)"));
     }
 
     @Test

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -24,8 +24,6 @@
 package org.jboss.dna.graph.query.model;
 
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 
 /**
@@ -40,14 +38,6 @@
         context = new ExecutionContext();
     }
 
-    protected Name name( String name ) {
-        return context.getValueFactories().getNameFactory().create(name);
-    }
-
-    protected Path path( String name ) {
-        return context.getValueFactories().getPathFactory().create(name);
-    }
-
     protected SelectorName selector( String name ) {
         return new SelectorName(name);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -120,10 +120,10 @@
     public void shouldConstructReadableString() {
         source = new NamedSelector(selector("nt:unstructured"));
         columns = Collections.singletonList(new Column(selector("selector1")));
-        constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+        constraint = new PropertyExistence(selector("selector1"), "jcr:uuid");
         orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context),
+        assertThat(Visitors.readable(query),
                    is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC"));
     }
 
@@ -131,11 +131,11 @@
     public void shouldConstructReadableStringWithLimits() {
         source = new NamedSelector(selector("nt:unstructured"));
         columns = Collections.singletonList(new Column(selector("selector1")));
-        constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+        constraint = new PropertyExistence(selector("selector1"), "jcr:uuid");
         orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
         limits = new Limit(10, 100);
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context),
+        assertThat(Visitors.readable(query),
                    is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC LIMIT 10 OFFSET 100"));
     }
 
@@ -143,10 +143,10 @@
     public void shouldConstructReadableStringWithNoColumns() {
         source = new NamedSelector(selector("nt:unstructured"));
         columns = Collections.emptyList();
-        constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+        constraint = new PropertyExistence(selector("selector1"), "jcr:uuid");
         orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context),
+        assertThat(Visitors.readable(query),
                    is("SELECT * FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC"));
     }
 
@@ -154,11 +154,10 @@
     public void shouldConstructReadableStringWithNoOrderings() {
         source = new NamedSelector(selector("nt:unstructured"));
         columns = Collections.singletonList(new Column(selector("selector1")));
-        constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+        constraint = new PropertyExistence(selector("selector1"), "jcr:uuid");
         orderings = Collections.emptyList();
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context),
-                   is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL"));
+        assertThat(Visitors.readable(query), is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL"));
     }
 
     @Test
@@ -168,7 +167,7 @@
         constraint = null;
         orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context), is("SELECT selector1.* FROM nt:unstructured ORDER BY NAME(selector1) ASC"));
+        assertThat(Visitors.readable(query), is("SELECT selector1.* FROM nt:unstructured ORDER BY NAME(selector1) ASC"));
     }
 
     @Test
@@ -179,11 +178,11 @@
         orderings = Collections.emptyList();
         distinct = true;
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context), is("SELECT DISTINCT * FROM nt:unstructured"));
+        assertThat(Visitors.readable(query), is("SELECT DISTINCT * FROM nt:unstructured"));
 
         source = new AllNodes();
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context), is("SELECT DISTINCT * FROM __ALLNODES__"));
+        assertThat(Visitors.readable(query), is("SELECT DISTINCT * FROM __ALLNODES__"));
     }
 
     @Test
@@ -193,10 +192,10 @@
         constraint = null;
         orderings = Collections.emptyList();
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM nt:unstructured"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM nt:unstructured"));
 
         source = new AllNodes();
         query = new Query(source, constraint, orderings, columns, limits, distinct);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM __ALLNODES__"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM __ALLNODES__"));
     }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -74,7 +74,7 @@
         right = new Query(new NamedSelector(selector("B")));
         operation = SetQuery.Operation.UNION;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A UNION SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A UNION SELECT * FROM B"));
     }
 
     @Test
@@ -84,7 +84,7 @@
         operation = SetQuery.Operation.UNION;
         all = true;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A UNION ALL SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A UNION ALL SELECT * FROM B"));
     }
 
     @Test
@@ -93,7 +93,7 @@
         right = new Query(new NamedSelector(selector("B")));
         operation = SetQuery.Operation.INTERSECT;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A INTERSECT SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A INTERSECT SELECT * FROM B"));
     }
 
     @Test
@@ -103,7 +103,7 @@
         operation = SetQuery.Operation.INTERSECT;
         all = true;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A INTERSECT ALL SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A INTERSECT ALL SELECT * FROM B"));
     }
 
     @Test
@@ -112,7 +112,7 @@
         right = new Query(new NamedSelector(selector("B")));
         operation = SetQuery.Operation.EXCEPT;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A EXCEPT SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A EXCEPT SELECT * FROM B"));
     }
 
     @Test
@@ -122,6 +122,6 @@
         operation = SetQuery.Operation.EXCEPT;
         all = true;
         query = new SetQuery(left, operation, right, all);
-        assertThat(Visitors.readable(query, context), is("SELECT * FROM A EXCEPT ALL SELECT * FROM B"));
+        assertThat(Visitors.readable(query), is("SELECT * FROM A EXCEPT ALL SELECT * FROM B"));
     }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -47,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), new ExecutionContext().getValueFactories().getTypeSystem());
         rule = AddAccessNodes.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -54,7 +54,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), new ExecutionContext().getValueFactories().getTypeSystem());
         bestRule = ChooseJoinAlgorithm.USE_BEST_JOIN_ALGORITHM;
         nestedRule = ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM;
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -49,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), new ExecutionContext().getValueFactories().getTypeSystem());
         rule = PushSelectCriteria.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,7 +29,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Column;
@@ -38,6 +37,7 @@
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -58,15 +58,15 @@
 
     @Before
     public void beforeEach() {
-        ExecutionContext execContext = new ExecutionContext();
+        TypeSystem typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
         rule = ReplaceViews.INSTANCE;
-        builder = ImmutableSchemata.createBuilder(execContext);
+        builder = ImmutableSchemata.createBuilder(typeSystem);
         builder.addTable("t1", "c11", "c12", "c13");
         builder.addTable("t2", "c21", "c22", "c23");
         builder.addView("v1", "SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS LONG)");
         builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
         schemata = builder.build();
-        context = new QueryContext(execContext, schemata);
+        context = new QueryContext(schemata, typeSystem);
     }
 
     /**
@@ -124,7 +124,7 @@
         PlanNode viewSelect = new PlanNode(Type.SELECT, viewProject, selector("t1"));
         PlanNode viewSource = new PlanNode(Type.SOURCE, viewSelect, selector("t1"));
         viewProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
-        viewSelect.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
+        viewSelect.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c13"),
                                                                         Operator.LESS_THAN, new Literal(3L)));
         viewSource.setProperty(Property.SOURCE_NAME, selector("t1"));
         viewSource.setProperty(Property.SOURCE_COLUMNS, schemata.getTable(selector("t1")).getColumns());
@@ -145,16 +145,12 @@
 
     protected Column column( String table,
                              String columnName ) {
-        return new Column(new SelectorName(table), name(columnName), columnName);
+        return new Column(new SelectorName(table), columnName, columnName);
     }
 
     protected Column column( String table,
                              String columnName,
                              String alias ) {
-        return new Column(new SelectorName(table), name(columnName), alias);
+        return new Column(new SelectorName(table), columnName, alias);
     }
-
-    protected Name name( String name ) {
-        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
-    }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteriaTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,7 +30,6 @@
 import static org.mockito.Mockito.mock;
 import java.util.LinkedList;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Between;
@@ -60,7 +59,7 @@
         rule = RewriteAsRangeCriteria.INSTANCE;
         rules = new LinkedList<OptimizerRule>();
         rules.add(rule);
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), new ExecutionContext().getValueFactories().getTypeSystem());
     }
 
     protected void print( PlanNode node ) {
@@ -99,11 +98,11 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
-                                                                     Operator.LESS_THAN, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"), Operator.LESS_THAN,
+                                                                     new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN, new Literal(1L)));
 
         // Execute the rule ...
@@ -160,11 +159,11 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.LESS_THAN_OR_EQUAL_TO, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(1L)));
 
         // Execute the rule ...
@@ -222,12 +221,12 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
-                                                                     Operator.LESS_THAN, new Literal(1L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"), Operator.LESS_THAN,
+                                                                     new Literal(1L)));
 
         // Execute the rule ...
         print(access);
@@ -273,11 +272,11 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.LESS_THAN_OR_EQUAL_TO, new Literal(1L)));
 
         // Execute the rule ...
@@ -323,11 +322,11 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.LESS_THAN_OR_EQUAL_TO, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN_OR_EQUAL_TO, new Literal(3L)));
 
         // Execute the rule ...
@@ -384,11 +383,11 @@
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c2")),
-                                                                     Operator.EQUAL_TO, new Literal(100L)));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
-                                                                     Operator.LESS_THAN, new Literal(3L)));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c1")),
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c2"), Operator.EQUAL_TO,
+                                                                     new Literal(100L)));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"), Operator.LESS_THAN,
+                                                                     new Literal(3L)));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c1"),
                                                                      Operator.GREATER_THAN, new Literal(3L)));
 
         // Execute the rule ...
@@ -410,8 +409,4 @@
         }
         return node;
     }
-
-    protected Name name( String name ) {
-        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
-    }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -49,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), new ExecutionContext().getValueFactories().getTypeSystem());
         rule = RightOuterToLeftOuterJoins.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -32,7 +32,6 @@
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.Column;
@@ -46,6 +45,7 @@
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.JoinAlgorithm;
@@ -72,8 +72,8 @@
 
     @Before
     public void beforeEach() {
-        ExecutionContext execContext = new ExecutionContext();
-        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(execContext);
+        TypeSystem typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
+        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(typeSystem);
         builder.addTable("t1", "c11", "c12", "c13");
         builder.addTable("t2", "c21", "c22", "c23");
         builder.addTable("all", "a1", "a2", "a3", "a4", "primaryType", "mixins");
@@ -88,7 +88,7 @@
         builder.addView("type2",
                         "SELECT all.a3, all.a4 FROM all WHERE all.primaryType IN ('t2','t0') AND all.mixins IN ('t4','t5')");
         Schemata schemata = builder.build();
-        context = new QueryContext(execContext, schemata);
+        context = new QueryContext(schemata, typeSystem);
 
         node = new PlanNode(Type.ACCESS);
 
@@ -191,8 +191,8 @@
         PlanNode project = new PlanNode(Type.PROJECT, expected, selector("t1"));
         project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
         PlanNode select = new PlanNode(Type.SELECT, project, selector("t1"));
-        select.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
-                                                                    Operator.LESS_THAN, new Literal(3L)));
+        select.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c13"), Operator.LESS_THAN,
+                                                                    new Literal(3L)));
         PlanNode source = new PlanNode(Type.SOURCE, select, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
         source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
@@ -210,7 +210,7 @@
         PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"), selector("t1"));
         join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
         join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
-        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), name("c11"), selector("t2"), name("c21")));
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), "c11", selector("t2"), "c21"));
 
         PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
         PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("t1"));
@@ -239,13 +239,13 @@
         PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
         project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11", "c1")));
         PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
-        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c11")),
-                                                                     Operator.EQUAL_TO, new Literal('x')));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c11"), Operator.EQUAL_TO,
+                                                                     new Literal('x')));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
-        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c12")),
-                                                                     Operator.EQUAL_TO, new Literal('y')));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c12"), Operator.EQUAL_TO,
+                                                                     new Literal('y')));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
-        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c13"),
                                                                      Operator.LESS_THAN, new Literal(3L)));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
         source.setProperty(Property.SOURCE_NAME, selector("t1"));
@@ -265,16 +265,16 @@
         PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"), selector("t1"));
         join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
         join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
-        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), name("c11"), selector("t2"), name("c21")));
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), "c11", selector("t2"), "c21"));
 
         PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
         PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("t1"));
         leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11")));
         PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject, selector("t1"));
-        leftSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c11")),
+        leftSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c11"),
                                                                          Operator.EQUAL_TO, new Literal('x')));
         PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1, selector("t1"));
-        leftSelect2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c12")),
+        leftSelect2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), "c12"),
                                                                          Operator.EQUAL_TO, new Literal('y')));
         PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2, selector("t1"));
         leftSource.setProperty(Property.SOURCE_NAME, selector("t1"));
@@ -284,7 +284,7 @@
         PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("t2"));
         rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t2", "c21")));
         PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject, selector("t2"));
-        rightSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t2"), name("c21")),
+        rightSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t2"), "c21"),
                                                                           Operator.EQUAL_TO, new Literal('x')));
         PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect1, selector("t2"));
         rightSource.setProperty(Property.SOURCE_NAME, selector("t2"));
@@ -303,12 +303,12 @@
         PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
         project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"), column("all", "a2", "b")));
         PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
-        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), "a2", "something"));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
-        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
                                                                       new Literal("t1"), new Literal("t0")));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
-        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                       new Literal("t3"), new Literal("t4")));
         PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
         source.setProperty(Property.SOURCE_NAME, selector("all"));
@@ -331,18 +331,18 @@
                                                               column("all", "a3", "c"),
                                                               column("all", "a4", "d")));
         PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
-        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), "a2", "something"));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
-        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
                                                                       new Literal("t1"), new Literal("t0")));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
-        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                       new Literal("t3"), new Literal("t4")));
         PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("all"));
-        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
                                                                       new Literal("t2"), new Literal("t0")));
         PlanNode select5 = new PlanNode(Type.SELECT, select4, selector("all"));
-        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                       new Literal("t4"), new Literal("t5")));
         PlanNode source = new PlanNode(Type.SOURCE, select5, selector("all"));
         source.setProperty(Property.SOURCE_NAME, selector("all"));
@@ -364,21 +364,20 @@
                                                               column("all", "a3", "c"),
                                                               column("all", "a4", "d")));
         PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
-        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a1"), "something"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), "a1", "something"));
         PlanNode join = new PlanNode(Type.JOIN, select1, selector("all"));
         join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
         join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
-        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("all"), name("a2"), selector("all"), name("a3")));
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("all"), "a2", selector("all"), "a3"));
 
         PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("all"));
         PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("all"));
         leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1"), column("all", "a2")));
         PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject, selector("all"));
-        leftSelect1.setProperty(Property.SELECT_CRITERIA,
-                                new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t1"),
-                                                new Literal("t0")));
+        leftSelect1.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
+                                                                          new Literal("t1"), new Literal("t0")));
         PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1, selector("all"));
-        leftSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        leftSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                           new Literal("t3"), new Literal("t4")));
         PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2, selector("all"));
         leftSource.setProperty(Property.SOURCE_NAME, selector("all"));
@@ -388,11 +387,10 @@
         PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("all"));
         rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a3"), column("all", "a4")));
         PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject, selector("all"));
-        rightSelect1.setProperty(Property.SELECT_CRITERIA,
-                                 new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t2"),
-                                                 new Literal("t0")));
+        rightSelect1.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
+                                                                           new Literal("t2"), new Literal("t0")));
         PlanNode rightSelect2 = new PlanNode(Type.SELECT, rightSelect1, selector("all"));
-        rightSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        rightSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                            new Literal("t4"), new Literal("t5")));
         PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect2, selector("all"));
         rightSource.setProperty(Property.SOURCE_NAME, selector("all"));
@@ -415,18 +413,18 @@
                                                               column("all", "a3", "c"),
                                                               column("all", "a4", "d")));
         PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
-        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), "a2", "something"));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
-        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
                                                                       new Literal("t1"), new Literal("t0")));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
-        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                       new Literal("t3"), new Literal("t4")));
         PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("all"));
-        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "primaryType"),
                                                                       new Literal("t2"), new Literal("t0")));
         PlanNode select5 = new PlanNode(Type.SELECT, select4, selector("all"));
-        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), "mixins"),
                                                                       new Literal("t4"), new Literal("t5")));
         PlanNode source = new PlanNode(Type.SOURCE, select5, selector("all"));
         source.setProperty(Property.SOURCE_NAME, selector("all"));
@@ -446,21 +444,17 @@
 
     protected Column column( String table,
                              String columnName ) {
-        return new Column(new SelectorName(table), name(columnName), columnName);
+        return new Column(new SelectorName(table), columnName, columnName);
     }
 
     protected Column column( String table,
                              String columnName,
                              String alias ) {
-        return new Column(new SelectorName(table), name(columnName), alias);
+        return new Column(new SelectorName(table), columnName, alias);
     }
 
-    protected Name name( String name ) {
-        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
-    }
-
     protected PlanNode optimize( String sql ) {
-        QueryCommand query = new SqlQueryParser().parseQuery(sql, context.getExecutionContext());
+        QueryCommand query = new SqlQueryParser().parseQuery(sql, context.getTypeSystem());
         Problems problems = context.getProblems();
         assertThat("Problems parsing query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
         PlanNode plan = new CanonicalPlanner().createPlan(context, query);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertThat;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -138,7 +139,8 @@
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldFailToParseUnknownLanguage() {
-        parsers.parse(new ExecutionContext(), "unknown language", "This is a bogus query");
+        TypeSystem typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
+        parsers.parse(typeSystem, "unknown language", "This is a bogus query");
     }
 
     protected static class MockParser implements QueryParser {
@@ -161,10 +163,11 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
+         * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String,
+         *      org.jboss.dna.graph.query.model.TypeSystem)
          */
         public QueryCommand parseQuery( String query,
-                                        ExecutionContext context ) throws InvalidQueryException {
+                                        TypeSystem typeSystem ) throws InvalidQueryException {
             throw new UnsupportedOperationException();
         }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -38,6 +38,7 @@
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.Between;
 import org.jboss.dna.graph.query.model.BindVariableName;
@@ -68,6 +69,7 @@
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.Source;
 import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
 import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
@@ -80,12 +82,12 @@
  */
 public class SqlQueryParserTest {
 
-    private ExecutionContext context;
+    private TypeSystem typeSystem;
     private SqlQueryParser parser;
 
     @Before
     public void beforeEach() {
-        context = new ExecutionContext();
+        typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
         parser = new SqlQueryParser();
     }
 
@@ -265,7 +267,7 @@
 
     protected void assertParseConstraint( String expression ) {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        parser.parseConstraint(tokens(expression), context, selector);
+        parser.parseConstraint(tokens(expression), typeSystem, selector);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -275,7 +277,7 @@
     @Test
     public void shouldParseConstraintFromStringWithValidBetweenExpressionUsing() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' AND 'upper'"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' AND 'upper'"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(Between.class)));
         Between between = (Between)constraint;
         assertThat(between.isLowerBoundIncluded(), is(true));
@@ -283,7 +285,7 @@
         assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue operand = (PropertyValue)between.getOperand();
         assertThat(operand.getSelectorName(), is(selector.getName()));
-        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(operand.getPropertyName(), is("id"));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat((Literal)between.getLowerBound(), is(literal("lower")));
@@ -294,7 +296,7 @@
     public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingExclusiveAndExclusive() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
         Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' EXCLUSIVE AND 'upper' EXCLUSIVE"),
-                                                       context,
+                                                       typeSystem,
                                                        selector);
         assertThat(constraint, is(instanceOf(Between.class)));
         Between between = (Between)constraint;
@@ -303,7 +305,7 @@
         assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue operand = (PropertyValue)between.getOperand();
         assertThat(operand.getSelectorName(), is(selector.getName()));
-        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(operand.getPropertyName(), is("id"));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat((Literal)between.getLowerBound(), is(literal("lower")));
@@ -314,7 +316,7 @@
     public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingInclusiveAndExclusive() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
         Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' AND 'upper' EXCLUSIVE"),
-                                                       context,
+                                                       typeSystem,
                                                        selector);
         assertThat(constraint, is(instanceOf(Between.class)));
         Between between = (Between)constraint;
@@ -323,7 +325,7 @@
         assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue operand = (PropertyValue)between.getOperand();
         assertThat(operand.getSelectorName(), is(selector.getName()));
-        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(operand.getPropertyName(), is("id"));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat((Literal)between.getLowerBound(), is(literal("lower")));
@@ -334,7 +336,7 @@
     public void shouldParseConstraintFromStringWithValidBetweenExpressionUsingExclusiveAndInclusive() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
         Constraint constraint = parser.parseConstraint(tokens("tableA.id BETWEEN 'lower' EXCLUSIVE AND 'upper'"),
-                                                       context,
+                                                       typeSystem,
                                                        selector);
         assertThat(constraint, is(instanceOf(Between.class)));
         Between between = (Between)constraint;
@@ -343,7 +345,7 @@
         assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue operand = (PropertyValue)between.getOperand();
         assertThat(operand.getSelectorName(), is(selector.getName()));
-        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(operand.getPropertyName(), is("id"));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat((Literal)between.getLowerBound(), is(literal("lower")));
@@ -353,7 +355,7 @@
     @Test
     public void shouldParseConstraintFromStringWithValidNotBetweenExpression() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("tableA.id NOT BETWEEN 'lower' AND 'upper'"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("tableA.id NOT BETWEEN 'lower' AND 'upper'"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(Not.class)));
         constraint = ((Not)constraint).getConstraint();
         assertThat(constraint, is(instanceOf(Between.class)));
@@ -363,7 +365,7 @@
         assertThat(between.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue operand = (PropertyValue)between.getOperand();
         assertThat(operand.getSelectorName(), is(selector.getName()));
-        assertThat(operand.getPropertyName(), is(name("id")));
+        assertThat(operand.getPropertyName(), is("id"));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat(between.getLowerBound(), is(instanceOf(Literal.class)));
         assertThat((Literal)between.getLowerBound(), is(literal("lower")));
@@ -377,28 +379,28 @@
     @Test
     public void shouldParseConstraintFromStringWithOuterParentheses() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("( ISSAMENODE('/a/b') )"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("( ISSAMENODE('/a/b') )"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)constraint;
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b")));
+        assertThat(same.getPath(), is("/a/b"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithMultipleOuterParentheses() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("((( ISSAMENODE('/a/b') )))"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("((( ISSAMENODE('/a/b') )))"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)constraint;
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b")));
+        assertThat(same.getPath(), is("/a/b"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithParenthesesAndConjunctionAndDisjunctions() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
         Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b') OR (ISSAMENODE('/c/d') AND ISSAMENODE('/e/f'))"),
-                                                       context,
+                                                       typeSystem,
                                                        selector);
         assertThat(constraint, is(instanceOf(Or.class)));
         Or or = (Or)constraint;
@@ -406,7 +408,7 @@
         assertThat(or.getLeft(), is(instanceOf(SameNode.class)));
         SameNode first = (SameNode)or.getLeft();
         assertThat(first.getSelectorName(), is(selectorName("tableA")));
-        assertThat(first.getPath(), is(path("/a/b")));
+        assertThat(first.getPath(), is("/a/b"));
 
         assertThat(or.getRight(), is(instanceOf(And.class)));
         And and = (And)or.getRight();
@@ -414,12 +416,12 @@
         assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
         SameNode second = (SameNode)and.getLeft();
         assertThat(second.getSelectorName(), is(selectorName("tableA")));
-        assertThat(second.getPath(), is(path("/c/d")));
+        assertThat(second.getPath(), is("/c/d"));
 
         assertThat(and.getRight(), is(instanceOf(SameNode.class)));
         SameNode third = (SameNode)and.getRight();
         assertThat(third.getSelectorName(), is(selectorName("tableA")));
-        assertThat(third.getPath(), is(path("/e/f")));
+        assertThat(third.getPath(), is("/e/f"));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -429,19 +431,21 @@
     @Test
     public void shouldParseConstraintFromStringWithAndExpressionWithNoParentheses() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND CONTAINS(p1,term1)"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND CONTAINS(p1,term1)"),
+                                                       typeSystem,
+                                                       selector);
         assertThat(constraint, is(instanceOf(And.class)));
         And and = (And)constraint;
 
         assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)and.getLeft();
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
 
         assertThat(and.getRight(), is(instanceOf(FullTextSearch.class)));
         FullTextSearch search = (FullTextSearch)and.getRight();
         assertThat(search.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search.getPropertyName(), is(name("p1")));
+        assertThat(search.getPropertyName(), is("p1"));
         assertThat(search.getFullTextSearchExpression(), is("term1"));
     }
 
@@ -449,7 +453,7 @@
     public void shouldParseConstraintFromStringWithMultipleAndExpressions() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
         Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND CONTAINS(p1,term1) AND CONTAINS(p2,term2)"),
-                                                       context,
+                                                       typeSystem,
                                                        selector);
         assertThat(constraint, is(instanceOf(And.class)));
         And and = (And)constraint;
@@ -457,7 +461,7 @@
         assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)and.getLeft();
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
 
         assertThat(and.getRight(), is(instanceOf(And.class)));
         And secondAnd = (And)and.getRight();
@@ -465,20 +469,20 @@
         assertThat(secondAnd.getLeft(), is(instanceOf(FullTextSearch.class)));
         FullTextSearch search1 = (FullTextSearch)secondAnd.getLeft();
         assertThat(search1.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search1.getPropertyName(), is(name("p1")));
+        assertThat(search1.getPropertyName(), is("p1"));
         assertThat(search1.getFullTextSearchExpression(), is("term1"));
 
         assertThat(secondAnd.getRight(), is(instanceOf(FullTextSearch.class)));
         FullTextSearch search2 = (FullTextSearch)secondAnd.getRight();
         assertThat(search2.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search2.getPropertyName(), is(name("p2")));
+        assertThat(search2.getPropertyName(), is("p2"));
         assertThat(search2.getFullTextSearchExpression(), is("term2"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithAndExpressionWithNoSecondConstraint() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND WHAT THE HECK IS THIS"), context, selector);
+        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND WHAT THE HECK IS THIS"), typeSystem, selector);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -488,19 +492,19 @@
     @Test
     public void shouldParseConstraintFromStringWithOrExpressionWithNoParentheses() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR CONTAINS(p1,term1)"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR CONTAINS(p1,term1)"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(Or.class)));
         Or or = (Or)constraint;
 
         assertThat(or.getLeft(), is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)or.getLeft();
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
 
         assertThat(or.getRight(), is(instanceOf(FullTextSearch.class)));
         FullTextSearch search = (FullTextSearch)or.getRight();
         assertThat(search.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search.getPropertyName(), is(name("p1")));
+        assertThat(search.getPropertyName(), is("p1"));
         assertThat(search.getFullTextSearchExpression(), is("term1"));
 
     }
@@ -508,7 +512,7 @@
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithOrExpressionWithNoSecondConstraint() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR WHAT THE HECK IS THIS"), context, selector);
+        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR WHAT THE HECK IS THIS"), typeSystem, selector);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -517,23 +521,23 @@
 
     @Test
     public void shouldParseConstraintFromStringWithNotSameNodeExpression() {
-        Constraint constraint = parser.parseConstraint(tokens("NOT(ISSAMENODE(tableA,'/a/b/c'))"), context, mock(Source.class));
+        Constraint constraint = parser.parseConstraint(tokens("NOT(ISSAMENODE(tableA,'/a/b/c'))"), typeSystem, mock(Source.class));
         assertThat(constraint, is(instanceOf(Not.class)));
         Not not = (Not)constraint;
         assertThat(not.getConstraint(), is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)not.getConstraint();
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithNotConstraintWithOutOpeningParenthesis() {
-        parser.parseConstraint(tokens("NOT CONTAINS(propertyA 'term1 term2 -term3')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("NOT CONTAINS(propertyA 'term1 term2 -term3')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithNotConstraintWithOutClosingParenthesis() {
-        parser.parseConstraint(tokens("NOT( CONTAINS(propertyA 'term1 term2 -term3') BLAH"), context, mock(Source.class));
+        parser.parseConstraint(tokens("NOT( CONTAINS(propertyA 'term1 term2 -term3') BLAH"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -543,30 +547,30 @@
     @Test
     public void shouldParseConstraintFromStringWithIsContainsExpressionWithPropertyAndNoSelectorNameOnlyIfThereIsOneSelectorSource() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(FullTextSearch.class)));
         FullTextSearch search = (FullTextSearch)constraint;
         assertThat(search.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search.getPropertyName(), is(name("propertyA")));
+        assertThat(search.getPropertyName(), is("propertyA"));
         assertThat(search.getFullTextSearchExpression(), is("term1 term2 -term3"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithIsContainsExpressionWithSelectorNameAndProperty() {
         Constraint constraint = parser.parseConstraint(tokens("CONTAINS(tableA.propertyA,'term1 term2 -term3')"),
-                                                       context,
+                                                       typeSystem,
                                                        mock(Source.class));
         assertThat(constraint, is(instanceOf(FullTextSearch.class)));
         FullTextSearch search = (FullTextSearch)constraint;
         assertThat(search.getSelectorName(), is(selectorName("tableA")));
-        assertThat(search.getPropertyName(), is(name("propertyA")));
+        assertThat(search.getPropertyName(), is("propertyA"));
         assertThat(search.getFullTextSearchExpression(), is("term1 term2 -term3"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithIsContainsExpressionWithSelectorNameAndAnyProperty() {
         Constraint constraint = parser.parseConstraint(tokens("CONTAINS(tableA.*,'term1 term2 -term3')"),
-                                                       context,
+                                                       typeSystem,
                                                        mock(Source.class));
         assertThat(constraint, is(instanceOf(FullTextSearch.class)));
         FullTextSearch search = (FullTextSearch)constraint;
@@ -577,22 +581,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoCommaAfterSelectorName() {
-        parser.parseConstraint(tokens("CONTAINS(propertyA 'term1 term2 -term3')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("CONTAINS(propertyA 'term1 term2 -term3')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoClosingParenthesis() {
-        parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3' OTHER"), context, mock(Source.class));
+        parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3' OTHER"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoOpeningParenthesis() {
-        parser.parseConstraint(tokens("CONTAINS propertyA,'term1 term2 -term3')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("CONTAINS propertyA,'term1 term2 -term3')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoSelectorNameIfSourceIsNotSelector() {
-        parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), context, mock(Join.class));
+        parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -602,40 +606,40 @@
     @Test
     public void shouldParseConstraintFromStringWithIsSameNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)constraint;
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithIsSameNodeExpressionWithSelectorNameAndPath() {
-        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c')"), context, mock(Source.class));
+        Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c')"), typeSystem, mock(Source.class));
         assertThat(constraint, is(instanceOf(SameNode.class)));
         SameNode same = (SameNode)constraint;
         assertThat(same.getSelectorName(), is(selectorName("tableA")));
-        assertThat(same.getPath(), is(path("/a/b/c")));
+        assertThat(same.getPath(), is("/a/b/c"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoCommaAfterSelectorName() {
-        parser.parseConstraint(tokens("ISSAMENODE(tableA '/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISSAMENODE(tableA '/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoClosingParenthesis() {
-        parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c' AND"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoOpeningParenthesis() {
-        parser.parseConstraint(tokens("ISSAMENODE tableA,'/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISSAMENODE tableA,'/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
-        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), context, mock(Join.class));
+        parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -645,40 +649,40 @@
     @Test
     public void shouldParseConstraintFromStringWithIsChildNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(ChildNode.class)));
         ChildNode child = (ChildNode)constraint;
         assertThat(child.getSelectorName(), is(selectorName("tableA")));
-        assertThat(child.getParentPath(), is(path("/a/b/c")));
+        assertThat(child.getParentPath(), is("/a/b/c"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithIsChildNodeExpressionWithSelectorNameAndPath() {
-        Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c')"), context, mock(Source.class));
+        Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c')"), typeSystem, mock(Source.class));
         assertThat(constraint, is(instanceOf(ChildNode.class)));
         ChildNode child = (ChildNode)constraint;
         assertThat(child.getSelectorName(), is(selectorName("tableA")));
-        assertThat(child.getParentPath(), is(path("/a/b/c")));
+        assertThat(child.getParentPath(), is("/a/b/c"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoCommaAfterSelectorName() {
-        parser.parseConstraint(tokens("ISCHILDNODE(tableA '/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISCHILDNODE(tableA '/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoClosingParenthesis() {
-        parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c' AND"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoOpeningParenthesis() {
-        parser.parseConstraint(tokens("ISCHILDNODE tableA,'/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISCHILDNODE tableA,'/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
-        parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), context, mock(Join.class));
+        parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -688,40 +692,42 @@
     @Test
     public void shouldParseConstraintFromStringWithIsDescendantNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
         NamedSelector selector = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), context, selector);
+        Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), typeSystem, selector);
         assertThat(constraint, is(instanceOf(DescendantNode.class)));
         DescendantNode descendant = (DescendantNode)constraint;
         assertThat(descendant.getSelectorName(), is(selectorName("tableA")));
-        assertThat(descendant.getAncestorPath(), is(path("/a/b/c")));
+        assertThat(descendant.getAncestorPath(), is("/a/b/c"));
     }
 
     @Test
     public void shouldParseConstraintFromStringWithIsDescendantNodeExpressionWithSelectorNameAndPath() {
-        Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c')"), context, mock(Source.class));
+        Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c')"),
+                                                       typeSystem,
+                                                       mock(Source.class));
         assertThat(constraint, is(instanceOf(DescendantNode.class)));
         DescendantNode descendant = (DescendantNode)constraint;
         assertThat(descendant.getSelectorName(), is(selectorName("tableA")));
-        assertThat(descendant.getAncestorPath(), is(path("/a/b/c")));
+        assertThat(descendant.getAncestorPath(), is("/a/b/c"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoCommaAfterSelectorName() {
-        parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA '/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA '/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoClosingParenthesis() {
-        parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c' AND"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoOpeningParenthesis() {
-        parser.parseConstraint(tokens("ISDESCENDANTNODE tableA,'/a/b/c')"), context, mock(Source.class));
+        parser.parseConstraint(tokens("ISDESCENDANTNODE tableA,'/a/b/c')"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
-        parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), context, mock(Join.class));
+        parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -730,14 +736,14 @@
 
     @Test
     public void shouldParseInClauseFromStringWithSingleValidLiteral() {
-        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1')"), context);
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1')"), typeSystem);
         assertThat(result.size(), is(1));
         assertThat(result.get(0), is((StaticOperand)literal("value1")));
     }
 
     @Test
     public void shouldParseInClauseFromStringWithTwoValidLiteral() {
-        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2')"), context);
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2')"), typeSystem);
         assertThat(result.size(), is(2));
         assertThat(result.get(0), is((StaticOperand)literal("value1")));
         assertThat(result.get(1), is((StaticOperand)literal("value2")));
@@ -745,7 +751,7 @@
 
     @Test
     public void shouldParseInClauseFromStringWithThreeValidLiteral() {
-        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2','value3')"), context);
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2','value3')"), typeSystem);
         assertThat(result.size(), is(3));
         assertThat(result.get(0), is((StaticOperand)literal("value1")));
         assertThat(result.get(1), is((StaticOperand)literal("value2")));
@@ -754,18 +760,19 @@
 
     @Test
     public void shouldParseInClauseFromStringWithSingleValidLiteralCast() {
-        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING))"), context);
+        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING))"), typeSystem);
         assertThat(result.size(), is(1));
         assertThat(result.iterator().next(), is((StaticOperand)literal("value1")));
 
-        result = parser.parseInClause(tokens("IN (CAST('3' AS LONG))"), context);
+        result = parser.parseInClause(tokens("IN (CAST('3' AS LONG))"), typeSystem);
         assertThat(result.size(), is(1));
         assertThat(result.iterator().next(), is((StaticOperand)literal(new Long(3))));
     }
 
     @Test
     public void shouldParseInClauseFromStringWithMultipleValidLiteralCasts() {
-        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING),CAST('3' AS LONG),'4')"), context);
+        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING),CAST('3' AS LONG),'4')"),
+                                                          typeSystem);
         assertThat(result.size(), is(3));
         assertThat(result.get(0), is((StaticOperand)literal("value1")));
         assertThat(result.get(1), is((StaticOperand)literal(new Long(3))));
@@ -840,7 +847,7 @@
 
     @Test
     public void shouldParserOrderByWithOneOrdering() {
-        List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC"), context, mock(Source.class));
+        List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC"), typeSystem, mock(Source.class));
         assertThat(orderBy.size(), is(1));
         Ordering first = orderBy.get(0);
         assertThat(first.getOperand(), is(instanceOf(NodeName.class)));
@@ -850,7 +857,7 @@
     @Test
     public void shouldParserOrderByWithTwoOrderings() {
         List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, SCORE(tableB) DESC"),
-                                                     context,
+                                                     typeSystem,
                                                      mock(Source.class));
         assertThat(orderBy.size(), is(2));
         Ordering first = orderBy.get(0);
@@ -864,7 +871,7 @@
     @Test
     public void shouldParserOrderByWithMultipleOrderings() {
         List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, SCORE(tableB) DESC, LENGTH(tableC.id) ASC"),
-                                                     context,
+                                                     typeSystem,
                                                      mock(Source.class));
         assertThat(orderBy.size(), is(3));
         Ordering first = orderBy.get(0);
@@ -880,12 +887,12 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseOrderByIfCommaNotFollowedByAnotherOrdering() {
-        parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, NOT A VALID ORDERING"), context, mock(Source.class));
+        parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, NOT A VALID ORDERING"), typeSystem, mock(Source.class));
     }
 
     @Test
     public void shouldReturnNullFromParseOrderByWithoutOrderByKeywords() {
-        assertThat(parser.parseOrderBy(tokens("NOT ORDER BY"), context, mock(Source.class)), is(nullValue()));
+        assertThat(parser.parseOrderBy(tokens("NOT ORDER BY"), typeSystem, mock(Source.class)), is(nullValue()));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -894,21 +901,21 @@
 
     @Test
     public void shouldParseOrderingFromDynamicOperandFollowedByAscendingKeyword() {
-        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) ASC"), context, mock(Source.class));
+        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) ASC"), typeSystem, mock(Source.class));
         assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
         assertThat(ordering.getOrder(), is(Order.ASCENDING));
     }
 
     @Test
     public void shouldParseOrderingFromDynamicOperandFollowedByDecendingKeyword() {
-        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) DESC"), context, mock(Source.class));
+        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) DESC"), typeSystem, mock(Source.class));
         assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
         assertThat(ordering.getOrder(), is(Order.DESCENDING));
     }
 
     @Test
     public void shouldParseOrderingFromDynamicOperandAndDefaultToAscendingWhenNotFollowedByAscendingOrDescendingKeyword() {
-        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) OTHER"), context, mock(Source.class));
+        Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) OTHER"), typeSystem, mock(Source.class));
         assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
         assertThat(ordering.getOrder(), is(Order.ASCENDING));
     }
@@ -919,45 +926,47 @@
 
     @Test
     public void shouldParsePropertyExistanceFromPropertyNameWithSelectorNameAndPropertyNameFollowedByIsNotNull() {
-        Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NOT NULL"), context, mock(Source.class));
+        Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NOT NULL"),
+                                                              typeSystem,
+                                                              mock(Source.class));
         assertThat(constraint, is(instanceOf(PropertyExistence.class)));
         PropertyExistence p = (PropertyExistence)constraint;
-        assertThat(p.getPropertyName(), is(name("property1")));
+        assertThat(p.getPropertyName(), is("property1"));
         assertThat(p.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldParsePropertyExistanceFromPropertyNameWithPropertyNameAndNoSelectorNameFollowedByIsNotNull() {
         NamedSelector source = new NamedSelector(selectorName("tableA"));
-        Constraint constraint = parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), context, source);
+        Constraint constraint = parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), typeSystem, source);
         assertThat(constraint, is(instanceOf(PropertyExistence.class)));
         PropertyExistence p = (PropertyExistence)constraint;
-        assertThat(p.getPropertyName(), is(name("property1")));
+        assertThat(p.getPropertyName(), is("property1"));
         assertThat(p.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParsePropertyExistanceFromPropertyNameWithNoSelectorNameIfSourceIsNotSelector() {
-        parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), context, mock(Source.class));
+        parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), typeSystem, mock(Source.class));
     }
 
     @Test
     public void shouldParseNotPropertyExistanceFromPropertyNameWithSelectorNameAndPropertyNameFollowedByIsNull() {
-        Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NULL"), context, mock(Source.class));
+        Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NULL"), typeSystem, mock(Source.class));
         assertThat(constraint, is(instanceOf(Not.class)));
         Not not = (Not)constraint;
         assertThat(not.getConstraint(), is(instanceOf(PropertyExistence.class)));
         PropertyExistence p = (PropertyExistence)not.getConstraint();
-        assertThat(p.getPropertyName(), is(name("property1")));
+        assertThat(p.getPropertyName(), is("property1"));
         assertThat(p.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldReturnNullFromParsePropertyExistanceIfExpressionDoesNotMatchPattern() {
         Source s = mock(Source.class);
-        assertThat(parser.parsePropertyExistance(tokens("tableA WILL NOT"), context, s), is(nullValue()));
-        assertThat(parser.parsePropertyExistance(tokens("tableA.property1 NOT NULL"), context, s), is(nullValue()));
-        assertThat(parser.parsePropertyExistance(tokens("tableA.property1 IS NOT SOMETHING"), context, s), is(nullValue()));
+        assertThat(parser.parsePropertyExistance(tokens("tableA WILL NOT"), typeSystem, s), is(nullValue()));
+        assertThat(parser.parsePropertyExistance(tokens("tableA.property1 NOT NULL"), typeSystem, s), is(nullValue()));
+        assertThat(parser.parsePropertyExistance(tokens("tableA.property1 IS NOT SOMETHING"), typeSystem, s), is(nullValue()));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -966,7 +975,7 @@
 
     @Test
     public void shouldParseStaticOperandFromStringWithBindVariable() {
-        StaticOperand operand = parser.parseStaticOperand(tokens("$VAR"), context);
+        StaticOperand operand = parser.parseStaticOperand(tokens("$VAR"), typeSystem);
         assertThat(operand, is(instanceOf(BindVariableName.class)));
         BindVariableName var = (BindVariableName)operand;
         assertThat(var.getVariableName(), is("VAR"));
@@ -974,20 +983,20 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseStaticOperandFromStringWithBindVariableWithNoVariableName() {
-        parser.parseStaticOperand(tokens("$"), context);
+        parser.parseStaticOperand(tokens("$"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseStaticOperandFromStringWithBindVariableWithCharactersThatAreNotFromNCName() {
-        parser.parseStaticOperand(tokens("$#2VAR"), context);
+        parser.parseStaticOperand(tokens("$#2VAR"), typeSystem);
     }
 
     @Test
     public void shouldParseStaticOperandFromStringWithLiteralValue() {
-        StaticOperand operand = parser.parseStaticOperand(tokens("CAST(123 AS DOUBLE)"), context);
+        StaticOperand operand = parser.parseStaticOperand(tokens("CAST(123 AS DOUBLE)"), typeSystem);
         assertThat(operand, is(instanceOf(Literal.class)));
         Literal literal = (Literal)operand;
-        assertThat((Double)literal.getValue(), is(context.getValueFactories().getDoubleFactory().create("123")));
+        assertThat((Double)literal.getValue(), is(typeSystem.getDoubleFactory().create("123")));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -996,142 +1005,147 @@
 
     @Test
     public void shouldParseLiteralFromStringWithCastBooleanLiteralToString() {
-        assertThat((String)parser.parseLiteral(tokens("CAST(true AS STRING)"), context).getValue(), is(Boolean.TRUE.toString()));
-        assertThat((String)parser.parseLiteral(tokens("CAST(false AS STRING)"), context).getValue(), is(Boolean.FALSE.toString()));
-        assertThat((String)parser.parseLiteral(tokens("CAST(TRUE AS STRING)"), context).getValue(), is(Boolean.TRUE.toString()));
-        assertThat((String)parser.parseLiteral(tokens("CAST(FALSE AS STRING)"), context).getValue(), is(Boolean.FALSE.toString()));
-        assertThat((String)parser.parseLiteral(tokens("CAST('true' AS stRinG)"), context).getValue(), is(Boolean.TRUE.toString()));
-        assertThat((String)parser.parseLiteral(tokens("CAST(\"false\" AS string)"), context).getValue(),
+        assertThat((String)parser.parseLiteral(tokens("CAST(true AS STRING)"), typeSystem).getValue(),
+                   is(Boolean.TRUE.toString()));
+        assertThat((String)parser.parseLiteral(tokens("CAST(false AS STRING)"), typeSystem).getValue(),
                    is(Boolean.FALSE.toString()));
+        assertThat((String)parser.parseLiteral(tokens("CAST(TRUE AS STRING)"), typeSystem).getValue(),
+                   is(Boolean.TRUE.toString()));
+        assertThat((String)parser.parseLiteral(tokens("CAST(FALSE AS STRING)"), typeSystem).getValue(),
+                   is(Boolean.FALSE.toString()));
+        assertThat((String)parser.parseLiteral(tokens("CAST('true' AS stRinG)"), typeSystem).getValue(),
+                   is(Boolean.TRUE.toString()));
+        assertThat((String)parser.parseLiteral(tokens("CAST(\"false\" AS string)"), typeSystem).getValue(),
+                   is(Boolean.FALSE.toString()));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastBooleanLiteralToBinary() {
-        Binary binaryTrue = context.getValueFactories().getBinaryFactory().create(true);
-        Binary binaryFalse = context.getValueFactories().getBinaryFactory().create(false);
-        assertThat((Binary)parser.parseLiteral(tokens("CAST(true AS BINARY)"), context).getValue(), is(binaryTrue));
-        assertThat((Binary)parser.parseLiteral(tokens("CAST(false AS BINARY)"), context).getValue(), is(binaryFalse));
-        assertThat((Binary)parser.parseLiteral(tokens("CAST(TRUE AS BINARY)"), context).getValue(), is(binaryTrue));
-        assertThat((Binary)parser.parseLiteral(tokens("CAST(FALSE AS BINARY)"), context).getValue(), is(binaryFalse));
-        assertThat((Binary)parser.parseLiteral(tokens("CAST('true' AS biNarY)"), context).getValue(), is(binaryTrue));
-        assertThat((Binary)parser.parseLiteral(tokens("CAST(\"false\" AS binary)"), context).getValue(), is(binaryFalse));
+        Binary binaryTrue = (Binary)typeSystem.getTypeFactory(PropertyType.BINARY.getName()).create(true);
+        Binary binaryFalse = (Binary)typeSystem.getTypeFactory(PropertyType.BINARY.getName()).create(false);
+        assertThat((Binary)parser.parseLiteral(tokens("CAST(true AS BINARY)"), typeSystem).getValue(), is(binaryTrue));
+        assertThat((Binary)parser.parseLiteral(tokens("CAST(false AS BINARY)"), typeSystem).getValue(), is(binaryFalse));
+        assertThat((Binary)parser.parseLiteral(tokens("CAST(TRUE AS BINARY)"), typeSystem).getValue(), is(binaryTrue));
+        assertThat((Binary)parser.parseLiteral(tokens("CAST(FALSE AS BINARY)"), typeSystem).getValue(), is(binaryFalse));
+        assertThat((Binary)parser.parseLiteral(tokens("CAST('true' AS biNarY)"), typeSystem).getValue(), is(binaryTrue));
+        assertThat((Binary)parser.parseLiteral(tokens("CAST(\"false\" AS binary)"), typeSystem).getValue(), is(binaryFalse));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToLong() {
-        parser.parseLiteral(tokens("CAST(true AS LONG)"), context);
+        parser.parseLiteral(tokens("CAST(true AS LONG)"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToDouble() {
-        parser.parseLiteral(tokens("CAST(true AS DOUBLE)"), context);
+        parser.parseLiteral(tokens("CAST(true AS DOUBLE)"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToDate() {
-        parser.parseLiteral(tokens("CAST(true AS DATE)"), context);
+        parser.parseLiteral(tokens("CAST(true AS DATE)"), typeSystem);
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastLongLiteralToString() {
-        assertThat((String)parser.parseLiteral(tokens("CAST(123 AS STRING)"), context).getValue(), is("123"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(+123 AS STRING)"), context).getValue(), is("123"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(-123 AS STRING)"), context).getValue(), is("-123"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(0 AS STRING)"), context).getValue(), is("0"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(123 AS STRING)"), typeSystem).getValue(), is("123"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(+123 AS STRING)"), typeSystem).getValue(), is("123"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(-123 AS STRING)"), typeSystem).getValue(), is("-123"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(0 AS STRING)"), typeSystem).getValue(), is("0"));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastLongLiteralToLong() {
-        assertThat((Long)parser.parseLiteral(tokens("CAST(123 AS LONG)"), context).getValue(), is(123L));
-        assertThat((Long)parser.parseLiteral(tokens("CAST(+123 AS LONG)"), context).getValue(), is(123L));
-        assertThat((Long)parser.parseLiteral(tokens("CAST(-123 AS LONG)"), context).getValue(), is(-123L));
-        assertThat((Long)parser.parseLiteral(tokens("CAST(0 AS LONG)"), context).getValue(), is(0L));
+        assertThat((Long)parser.parseLiteral(tokens("CAST(123 AS LONG)"), typeSystem).getValue(), is(123L));
+        assertThat((Long)parser.parseLiteral(tokens("CAST(+123 AS LONG)"), typeSystem).getValue(), is(123L));
+        assertThat((Long)parser.parseLiteral(tokens("CAST(-123 AS LONG)"), typeSystem).getValue(), is(-123L));
+        assertThat((Long)parser.parseLiteral(tokens("CAST(0 AS LONG)"), typeSystem).getValue(), is(0L));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastDoubleLiteralToString() {
-        assertThat((String)parser.parseLiteral(tokens("CAST(1.23 AS STRING)"), context).getValue(), is("1.23"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(+1.23 AS STRING)"), context).getValue(), is("1.23"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(-1.23 AS STRING)"), context).getValue(), is("-1.23"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e10 AS STRING)"), context).getValue(), is("1.23E10"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e+10 AS STRING)"), context).getValue(), is("1.23E10"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e-10 AS STRING)"), context).getValue(), is("1.23E-10"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(1.23 AS STRING)"), typeSystem).getValue(), is("1.23"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(+1.23 AS STRING)"), typeSystem).getValue(), is("1.23"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(-1.23 AS STRING)"), typeSystem).getValue(), is("-1.23"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e10 AS STRING)"), typeSystem).getValue(), is("1.23E10"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e+10 AS STRING)"), typeSystem).getValue(), is("1.23E10"));
+        assertThat((String)parser.parseLiteral(tokens("CAST(1.23e-10 AS STRING)"), typeSystem).getValue(), is("1.23E-10"));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastDateLiteralToString() {
-        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345Z AS STRING)"), context).getValue(),
+        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345Z AS STRING)"), typeSystem).getValue(),
                    is("2009-03-22T03:22:45.345Z"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345UTC AS STRING)"), context).getValue(),
+        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345UTC AS STRING)"), typeSystem).getValue(),
                    is("2009-03-22T03:22:45.345Z"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.3-01:00 AS STRING)"), context).getValue(),
+        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.3-01:00 AS STRING)"), typeSystem).getValue(),
                    is("2009-03-22T04:22:45.300Z"));
-        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345+01:00 AS STRING)"), context).getValue(),
+        assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345+01:00 AS STRING)"), typeSystem).getValue(),
                    is("2009-03-22T02:22:45.345Z"));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastStringLiteralToName() {
-        assertThat((Name)parser.parseLiteral(tokens("CAST([dna:name] AS NAME)"), context).getValue(), is(name("dna:name")));
-        assertThat((Name)parser.parseLiteral(tokens("CAST('dna:name' AS NAME)"), context).getValue(), is(name("dna:name")));
-        assertThat((Name)parser.parseLiteral(tokens("CAST(\"dna:name\" AS NAME)"), context).getValue(), is(name("dna:name")));
+        assertThat((Name)parser.parseLiteral(tokens("CAST([dna:name] AS NAME)"), typeSystem).getValue(), is(name("dna:name")));
+        assertThat((Name)parser.parseLiteral(tokens("CAST('dna:name' AS NAME)"), typeSystem).getValue(), is(name("dna:name")));
+        assertThat((Name)parser.parseLiteral(tokens("CAST(\"dna:name\" AS NAME)"), typeSystem).getValue(), is(name("dna:name")));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithCastStringLiteralToPath() {
-        assertThat((Path)parser.parseLiteral(tokens("CAST([/dna:name/a/b] AS PATH)"), context).getValue(),
+        assertThat((Path)parser.parseLiteral(tokens("CAST([/dna:name/a/b] AS PATH)"), typeSystem).getValue(),
                    is(path("/dna:name/a/b")));
     }
 
     @Test
     public void shouldParseLiteralFromStringWithUncastLiteralValueAndRepresentValueAsStringRepresentation() {
-        assertThat(parser.parseLiteral(tokens("true"), context).getValue(), is((Object)Boolean.TRUE.toString()));
-        assertThat(parser.parseLiteral(tokens("false"), context).getValue(), is((Object)Boolean.FALSE.toString()));
-        assertThat(parser.parseLiteral(tokens("TRUE"), context).getValue(), is((Object)Boolean.TRUE.toString()));
-        assertThat(parser.parseLiteral(tokens("FALSE"), context).getValue(), is((Object)Boolean.FALSE.toString()));
-        assertThat(parser.parseLiteral(tokens("123"), context).getValue(), is((Object)"123"));
-        assertThat(parser.parseLiteral(tokens("+123"), context).getValue(), is((Object)"123"));
-        assertThat(parser.parseLiteral(tokens("-123"), context).getValue(), is((Object)"-123"));
-        assertThat(parser.parseLiteral(tokens("1.23"), context).getValue(), is((Object)"1.23"));
-        assertThat(parser.parseLiteral(tokens("+1.23"), context).getValue(), is((Object)"1.23"));
-        assertThat(parser.parseLiteral(tokens("-1.23"), context).getValue(), is((Object)"-1.23"));
-        assertThat(parser.parseLiteral(tokens("1.23e10"), context).getValue(), is((Object)"1.23E10"));
-        assertThat(parser.parseLiteral(tokens("1.23e+10"), context).getValue(), is((Object)"1.23E10"));
-        assertThat(parser.parseLiteral(tokens("1.23e-10"), context).getValue(), is((Object)"1.23E-10"));
-        assertThat(parser.parseLiteral(tokens("0"), context).getValue(), is((Object)"0"));
-        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345Z"), context).getValue(),
+        assertThat(parser.parseLiteral(tokens("true"), typeSystem).getValue(), is((Object)Boolean.TRUE.toString()));
+        assertThat(parser.parseLiteral(tokens("false"), typeSystem).getValue(), is((Object)Boolean.FALSE.toString()));
+        assertThat(parser.parseLiteral(tokens("TRUE"), typeSystem).getValue(), is((Object)Boolean.TRUE.toString()));
+        assertThat(parser.parseLiteral(tokens("FALSE"), typeSystem).getValue(), is((Object)Boolean.FALSE.toString()));
+        assertThat(parser.parseLiteral(tokens("123"), typeSystem).getValue(), is((Object)"123"));
+        assertThat(parser.parseLiteral(tokens("+123"), typeSystem).getValue(), is((Object)"123"));
+        assertThat(parser.parseLiteral(tokens("-123"), typeSystem).getValue(), is((Object)"-123"));
+        assertThat(parser.parseLiteral(tokens("1.23"), typeSystem).getValue(), is((Object)"1.23"));
+        assertThat(parser.parseLiteral(tokens("+1.23"), typeSystem).getValue(), is((Object)"1.23"));
+        assertThat(parser.parseLiteral(tokens("-1.23"), typeSystem).getValue(), is((Object)"-1.23"));
+        assertThat(parser.parseLiteral(tokens("1.23e10"), typeSystem).getValue(), is((Object)"1.23E10"));
+        assertThat(parser.parseLiteral(tokens("1.23e+10"), typeSystem).getValue(), is((Object)"1.23E10"));
+        assertThat(parser.parseLiteral(tokens("1.23e-10"), typeSystem).getValue(), is((Object)"1.23E-10"));
+        assertThat(parser.parseLiteral(tokens("0"), typeSystem).getValue(), is((Object)"0"));
+        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345Z"), typeSystem).getValue(),
                    is((Object)"2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345UTC"), context).getValue(),
+        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345UTC"), typeSystem).getValue(),
                    is((Object)"2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.3-01:00"), context).getValue(),
+        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.3-01:00"), typeSystem).getValue(),
                    is((Object)"2009-03-22T04:22:45.300Z"));
-        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345+01:00"), context).getValue(),
+        assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345+01:00"), typeSystem).getValue(),
                    is((Object)"2009-03-22T02:22:45.345Z"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastAndNoEndingParenthesis() {
-        parser.parseLiteral(tokens("CAST(123 AS STRING OTHER"), context);
+        parser.parseLiteral(tokens("CAST(123 AS STRING OTHER"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastAndNoOpeningParenthesis() {
-        parser.parseLiteral(tokens("CAST 123 AS STRING) OTHER"), context);
+        parser.parseLiteral(tokens("CAST 123 AS STRING) OTHER"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastAndInvalidType() {
-        parser.parseLiteral(tokens("CAST(123 AS FOOD) OTHER"), context);
+        parser.parseLiteral(tokens("CAST(123 AS FOOD) OTHER"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastAndNoAsKeyword() {
-        parser.parseLiteral(tokens("CAST(123 STRING) OTHER"), context);
+        parser.parseLiteral(tokens("CAST(123 STRING) OTHER"), typeSystem);
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseLiteralFromStringWithCastAndNoLiteralValueBeforeAs() {
-        parser.parseLiteral(tokens("CAST(AS STRING) OTHER"), context);
+        parser.parseLiteral(tokens("CAST(AS STRING) OTHER"), typeSystem);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1140,54 +1154,55 @@
 
     @Test
     public void shouldParseLiteralValueFromStringWithPositiveAndNegativeIntegerValues() {
-        assertThat(parser.parseLiteralValue(tokens("123"), context), is("123"));
-        assertThat(parser.parseLiteralValue(tokens("-123"), context), is("-123"));
-        assertThat(parser.parseLiteralValue(tokens("- 123"), context), is("-123"));
-        assertThat(parser.parseLiteralValue(tokens("+123"), context), is("123"));
-        assertThat(parser.parseLiteralValue(tokens("+ 123"), context), is("123"));
-        assertThat(parser.parseLiteralValue(tokens("0"), context), is("0"));
+        assertThat(parser.parseLiteralValue(tokens("123"), typeSystem), is("123"));
+        assertThat(parser.parseLiteralValue(tokens("-123"), typeSystem), is("-123"));
+        assertThat(parser.parseLiteralValue(tokens("- 123"), typeSystem), is("-123"));
+        assertThat(parser.parseLiteralValue(tokens("+123"), typeSystem), is("123"));
+        assertThat(parser.parseLiteralValue(tokens("+ 123"), typeSystem), is("123"));
+        assertThat(parser.parseLiteralValue(tokens("0"), typeSystem), is("0"));
     }
 
     @Test
     public void shouldParseLiteralValueFromStringWithPositiveAndNegativeDecimalValues() {
-        assertThat(parser.parseLiteralValue(tokens("1.23"), context), is("1.23"));
-        assertThat(parser.parseLiteralValue(tokens("-1.23"), context), is("-1.23"));
-        assertThat(parser.parseLiteralValue(tokens("+0.123"), context), is("0.123"));
+        assertThat(parser.parseLiteralValue(tokens("1.23"), typeSystem), is("1.23"));
+        assertThat(parser.parseLiteralValue(tokens("-1.23"), typeSystem), is("-1.23"));
+        assertThat(parser.parseLiteralValue(tokens("+0.123"), typeSystem), is("0.123"));
     }
 
     @Test
     public void shouldParseLiteralValueFromStringWithPositiveAndNegativeDecimalValuesInScientificNotation() {
-        assertThat(parser.parseLiteralValue(tokens("1.23"), context), is("1.23"));
-        assertThat(parser.parseLiteralValue(tokens("1.23e10"), context), is("1.23E10"));
-        assertThat(parser.parseLiteralValue(tokens("- 1.23e10"), context), is("-1.23E10"));
-        assertThat(parser.parseLiteralValue(tokens("- 1.23e-10"), context), is("-1.23E-10"));
+        assertThat(parser.parseLiteralValue(tokens("1.23"), typeSystem), is("1.23"));
+        assertThat(parser.parseLiteralValue(tokens("1.23e10"), typeSystem), is("1.23E10"));
+        assertThat(parser.parseLiteralValue(tokens("- 1.23e10"), typeSystem), is("-1.23E10"));
+        assertThat(parser.parseLiteralValue(tokens("- 1.23e-10"), typeSystem), is("-1.23E-10"));
     }
 
     @Test
     public void shouldParseLiteralValueFromStringWithBooleanValues() {
-        assertThat(parser.parseLiteralValue(tokens("true"), context), is(Boolean.TRUE.toString()));
-        assertThat(parser.parseLiteralValue(tokens("false"), context), is(Boolean.FALSE.toString()));
-        assertThat(parser.parseLiteralValue(tokens("TRUE"), context), is(Boolean.TRUE.toString()));
-        assertThat(parser.parseLiteralValue(tokens("FALSE"), context), is(Boolean.FALSE.toString()));
+        assertThat(parser.parseLiteralValue(tokens("true"), typeSystem), is(Boolean.TRUE.toString()));
+        assertThat(parser.parseLiteralValue(tokens("false"), typeSystem), is(Boolean.FALSE.toString()));
+        assertThat(parser.parseLiteralValue(tokens("TRUE"), typeSystem), is(Boolean.TRUE.toString()));
+        assertThat(parser.parseLiteralValue(tokens("FALSE"), typeSystem), is(Boolean.FALSE.toString()));
     }
 
     @Test
     public void shouldParseLiteralValueFromStringWithDateValues() {
         // sYYYY-MM-DDThh:mm:ss.sssTZD
-        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345Z"), context), is("2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345UTC"), context), is("2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.3-01:00"), context), is("2009-03-22T04:22:45.300Z"));
-        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345+01:00"), context), is("2009-03-22T02:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345Z"), typeSystem), is("2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345UTC"), typeSystem), is("2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.3-01:00"), typeSystem), is("2009-03-22T04:22:45.300Z"));
+        assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345+01:00"), typeSystem), is("2009-03-22T02:22:45.345Z"));
 
-        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345Z"), context), is("-2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345UTC"), context), is("-2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.3-01:00"), context), is("-2009-03-22T04:22:45.300Z"));
-        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345+01:00"), context), is("-2009-03-22T02:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345Z"), typeSystem), is("-2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345UTC"), typeSystem), is("-2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.3-01:00"), typeSystem), is("-2009-03-22T04:22:45.300Z"));
+        assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345+01:00"), typeSystem),
+                   is("-2009-03-22T02:22:45.345Z"));
 
-        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345Z"), context), is("2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345UTC"), context), is("2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.3-01:00"), context), is("2009-03-22T04:22:45.300Z"));
-        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345+01:00"), context), is("2009-03-22T02:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345Z"), typeSystem), is("2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345UTC"), typeSystem), is("2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.3-01:00"), typeSystem), is("2009-03-22T04:22:45.300Z"));
+        assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345+01:00"), typeSystem), is("2009-03-22T02:22:45.345Z"));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1196,58 +1211,61 @@
 
     @Test
     public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeIntegerValues() {
-        assertThat(parser.parseLiteralValue(tokens("'123'"), context), is("123"));
-        assertThat(parser.parseLiteralValue(tokens("'-123'"), context), is("-123"));
-        assertThat(parser.parseLiteralValue(tokens("'- 123'"), context), is("- 123"));
-        assertThat(parser.parseLiteralValue(tokens("'+123'"), context), is("+123"));
-        assertThat(parser.parseLiteralValue(tokens("'+ 123'"), context), is("+ 123"));
-        assertThat(parser.parseLiteralValue(tokens("'0'"), context), is("0"));
+        assertThat(parser.parseLiteralValue(tokens("'123'"), typeSystem), is("123"));
+        assertThat(parser.parseLiteralValue(tokens("'-123'"), typeSystem), is("-123"));
+        assertThat(parser.parseLiteralValue(tokens("'- 123'"), typeSystem), is("- 123"));
+        assertThat(parser.parseLiteralValue(tokens("'+123'"), typeSystem), is("+123"));
+        assertThat(parser.parseLiteralValue(tokens("'+ 123'"), typeSystem), is("+ 123"));
+        assertThat(parser.parseLiteralValue(tokens("'0'"), typeSystem), is("0"));
     }
 
     @Test
     public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeDecimalValues() {
-        assertThat(parser.parseLiteralValue(tokens("'1.23'"), context), is("1.23"));
-        assertThat(parser.parseLiteralValue(tokens("'-1.23'"), context), is("-1.23"));
-        assertThat(parser.parseLiteralValue(tokens("'+0.123'"), context), is("+0.123"));
+        assertThat(parser.parseLiteralValue(tokens("'1.23'"), typeSystem), is("1.23"));
+        assertThat(parser.parseLiteralValue(tokens("'-1.23'"), typeSystem), is("-1.23"));
+        assertThat(parser.parseLiteralValue(tokens("'+0.123'"), typeSystem), is("+0.123"));
     }
 
     @Test
     public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeDecimalValuesInScientificNotation() {
-        assertThat(parser.parseLiteralValue(tokens("'1.23'"), context), is("1.23"));
-        assertThat(parser.parseLiteralValue(tokens("'1.23e10'"), context), is("1.23e10"));
-        assertThat(parser.parseLiteralValue(tokens("'- 1.23e10'"), context), is("- 1.23e10"));
-        assertThat(parser.parseLiteralValue(tokens("'- 1.23e-10'"), context), is("- 1.23e-10"));
+        assertThat(parser.parseLiteralValue(tokens("'1.23'"), typeSystem), is("1.23"));
+        assertThat(parser.parseLiteralValue(tokens("'1.23e10'"), typeSystem), is("1.23e10"));
+        assertThat(parser.parseLiteralValue(tokens("'- 1.23e10'"), typeSystem), is("- 1.23e10"));
+        assertThat(parser.parseLiteralValue(tokens("'- 1.23e-10'"), typeSystem), is("- 1.23e-10"));
     }
 
     @Test
     public void shouldParseLiteralValueFromQuotedStringWithBooleanValues() {
-        assertThat(parser.parseLiteralValue(tokens("'true'"), context), is("true"));
-        assertThat(parser.parseLiteralValue(tokens("'false'"), context), is("false"));
-        assertThat(parser.parseLiteralValue(tokens("'TRUE'"), context), is("TRUE"));
-        assertThat(parser.parseLiteralValue(tokens("'FALSE'"), context), is("FALSE"));
+        assertThat(parser.parseLiteralValue(tokens("'true'"), typeSystem), is("true"));
+        assertThat(parser.parseLiteralValue(tokens("'false'"), typeSystem), is("false"));
+        assertThat(parser.parseLiteralValue(tokens("'TRUE'"), typeSystem), is("TRUE"));
+        assertThat(parser.parseLiteralValue(tokens("'FALSE'"), typeSystem), is("FALSE"));
     }
 
     @Test
     public void shouldParseLiteralValueFromQuotedStringWithDateValues() {
         // sYYYY-MM-DDThh:mm:ss.sssTZD
-        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345Z'"), context), is("2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345UTC'"), context), is("2009-03-22T03:22:45.345UTC"));
-        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.3-01:00'"), context), is("2009-03-22T03:22:45.3-01:00"));
-        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345+01:00'"), context),
+        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345Z'"), typeSystem), is("2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345UTC'"), typeSystem), is("2009-03-22T03:22:45.345UTC"));
+        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.3-01:00'"), typeSystem),
+                   is("2009-03-22T03:22:45.3-01:00"));
+        assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345+01:00'"), typeSystem),
                    is("2009-03-22T03:22:45.345+01:00"));
 
-        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345Z'"), context), is("-2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345UTC'"), context), is("-2009-03-22T03:22:45.345UTC"));
-        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.3-01:00'"), context),
+        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345Z'"), typeSystem), is("-2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345UTC'"), typeSystem),
+                   is("-2009-03-22T03:22:45.345UTC"));
+        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.3-01:00'"), typeSystem),
                    is("-2009-03-22T03:22:45.3-01:00"));
-        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345+01:00'"), context),
+        assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345+01:00'"), typeSystem),
                    is("-2009-03-22T03:22:45.345+01:00"));
 
-        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345Z'"), context), is("+2009-03-22T03:22:45.345Z"));
-        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345UTC'"), context), is("+2009-03-22T03:22:45.345UTC"));
-        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.3-01:00'"), context),
+        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345Z'"), typeSystem), is("+2009-03-22T03:22:45.345Z"));
+        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345UTC'"), typeSystem),
+                   is("+2009-03-22T03:22:45.345UTC"));
+        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.3-01:00'"), typeSystem),
                    is("+2009-03-22T03:22:45.3-01:00"));
-        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345+01:00'"), context),
+        assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345+01:00'"), typeSystem),
                    is("+2009-03-22T03:22:45.345+01:00"));
     }
 
@@ -1257,30 +1275,30 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingLengthOfPropertyValue() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("LENGTH(tableA.property)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("LENGTH(tableA.property)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(Length.class)));
         Length length = (Length)operand;
-        assertThat(length.getPropertyValue().getPropertyName(), is(name("property")));
+        assertThat(length.getPropertyValue().getPropertyName(), is("property"));
         assertThat(length.getPropertyValue().getSelectorName(), is(selectorName("tableA")));
         assertThat(length.getSelectorName(), is(selectorName("tableA")));
 
         Source source = new NamedSelector(selectorName("tableA"));
-        operand = parser.parseDynamicOperand(tokens("LENGTH(property)"), context, source);
+        operand = parser.parseDynamicOperand(tokens("LENGTH(property)"), typeSystem, source);
         assertThat(operand, is(instanceOf(Length.class)));
         length = (Length)operand;
-        assertThat(length.getPropertyValue().getPropertyName(), is(name("property")));
+        assertThat(length.getPropertyValue().getPropertyName(), is("property"));
         assertThat(length.getPropertyValue().getSelectorName(), is(selectorName("tableA")));
         assertThat(length.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLengthWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("LENGTH(tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LENGTH(tableA.property other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLengthWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("LENGTH tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LENGTH tableA.property other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1289,29 +1307,31 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingLowerOfAnotherDynamicOperand() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(tableA.property)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(tableA.property)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(LowerCase.class)));
         LowerCase lower = (LowerCase)operand;
         assertThat(lower.getSelectorName(), is(selectorName("tableA")));
         assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)lower.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
 
         Source source = new NamedSelector(selectorName("tableA"));
-        operand = parser.parseDynamicOperand(tokens("LOWER(property)"), context, source);
+        operand = parser.parseDynamicOperand(tokens("LOWER(property)"), typeSystem, source);
         assertThat(operand, is(instanceOf(LowerCase.class)));
         lower = (LowerCase)operand;
         assertThat(lower.getSelectorName(), is(selectorName("tableA")));
         assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
         value = (PropertyValue)lower.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingLowerOfUpperCaseOfAnotherOperand() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(UPPER(tableA.property))"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(UPPER(tableA.property))"),
+                                                            typeSystem,
+                                                            mock(Source.class));
         assertThat(operand, is(instanceOf(LowerCase.class)));
         LowerCase lower = (LowerCase)operand;
         assertThat(lower.getSelectorName(), is(selectorName("tableA")));
@@ -1320,18 +1340,18 @@
         assertThat(upper.getSelectorName(), is(selectorName("tableA")));
         assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)upper.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLowerWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("LOWER(tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LOWER(tableA.property other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLowerWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("LOWER tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LOWER tableA.property other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1340,29 +1360,31 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingUpperOfAnotherDynamicOperand() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(tableA.property)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(tableA.property)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(UpperCase.class)));
         UpperCase upper = (UpperCase)operand;
         assertThat(upper.getSelectorName(), is(selectorName("tableA")));
         assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)upper.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
 
         Source source = new NamedSelector(selectorName("tableA"));
-        operand = parser.parseDynamicOperand(tokens("UPPER(property)"), context, source);
+        operand = parser.parseDynamicOperand(tokens("UPPER(property)"), typeSystem, source);
         assertThat(operand, is(instanceOf(UpperCase.class)));
         upper = (UpperCase)operand;
         assertThat(upper.getSelectorName(), is(selectorName("tableA")));
         assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
         value = (PropertyValue)upper.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingUpperOfLowerCaseOfAnotherOperand() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(LOWER(tableA.property))"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(LOWER(tableA.property))"),
+                                                            typeSystem,
+                                                            mock(Source.class));
         assertThat(operand, is(instanceOf(UpperCase.class)));
         UpperCase upper = (UpperCase)operand;
         assertThat(upper.getSelectorName(), is(selectorName("tableA")));
@@ -1371,18 +1393,18 @@
         assertThat(lower.getSelectorName(), is(selectorName("tableA")));
         assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)lower.getOperand();
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingUpperWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("UPPER(tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("UPPER(tableA.property other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingUpperWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("Upper tableA.property other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("Upper tableA.property other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1391,7 +1413,7 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingDepthOfSelector() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH(tableA)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH(tableA)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(NodeDepth.class)));
         NodeDepth depth = (NodeDepth)operand;
         assertThat(depth.getSelectorName(), is(selectorName("tableA")));
@@ -1400,7 +1422,7 @@
     @Test
     public void shouldParseDynamicOperandFromStringContainingDepthWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH()"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH()"), typeSystem, source);
         assertThat(operand, is(instanceOf(NodeDepth.class)));
         NodeDepth depth = (NodeDepth)operand;
         assertThat(depth.getSelectorName(), is(selectorName("tableA")));
@@ -1408,22 +1430,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingDepthWithNoSelectorIfTheSourceIsNotASelector() {
-        parser.parseDynamicOperand(tokens("DEPTH()"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("DEPTH()"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingDepthWithSelectorNameAndProperty() {
-        parser.parseDynamicOperand(tokens("DEPTH(tableA.property) other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("DEPTH(tableA.property) other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingDepthWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("DEPTH(tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("DEPTH(tableA other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingDepthWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("Depth  tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("Depth  tableA other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1432,7 +1454,7 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingPathOfSelector() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH(tableA)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH(tableA)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(NodePath.class)));
         NodePath path = (NodePath)operand;
         assertThat(path.getSelectorName(), is(selectorName("tableA")));
@@ -1441,7 +1463,7 @@
     @Test
     public void shouldParseDynamicOperandFromStringContainingPathWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH()"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH()"), typeSystem, source);
         assertThat(operand, is(instanceOf(NodePath.class)));
         NodePath path = (NodePath)operand;
         assertThat(path.getSelectorName(), is(selectorName("tableA")));
@@ -1449,22 +1471,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingPathWithNoSelectorIfTheSourceIsNotASelector() {
-        parser.parseDynamicOperand(tokens("PATH()"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("PATH()"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingPathWithSelectorNameAndProperty() {
-        parser.parseDynamicOperand(tokens("PATH(tableA.property) other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("PATH(tableA.property) other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingPathWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("PATH(tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("PATH(tableA other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingPathWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("Path  tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("Path  tableA other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1473,7 +1495,7 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingNameOfSelector() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME(tableA)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME(tableA)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(NodeName.class)));
         NodeName name = (NodeName)operand;
         assertThat(name.getSelectorName(), is(selectorName("tableA")));
@@ -1482,7 +1504,7 @@
     @Test
     public void shouldParseDynamicOperandFromStringContainingNameWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME()"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME()"), typeSystem, source);
         assertThat(operand, is(instanceOf(NodeName.class)));
         NodeName name = (NodeName)operand;
         assertThat(name.getSelectorName(), is(selectorName("tableA")));
@@ -1490,22 +1512,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingNameWithNoSelectorIfTheSourceIsNotASelector() {
-        parser.parseDynamicOperand(tokens("NAME()"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("NAME()"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingNameWithSelectorNameAndProperty() {
-        parser.parseDynamicOperand(tokens("NAME(tableA.property) other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("NAME(tableA.property) other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingNameWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("NAME(tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("NAME(tableA other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingNameWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("Name  tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("Name  tableA other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1514,7 +1536,7 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingLocalNameOfSelector() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME(tableA)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME(tableA)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(NodeLocalName.class)));
         NodeLocalName name = (NodeLocalName)operand;
         assertThat(name.getSelectorName(), is(selectorName("tableA")));
@@ -1523,7 +1545,7 @@
     @Test
     public void shouldParseDynamicOperandFromStringContainingLocalNameWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME()"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME()"), typeSystem, source);
         assertThat(operand, is(instanceOf(NodeLocalName.class)));
         NodeLocalName name = (NodeLocalName)operand;
         assertThat(name.getSelectorName(), is(selectorName("tableA")));
@@ -1531,22 +1553,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithNoSelectorIfTheSourceIsNotASelector() {
-        parser.parseDynamicOperand(tokens("LOCALNAME()"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LOCALNAME()"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithSelectorNameAndProperty() {
-        parser.parseDynamicOperand(tokens("LOCALNAME(tableA.property) other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LOCALNAME(tableA.property) other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("LOCALNAME(tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LOCALNAME(tableA other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("LocalName  tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("LocalName  tableA other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1555,7 +1577,7 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringContainingFullTextSearchScoreOfSelector() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE(tableA)"), context, mock(Source.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE(tableA)"), typeSystem, mock(Source.class));
         assertThat(operand, is(instanceOf(FullTextSearchScore.class)));
         FullTextSearchScore score = (FullTextSearchScore)operand;
         assertThat(score.getSelectorName(), is(selectorName("tableA")));
@@ -1564,7 +1586,7 @@
     @Test
     public void shouldParseDynamicOperandFromStringContainingFullTextSearchScoreWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE()"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE()"), typeSystem, source);
         assertThat(operand, is(instanceOf(FullTextSearchScore.class)));
         FullTextSearchScore score = (FullTextSearchScore)operand;
         assertThat(score.getSelectorName(), is(selectorName("tableA")));
@@ -1572,22 +1594,22 @@
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithNoSelectorIfTheSourceIsNotASelector() {
-        parser.parseDynamicOperand(tokens("SCORE()"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("SCORE()"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithWithSelectorNameAndProperty() {
-        parser.parseDynamicOperand(tokens("SCORE(tableA.property) other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("SCORE(tableA.property) other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithoutClosingParenthesis() {
-        parser.parseDynamicOperand(tokens("SCORE(tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("SCORE(tableA other"), typeSystem, mock(Source.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithoutOpeningParenthesis() {
-        parser.parseDynamicOperand(tokens("Score  tableA other"), context, mock(Source.class));
+        parser.parseDynamicOperand(tokens("Score  tableA other"), typeSystem, mock(Source.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1596,49 +1618,49 @@
 
     @Test
     public void shouldParseDynamicOperandFromStringWithUnquotedSelectorNameAndUnquotedPropertyName() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("tableA.property"), context, mock(Join.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("tableA.property"), typeSystem, mock(Join.class));
         assertThat(operand, is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)operand;
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldParseDynamicOperandFromStringWithQuotedSelectorNameAndUnquotedPropertyName() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].property"), context, mock(Join.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].property"), typeSystem, mock(Join.class));
         assertThat(operand, is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)operand;
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
     }
 
     @Test
     public void shouldParseDynamicOperandFromStringWithQuotedSelectorNameAndQuotedPropertyName() {
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].[dna:property]"), context, mock(Join.class));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].[dna:property]"), typeSystem, mock(Join.class));
         assertThat(operand, is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)operand;
-        assertThat(value.getPropertyName(), is(name("dna:property")));
+        assertThat(value.getPropertyName(), is("dna:property"));
         assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
     }
 
     @Test
     public void shouldParseDynamicOperandFromStringWithOnlyPropertyNameIfSourceIsSelector() {
         Source source = new NamedSelector(selectorName("tableA"));
-        DynamicOperand operand = parser.parseDynamicOperand(tokens("property"), context, source);
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("property"), typeSystem, source);
         assertThat(operand, is(instanceOf(PropertyValue.class)));
         PropertyValue value = (PropertyValue)operand;
-        assertThat(value.getPropertyName(), is(name("property")));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToDynamicOperandValueFromStringWithOnlyPropertyNameIfSourceIsNotSelector() {
-        parser.parsePropertyValue(tokens("property"), context, mock(Join.class));
+        parser.parsePropertyValue(tokens("property"), typeSystem, mock(Join.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseDynamicOperandFromStringWithOnlySelectorNameAndPeriod() {
-        parser.parsePropertyValue(tokens("tableA. "), context, mock(Join.class));
+        parser.parsePropertyValue(tokens("tableA. "), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1647,41 +1669,41 @@
 
     @Test
     public void shouldParsePropertyValueFromStringWithUnquotedSelectorNameAndUnquotedPropertyName() {
-        PropertyValue value = parser.parsePropertyValue(tokens("tableA.property"), context, mock(Join.class));
-        assertThat(value.getPropertyName(), is(name("property")));
+        PropertyValue value = parser.parsePropertyValue(tokens("tableA.property"), typeSystem, mock(Join.class));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test
     public void shouldParsePropertyValueFromStringWithQuotedSelectorNameAndUnquotedPropertyName() {
-        PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].property"), context, mock(Join.class));
-        assertThat(value.getPropertyName(), is(name("property")));
+        PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].property"), typeSystem, mock(Join.class));
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
     }
 
     @Test
     public void shouldParsePropertyValueFromStringWithQuotedSelectorNameAndQuotedPropertyName() {
-        PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].[dna:property]"), context, mock(Join.class));
-        assertThat(value.getPropertyName(), is(name("dna:property")));
+        PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].[dna:property]"), typeSystem, mock(Join.class));
+        assertThat(value.getPropertyName(), is("dna:property"));
         assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
     }
 
     @Test
     public void shouldParsePropertyValueFromStringWithOnlyPropertyNameIfSourceIsSelector() {
         Source source = new NamedSelector(selectorName("tableA"));
-        PropertyValue value = parser.parsePropertyValue(tokens("property"), context, source);
-        assertThat(value.getPropertyName(), is(name("property")));
+        PropertyValue value = parser.parsePropertyValue(tokens("property"), typeSystem, source);
+        assertThat(value.getPropertyName(), is("property"));
         assertThat(value.getSelectorName(), is(selectorName("tableA")));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParsePropertyValueFromStringWithOnlyPropertyNameIfSourceIsNotSelector() {
-        parser.parsePropertyValue(tokens("property"), context, mock(Join.class));
+        parser.parsePropertyValue(tokens("property"), typeSystem, mock(Join.class));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParsePropertyValueFromStringWithOnlySelectorNameAndPeriod() {
-        parser.parsePropertyValue(tokens("tableA. "), context, mock(Join.class));
+        parser.parsePropertyValue(tokens("tableA. "), typeSystem, mock(Join.class));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1809,48 +1831,48 @@
 
     @Test
     public void shouldParseNameFromSingleQuotedString() {
-        assertThat(parser.parseName(tokens("'jcr:name'"), context), is(name("jcr:name")));
+        assertThat(parser.parseName(tokens("'jcr:name'"), typeSystem), is("jcr:name"));
     }
 
     @Test
     public void shouldParseNameFromDoubleQuotedString() {
-        assertThat(parser.parseName(tokens("\"jcr:name\""), context), is(name("jcr:name")));
+        assertThat(parser.parseName(tokens("\"jcr:name\""), typeSystem), is("jcr:name"));
     }
 
     @Test
     public void shouldParseNameFromBracketedString() {
-        assertThat(parser.parseName(tokens("[jcr:name]"), context), is(name("jcr:name")));
+        assertThat(parser.parseName(tokens("[jcr:name]"), typeSystem), is("jcr:name"));
     }
 
     @Test
     public void shouldParseNameFromUnquotedStringWithoutPrefix() {
-        assertThat(parser.parseName(tokens("name"), context), is(name("name")));
+        assertThat(parser.parseName(tokens("name"), typeSystem), is("name"));
     }
 
     @Test
     public void shouldParseNameFromSingleQuotedStringWithoutPrefix() {
-        assertThat(parser.parseName(tokens("'name'"), context), is(name("name")));
+        assertThat(parser.parseName(tokens("'name'"), typeSystem), is("name"));
     }
 
     @Test
     public void shouldParseNameFromDoubleQuotedStringWithoutPrefix() {
-        assertThat(parser.parseName(tokens("\"name\""), context), is(name("name")));
+        assertThat(parser.parseName(tokens("\"name\""), typeSystem), is("name"));
     }
 
     @Test
     public void shouldParseNameFromBracketedStringWithoutPrefix() {
-        assertThat(parser.parseName(tokens("[name]"), context), is(name("name")));
+        assertThat(parser.parseName(tokens("[name]"), typeSystem), is("name"));
     }
 
     @Test
     public void shouldParseNameFromBracketedAndQuotedStringWithoutPrefix() {
-        assertThat(parser.parseName(tokens("['name']"), context), is(name("name")));
-        assertThat(parser.parseName(tokens("[\"name\"]"), context), is(name("name")));
+        assertThat(parser.parseName(tokens("['name']"), typeSystem), is("name"));
+        assertThat(parser.parseName(tokens("[\"name\"]"), typeSystem), is("name"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailToParseNameIfNoMoreTokens() {
-        parser.parseName(tokens("  "), context);
+        parser.parseName(tokens("  "), typeSystem);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1860,22 +1882,22 @@
     @Test
     public void shouldParsePathFromUnquotedStringConsistingOfSql92Identifiers() {
         String identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-        assertThat(parser.parsePath(tokens(identifier), context), is(path(identifier)));
+        assertThat(parser.parsePath(tokens(identifier), typeSystem), is(identifier));
     }
 
     @Test
     public void shouldParsePathFromSingleQuotedString() {
-        assertThat(parser.parsePath(tokens("'/a/b/c/dna:something/d'"), context), is(path("/a/b/c/dna:something/d")));
+        assertThat(parser.parsePath(tokens("'/a/b/c/dna:something/d'"), typeSystem), is("/a/b/c/dna:something/d"));
     }
 
     @Test
     public void shouldParsePathFromDoubleQuotedString() {
-        assertThat(parser.parsePath(tokens("\"/a/b/c/dna:something/d\""), context), is(path("/a/b/c/dna:something/d")));
+        assertThat(parser.parsePath(tokens("\"/a/b/c/dna:something/d\""), typeSystem), is("/a/b/c/dna:something/d"));
     }
 
     @Test( expected = ParsingException.class )
     public void shouldFailInParsePathIfNoMoreTokens() {
-        parser.parsePath(tokens("  "), context);
+        parser.parsePath(tokens("  "), typeSystem);
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -1899,7 +1921,7 @@
     // ----------------------------------------------------------------------------------------------------------------
 
     protected void parse( String query ) {
-        parser.parseQuery(query, context);
+        parser.parseQuery(query, typeSystem);
     }
 
     protected SelectorName selectorName( String name ) {
@@ -1907,11 +1929,11 @@
     }
 
     protected Name name( String name ) {
-        return context.getValueFactories().getNameFactory().create(name);
+        return (Name)typeSystem.getTypeFactory(PropertyType.NAME.getName()).create(name);
     }
 
     protected Path path( String path ) {
-        return context.getValueFactories().getPathFactory().create(path);
+        return (Path)typeSystem.getTypeFactory(PropertyType.PATH.getName()).create(path);
     }
 
     protected TokenStream tokens( String content ) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -35,6 +35,7 @@
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.ImmutableSchemata;
@@ -48,7 +49,7 @@
 public class CanonicalPlannerTest {
 
     private CanonicalPlanner planner;
-    private ExecutionContext context;
+    private TypeSystem typeSystem;
     private QueryBuilder builder;
     private PlanHints hints;
     private QueryCommand query;
@@ -61,11 +62,11 @@
     @Before
     public void beforeEach() {
         planner = new CanonicalPlanner();
-        context = new ExecutionContext();
+        typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
         hints = new PlanHints();
-        builder = new QueryBuilder(context);
+        builder = new QueryBuilder(typeSystem);
         problems = new SimpleProblems();
-        schemataBuilder = ImmutableSchemata.createBuilder(context);
+        schemataBuilder = ImmutableSchemata.createBuilder(typeSystem);
     }
 
     protected SelectorName selector( String name ) {
@@ -112,7 +113,7 @@
     public void shouldProducePlanForSelectStarFromTable() {
         schemata = schemataBuilder.addTable("__ALLNODES__", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.isEmpty(), is(true));
         assertProjectNode(plan, "column1", "column2", "column3");
@@ -128,7 +129,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -137,7 +138,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantColumnOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.select("column1", "column4").from("someTable").query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -146,7 +147,7 @@
     public void shouldProducePlanWhenSelectingAllColumnsOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().from("someTable").query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         System.out.println(plan);
         assertThat(problems.hasErrors(), is(false));
@@ -164,12 +165,12 @@
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         // Make sure the query without the search criteria does not have an error
         query = builder.select("column1", "column2").from("someTable").query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(false));
 
         query = builder.select("column1", "column2").from("someTable").where().search("someTable", "term1").end().query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -180,7 +181,7 @@
                                   .makeSearchable("someTable", "column1")
                                   .build();
         query = builder.select("column1", "column4").from("someTable").where().search("someTable", "term1").end().query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -190,7 +191,7 @@
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         // Make sure the query without the search criteria does not have an error
         query = builder.select("column1", "column2").from("someTable").query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(false));
 
@@ -200,7 +201,7 @@
                        .search("someTable", "column2", "term1")
                        .end()
                        .query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -216,7 +217,7 @@
                        .search("someTable", "column1", "term1")
                        .end()
                        .query();
-        queryContext = new QueryContext(context, schemata, hints, problems);
+        queryContext = new QueryContext(schemata, typeSystem, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -29,7 +29,6 @@
 import java.util.List;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.AbstractQueryTest;
@@ -45,6 +44,7 @@
 import org.jboss.dna.graph.query.model.Ordering;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.validate.ImmutableSchemata;
 import org.jboss.dna.graph.query.validate.Schemata;
 
@@ -54,11 +54,8 @@
 public abstract class AbstractQueryResultsTest extends AbstractQueryTest {
 
     protected ExecutionContext executionContext = ExecutionContext.DEFAULT_CONTEXT;
+    protected TypeSystem typeSystem = executionContext.getValueFactories().getTypeSystem();
 
-    protected Name name( String name ) {
-        return executionContext.getValueFactories().getNameFactory().create(name);
-    }
-
     protected Path path( String name ) {
         return executionContext.getValueFactories().getPathFactory().create(name);
     }
@@ -69,7 +66,7 @@
 
     protected Schemata schemataFor( Columns columns,
                                     PropertyType... types ) {
-        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(executionContext);
+        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(typeSystem);
         for (String selectorName : columns.getSelectorNames()) {
             final SelectorName selector = selector(selectorName);
             int i = 0;
@@ -77,7 +74,7 @@
                 final String name = column.getColumnName();
                 final PropertyType type = types != null && types.length > i && types[i] != null ? types[i] : PropertyType.STRING;
                 if (column.getSelectorName().equals(selector)) {
-                    builder.addColumn(selectorName, name, type);
+                    builder.addColumn(selectorName, name, type.getName().toUpperCase());
                     ++i;
                 }
             }
@@ -91,7 +88,7 @@
         List<Column> columnObj = new ArrayList<Column>();
         SelectorName selector = selector(selectorName);
         for (String columnName : columnNames) {
-            columnObj.add(new Column(selector, name(columnName), columnName));
+            columnObj.add(new Column(selector, columnName, columnName));
         }
         return new QueryResultColumns(columnObj, false);
     }
@@ -102,7 +99,7 @@
         List<Column> columnObj = new ArrayList<Column>();
         SelectorName selector = selector(selectorName);
         for (String columnName : columnNames) {
-            columnObj.add(new Column(selector, name(columnName), columnName));
+            columnObj.add(new Column(selector, columnName, columnName));
         }
         return new QueryResultColumns(columnObj, true);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.mock;
 import java.util.ArrayList;
 import java.util.List;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -47,7 +46,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), typeSystem);
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -63,14 +63,14 @@
     @Before
     public void beforeEach() {
         MockitoAnnotations.initMocks(this);
-        context = new QueryContext(executionContext, schemata);
+        context = new QueryContext(schemata, typeSystem);
         columnList = new ArrayList<Column>();
-        columnList.add(new Column(selector("table1"), name("colA"), "colA"));
-        columnList.add(new Column(selector("table1"), name("colB"), "colB"));
-        columnList.add(new Column(selector("table1"), name("colC"), "colC"));
-        columnList.add(new Column(selector("table2"), name("colA"), "colA2"));
-        columnList.add(new Column(selector("table2"), name("colB"), "colB2"));
-        columnList.add(new Column(selector("table2"), name("colX"), "colX"));
+        columnList.add(new Column(selector("table1"), "colA", "colA"));
+        columnList.add(new Column(selector("table1"), "colB", "colB"));
+        columnList.add(new Column(selector("table1"), "colC", "colC"));
+        columnList.add(new Column(selector("table2"), "colA", "colA2"));
+        columnList.add(new Column(selector("table2"), "colB", "colB2"));
+        columnList.add(new Column(selector("table2"), "colX", "colX"));
         columns = new QueryResultColumns(columnList, false);
         tuples = new ArrayList<Object[]>();
         tuples.add(tuple(columns, new String[] {"/a/b/c", "/a/x/y"}, 1, 2, 3, "2a", "2b", "x"));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,7 +30,6 @@
 import java.util.NoSuchElementException;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Column;
-import org.jboss.dna.graph.query.process.QueryResultColumns;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -46,12 +45,12 @@
     @Before
     public void beforeEach() {
         columnList = new ArrayList<Column>();
-        columnList.add(new Column(selector("table1"), name("colA"), "colA"));
-        columnList.add(new Column(selector("table1"), name("colB"), "colB"));
-        columnList.add(new Column(selector("table1"), name("colC"), "colC"));
-        columnList.add(new Column(selector("table2"), name("colA"), "colA2"));
-        columnList.add(new Column(selector("table2"), name("colB"), "colB2"));
-        columnList.add(new Column(selector("table2"), name("colX"), "colX"));
+        columnList.add(new Column(selector("table1"), "colA", "colA"));
+        columnList.add(new Column(selector("table1"), "colB", "colB"));
+        columnList.add(new Column(selector("table1"), "colC", "colC"));
+        columnList.add(new Column(selector("table2"), "colA", "colA2"));
+        columnList.add(new Column(selector("table2"), "colB", "colB2"));
+        columnList.add(new Column(selector("table2"), "colX", "colX"));
         columnsWithoutScores = new QueryResultColumns(columnList, false);
         columnsWithScores = new QueryResultColumns(columnList, true);
     }
@@ -148,22 +147,22 @@
 
     @Test( expected = NoSuchElementException.class )
     public void shouldFailToFindIndexOfColumnGivenUnusedSelectorName() {
-        columnsWithScores.getColumnIndexForProperty("non-existant", name("colA"));
+        columnsWithScores.getColumnIndexForProperty("non-existant", "colA");
     }
 
     @Test( expected = NoSuchElementException.class )
     public void shouldFailToFindIndexOfColumnGivenNullSelectorName() {
-        columnsWithScores.getColumnIndexForProperty(null, name("colA"));
+        columnsWithScores.getColumnIndexForProperty(null, "colA");
     }
 
     @Test( expected = NoSuchElementException.class )
     public void shouldFailToFindIndexOfColumnGivenEmptySelectorName() {
-        columnsWithScores.getColumnIndexForProperty("", name("colA"));
+        columnsWithScores.getColumnIndexForProperty("", "colA");
     }
 
     @Test( expected = NoSuchElementException.class )
     public void shouldFailToFindIndexOfColumnGivenUnusedPropertyNameName() {
-        columnsWithScores.getColumnIndexForProperty("table1", name("non-existant"));
+        columnsWithScores.getColumnIndexForProperty("table1", "non-existant");
     }
 
     @Test( expected = NoSuchElementException.class )
@@ -344,7 +343,7 @@
         for (Column column : columnsWithScores.getColumns()) {
             columnListCopy.add(new Column(column.getSelectorName(), column.getPropertyName(), column.getColumnName()));
         }
-        columnListCopy.add(new Column(selector("table2"), name("colZ"), "colZ"));
+        columnListCopy.add(new Column(selector("table2"), "colZ", "colZ"));
         Columns other = new QueryResultColumns(columnListCopy, columnsWithScores.hasFullTextSearchScores());
         assertThat(columnsWithScores.isUnionCompatible(other), is(false));
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.mock;
 import java.util.ArrayList;
 import java.util.List;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -47,7 +46,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
+        context = new QueryContext(mock(Schemata.class), typeSystem);
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -27,7 +27,6 @@
 import static org.junit.Assert.assertThat;
 import java.util.ArrayList;
 import java.util.List;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
@@ -55,7 +54,7 @@
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
         schemata = schemataFor(columns, PropertyType.STRING, PropertyType.LONG, PropertyType.STRING);
         // Define the context ...
-        context = new QueryContext(new ExecutionContext(), schemata);
+        context = new QueryContext(schemata, typeSystem);
         inputTuples = new ArrayList<Object[]>();
         // And define the delegating component ...
         delegate = new ProcessingComponent(context, columns) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -41,6 +41,8 @@
  */
 public class ImmutableSchemataBuilderTest {
 
+    private static final String STRING_TYPE = PropertyType.STRING.getName().toUpperCase();
+
     private ExecutionContext context;
     private ImmutableSchemata.Builder builder;
     private Schemata schemata;
@@ -48,7 +50,7 @@
     @Before
     public void beforeEach() {
         context = new ExecutionContext();
-        builder = ImmutableSchemata.createBuilder(context);
+        builder = ImmutableSchemata.createBuilder(context.getValueFactories().getTypeSystem());
     }
 
     @After
@@ -64,9 +66,9 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c2").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c3").getPropertyType(), is(STRING_TYPE));
     }
 
     @Test
@@ -76,14 +78,14 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c11").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c13").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("t2"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c21").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c22").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c23").getPropertyType(), is(STRING_TYPE));
     }
 
     @Test
@@ -93,14 +95,14 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c2").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c3").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("t2"));
         assertThat(table, is(instanceOf(View.class)));
-        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c2").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c3").getPropertyType(), is(STRING_TYPE));
     }
 
     @Test
@@ -110,13 +112,13 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c2").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c3").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("t2"));
         assertThat(table, is(instanceOf(View.class)));
-        assertThat(table.getColumn("v1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("v1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c2").getPropertyType(), is(STRING_TYPE));
     }
 
     @Test
@@ -127,20 +129,20 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c11").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c13").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("t2"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c21").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c22").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c23").getPropertyType(), is(STRING_TYPE));
         schemata = builder.build();
         table = schemata.getTable(selector("v1"));
         assertThat(table, is(instanceOf(View.class)));
-        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("x1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c23").getPropertyType(), is(STRING_TYPE));
     }
 
     @Test
@@ -152,24 +154,24 @@
         schemata = builder.build();
         Table table = schemata.getTable(selector("t1"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c11").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c13").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("t2"));
         assertThat(table, is(notNullValue()));
-        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c21").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c22").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c23").getPropertyType(), is(STRING_TYPE));
         schemata = builder.build();
         table = schemata.getTable(selector("v1"));
         assertThat(table, is(instanceOf(View.class)));
-        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("x1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c23").getPropertyType(), is(STRING_TYPE));
         table = schemata.getTable(selector("v2"));
         assertThat(table, is(instanceOf(View.class)));
-        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
-        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("x1").getPropertyType(), is(STRING_TYPE));
+        assertThat(table.getColumn("c12").getPropertyType(), is(STRING_TYPE));
     }
 
     protected SelectorName selector( String name ) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/search/SearchEngineTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -248,7 +248,7 @@
         Query query = mock(Query.class);
         Schemata schemata = mock(Schemata.class);
         engine.query(context, workspaceName1, query, schemata);
-        verify(sessionWs1).query(eq(new QueryContext(context, schemata)), eq(query));
+        verify(sessionWs1).query(eq(new QueryContext(schemata, context.getValueFactories().getTypeSystem())), eq(query));
         verify(sessionWs1).commit();
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -1007,11 +1007,10 @@
         throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
         RepositoryException {
 
-
         if (isLocked() && !holdsLock()) {
             throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
         }
-        
+
         // Determine the path ...
         NodeEditor editor = null;
         Path path = null;
@@ -1336,7 +1335,7 @@
      * @return <code>false</code>
      * @see javax.jcr.Node#holdsLock()
      */
-    public final boolean holdsLock() throws RepositoryException {
+    public final boolean holdsLock() /*throws RepositoryException*/{
         WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(this.location);
 
         return lock != null && cache.session().lockTokens().contains(lock.getLockToken());
@@ -1351,7 +1350,7 @@
     public final boolean isLocked() throws LockException, RepositoryException {
         return lock() != null;
     }
-    
+
     /**
      * {@inheritDoc}
      * 
@@ -1440,7 +1439,7 @@
         if (lock == null) throw new LockException(JcrI18n.notLocked.text(this.location));
         return lock.lockFor(cache);
     }
-    
+
     /**
      * {@inheritDoc}
      * 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -34,11 +34,10 @@
 import org.jboss.dna.common.text.TokenStream.Tokenizer;
 import org.jboss.dna.common.text.TokenStream.Tokens;
 import org.jboss.dna.common.xml.XmlCharacters;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.jcr.xpath.XPath.Add;
 import org.jboss.dna.jcr.xpath.XPath.And;
 import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
@@ -79,10 +78,10 @@
  * defined by JCR 1.0, and is a subset of what is allowed by the W3C XPath 2.0 specification.
  */
 public class XPathParser {
-    private final ExecutionContext context;
+    private final TypeSystem typeSystem;
 
-    public XPathParser( ExecutionContext context ) {
-        this.context = context;
+    public XPathParser( TypeSystem context ) {
+        this.typeSystem = context;
     }
 
     public Component parseXPath( String xpath ) {
@@ -417,7 +416,6 @@
         else if (tokens.canConsume('+')) sign = "";
 
         // Try to parse this value as a number ...
-        ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
         String number = tokens.consume();
         if (number.indexOf(".") != -1) {
             String value = sign + number;
@@ -427,7 +425,7 @@
             }
             try {
                 // Convert to a double and then back to a string to get canonical form ...
-                String canonical = stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
+                String canonical = typeSystem.getDoubleFactory().asString(value);
                 return new Literal(canonical);
             } catch (ValueFormatException e) {
                 String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
@@ -438,7 +436,7 @@
         String value = sign + number;
         try {
             // Convert to a long and then back to a string to get canonical form ...
-            String canonical = stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+            String canonical = typeSystem.getLongFactory().asString(value);
             return new Literal(canonical);
         } catch (ValueFormatException e) {
             String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -24,8 +24,8 @@
 package org.jboss.dna.jcr.xpath;
 
 import org.jboss.dna.common.text.ParsingException;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.parse.InvalidQueryException;
 import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.jcr.xpath.XPath.Component;
@@ -76,15 +76,15 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
+     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.query.model.TypeSystem)
      */
     public QueryCommand parseQuery( String query,
-                                    ExecutionContext context ) throws InvalidQueryException, ParsingException {
-        Component xpath = new XPathParser(context).parseXPath(query);
+                                    TypeSystem typeSystem ) throws InvalidQueryException, ParsingException {
+        Component xpath = new XPathParser(typeSystem).parseXPath(query);
         System.out.println(query);
         System.out.println(" --> " + xpath);
         // Convert the result into a QueryCommand ...
-        QueryCommand command = new XPathToQueryTranslator(context, query).createQuery(xpath);
+        QueryCommand command = new XPathToQueryTranslator(typeSystem, query).createQuery(xpath);
         return command;
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -30,12 +30,12 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.query.QueryBuilder;
 import org.jboss.dna.graph.query.QueryBuilder.ConstraintBuilder;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.parse.InvalidQueryException;
 import org.jboss.dna.jcr.xpath.XPath.And;
 import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
@@ -65,33 +65,33 @@
  */
 public class XPathToQueryTranslator {
 
-    protected static final Map<NameTest, PropertyType> CAST_FUNCTION_NAME_TO_TYPE;
+    protected static final Map<NameTest, String> CAST_FUNCTION_NAME_TO_TYPE;
 
     static {
-        Map<NameTest, PropertyType> map = new HashMap<NameTest, PropertyType>();
-        map.put(new NameTest("fn", "string"), PropertyType.STRING);
-        map.put(new NameTest("xs", "string"), PropertyType.STRING);
-        map.put(new NameTest("xs", "base64Binary"), PropertyType.BINARY);
-        map.put(new NameTest("xs", "double"), PropertyType.DOUBLE);
-        map.put(new NameTest("xs", "long"), PropertyType.LONG);
-        map.put(new NameTest("xs", "boolean"), PropertyType.BOOLEAN);
-        map.put(new NameTest("xs", "dateTime"), PropertyType.DATE);
-        map.put(new NameTest("xs", "string"), PropertyType.PATH);
-        map.put(new NameTest("xs", "string"), PropertyType.NAME);
-        map.put(new NameTest("xs", "IDREF"), PropertyType.REFERENCE);
+        Map<NameTest, String> map = new HashMap<NameTest, String>();
+        map.put(new NameTest("fn", "string"), PropertyType.STRING.getName().toUpperCase());
+        map.put(new NameTest("xs", "string"), PropertyType.STRING.getName().toUpperCase());
+        map.put(new NameTest("xs", "base64Binary"), PropertyType.BINARY.getName().toUpperCase());
+        map.put(new NameTest("xs", "double"), PropertyType.DOUBLE.getName().toUpperCase());
+        map.put(new NameTest("xs", "long"), PropertyType.LONG.getName().toUpperCase());
+        map.put(new NameTest("xs", "boolean"), PropertyType.BOOLEAN.getName().toUpperCase());
+        map.put(new NameTest("xs", "dateTime"), PropertyType.DATE.getName().toUpperCase());
+        map.put(new NameTest("xs", "string"), PropertyType.PATH.getName().toUpperCase());
+        map.put(new NameTest("xs", "string"), PropertyType.NAME.getName().toUpperCase());
+        map.put(new NameTest("xs", "IDREF"), PropertyType.REFERENCE.getName().toUpperCase());
         CAST_FUNCTION_NAME_TO_TYPE = Collections.unmodifiableMap(map);
     }
 
     private final String query;
-    private final ExecutionContext context;
+    private final TypeSystem typeSystem;
     private final QueryBuilder builder;
     private final Set<String> aliases = new HashSet<String>();
 
-    public XPathToQueryTranslator( ExecutionContext context,
+    public XPathToQueryTranslator( TypeSystem context,
                                    String query ) {
         this.query = query;
-        this.context = context;
-        this.builder = new QueryBuilder(this.context);
+        this.typeSystem = context;
+        this.builder = new QueryBuilder(this.typeSystem);
     }
 
     public QueryCommand createQuery( Component xpath ) {
@@ -429,7 +429,7 @@
                     NameTest functionName = call.getName();
                     List<Component> parameters = call.getParameters();
                     // Is this a cast ...
-                    PropertyType castType = CAST_FUNCTION_NAME_TO_TYPE.get(functionName);
+                    String castType = CAST_FUNCTION_NAME_TO_TYPE.get(functionName);
                     if (castType != null) {
                         if (parameters.size() == 1 && parameters.get(0).collapse() instanceof Literal) {
                             // The first parameter can be the type name (or table name) ...

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -58,7 +58,6 @@
 import javax.security.auth.login.LoginContext;
 import org.jboss.dna.graph.JaasSecurityContext;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -159,9 +158,6 @@
         sessionAttributes = new HashMap<String, Object>();
 
         // Now create the workspace ...
-        PathFactory pathFactory = context.getValueFactories().getPathFactory();
-        Path locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
-        WorkspaceLockManager wlm = new WorkspaceLockManager(context, repository, workspaceName, locksPath);
         workspace = new JcrWorkspace(repository, workspaceName, context, sessionAttributes);
 
         // Create the session and log in ...

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -8,7 +8,6 @@
 import java.util.LinkedList;
 import java.util.UUID;
 import javax.jcr.RepositoryException;
-import javax.security.auth.login.LoginException;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
@@ -33,7 +32,7 @@
 
 public class WorkspaceLockManagerTest {
 
-    private Graph graph;
+    protected Graph graph;
     private ExecutionContext context;
     private UUID validUuid;
     private Location validLocation;
@@ -43,7 +42,7 @@
     private LinkedList<Request> executedRequests;
 
     private RepositoryNodeTypeManager repoTypeManager;
-    private WorkspaceLockManager workspaceLockManager;
+    protected WorkspaceLockManager workspaceLockManager;
 
     @Mock
     private RepositoryConnectionFactory connectionFactory;
@@ -51,7 +50,7 @@
     protected JcrRepository repository;
 
     @Before
-    public void beforeEach() throws LoginException {
+    public void beforeEach() {
         MockitoAnnotations.initMocks(this);
         executedRequests = new LinkedList<Request>();
         sourceName = "Source";

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -36,6 +36,7 @@
 import org.jboss.dna.common.text.TokenStream.Tokenizer;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.jcr.xpath.XPath.And;
 import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
 import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
@@ -70,13 +71,13 @@
  */
 public class XPathParserTest {
 
-    private ExecutionContext context;
+    private TypeSystem typeSystem;
     private XPathParser parser;
 
     @Before
     public void beforeEach() {
-        context = new ExecutionContext();
-        parser = new XPathParser(context);
+        typeSystem = new ExecutionContext().getValueFactories().getTypeSystem();
+        parser = new XPathParser(typeSystem);
     }
 
     @After
@@ -961,7 +962,7 @@
     }
 
     protected void assertParsable( String xpath ) {
-        new XPathQueryParser().parseQuery(xpath, context);
+        new XPathQueryParser().parseQuery(xpath, typeSystem);
     }
 
     protected void assertParsable( String xpath,
@@ -976,7 +977,7 @@
 
     protected void assertNotParsable( String xpath ) {
         try {
-            new XPathQueryParser().parseQuery(xpath, context);
+            new XPathQueryParser().parseQuery(xpath, typeSystem);
             fail("Expected an invalid XPath:  " + xpath);
         } catch (ParsingException e) {
             // expected

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -28,6 +28,7 @@
 import org.hamcrest.Matcher;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.jcr.xpath.XPath.Component;
 import org.junit.After;
@@ -40,13 +41,15 @@
 public class XPathToQueryTranslatorTest {
 
     private ExecutionContext context;
+    private TypeSystem typeSystem;
     private XPathParser parser;
 
     @Before
     public void beforeEach() {
         context = new ExecutionContext();
         context.getNamespaceRegistry().register("x", "http://example.com");
-        parser = new XPathParser(context);
+        typeSystem = context.getValueFactories().getTypeSystem();
+        parser = new XPathParser(typeSystem);
     }
 
     @After
@@ -276,18 +279,18 @@
 
     private QueryCommand translateToSql( String xpath ) {
         Component component = parser.parseXPath(xpath);
-        XPathToQueryTranslator translator = new XPathToQueryTranslator(context, xpath);
+        XPathToQueryTranslator translator = new XPathToQueryTranslator(typeSystem, xpath);
         return translator.createQuery(component);
     }
 
     private QueryCommand xpath( String xpath ) {
         Component component = parser.parseXPath(xpath);
-        XPathToQueryTranslator translator = new XPathToQueryTranslator(context, xpath);
+        XPathToQueryTranslator translator = new XPathToQueryTranslator(typeSystem, xpath);
         return translator.createQuery(component);
     }
 
     protected QueryCommand sql( String sql ) {
-        return new SqlQueryParser().parseQuery(sql, context);
+        return new SqlQueryParser().parseQuery(sql, typeSystem);
     }
 
     protected Matcher<QueryCommand> isSql( String sql ) {

Modified: trunk/dna-jcr-tck/pom.xml
===================================================================
--- trunk/dna-jcr-tck/pom.xml	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-jcr-tck/pom.xml	2009-11-24 17:46:37 UTC (rev 1340)
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.jboss.dna</groupId>
     <artifactId>dna</artifactId>
-    <version>0.6-SNAPSHOT</version>
+    <version>0.7-SNAPSHOT</version>
   </parent>
   <!-- The groupId and version values are inherited from parent -->
   <artifactId>dna-jcr-tck</artifactId>

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -34,11 +34,9 @@
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.And;
@@ -68,9 +66,11 @@
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.SetCriteria;
 import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.model.FullTextSearch.NegationTerm;
+import org.jboss.dna.graph.query.model.TypeSystem.TypeFactory;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.process.AbstractAccessComponent;
 import org.jboss.dna.graph.query.process.ProcessingComponent;
@@ -106,8 +106,9 @@
         this.workspaceName = workspaceName;
     }
 
-    protected String fieldNameFor( Name name ) {
-        return session.stringFactory.create(name);
+    protected String fieldNameFor( String name ) {
+        // Convert to a name and then to a string, so that the namespaces are resolved
+        return session.stringFactory.create(session.nameFactory.create(name));
     }
 
     /**
@@ -175,14 +176,14 @@
             // There was a error working with the constraints (such as a ValueFormatException) ...
             QueryContext context = getContext();
             I18n msg = SearchI18n.errorWhilePerformingQuery;
-            String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+            String origQueryString = Visitors.readable(originalQuery);
             context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
             return emptyTuples();
         } catch (RuntimeException e) {
             // There was a error working with the constraints (such as a ValueFormatException) ...
             QueryContext context = getContext();
             I18n msg = SearchI18n.errorWhilePerformingQuery;
-            String origQueryString = Visitors.readable(originalQuery, context.getExecutionContext());
+            String origQueryString = Visitors.readable(originalQuery);
             context.getProblems().addError(e, msg, origQueryString, workspaceName, sourceName, e.getMessage());
             return emptyTuples();
         }
@@ -196,7 +197,6 @@
         List<Object[]> tuples = null;
         final Columns columns = getColumns();
         final QueryContext context = getContext();
-        final ExecutionContext execContext = context.getExecutionContext();
         try {
             // Execute the query against the content indexes ...
             IndexSearcher searcher = session.getContentSearcher();
@@ -206,7 +206,7 @@
         } catch (IOException e) {
             // There was a problem executing the Lucene query ...
             I18n msg = SearchI18n.errorWhilePerformingLuceneQuery;
-            String origQueryString = Visitors.readable(originalQuery, execContext);
+            String origQueryString = Visitors.readable(originalQuery);
             context.getProblems().addError(e, msg, pushDownQuery, origQueryString, workspaceName, sourceName, e.getMessage());
             return emptyTuples();
         }
@@ -287,7 +287,7 @@
         if (constraint instanceof FullTextSearch) {
             FullTextSearch search = (FullTextSearch)constraint;
             String fieldName = ContentIndex.FULL_TEXT;
-            Name propertyName = search.getPropertyName();
+            String propertyName = search.getPropertyName();
             if (propertyName != null) {
                 fieldName = session.fullTextFieldName(fieldNameFor(propertyName));
             }
@@ -296,15 +296,18 @@
         try {
             if (constraint instanceof SameNode) {
                 SameNode sameNode = (SameNode)constraint;
-                return session.findNodeAt(sameNode.getPath());
+                Path path = session.pathFactory.create(sameNode.getPath());
+                return session.findNodeAt(path);
             }
             if (constraint instanceof ChildNode) {
                 ChildNode childNode = (ChildNode)constraint;
-                return session.findChildNodes(childNode.getParentPath());
+                Path path = session.pathFactory.create(childNode.getParentPath());
+                return session.findChildNodes(path);
             }
             if (constraint instanceof DescendantNode) {
                 DescendantNode descendantNode = (DescendantNode)constraint;
-                return session.findAllNodesBelow(descendantNode.getAncestorPath());
+                Path path = session.pathFactory.create(descendantNode.getAncestorPath());
+                return session.findAllNodesBelow(path);
             }
         } catch (IOException e) {
             I18n msg = SearchI18n.errorWhilePerformingQuery;
@@ -435,13 +438,14 @@
             return ((String)value).toLowerCase();
         }
         assert !(value instanceof Binary);
-        ValueFactory<String> stringFactory = getContext().getExecutionContext().getValueFactories().getStringFactory();
-        ValueFactory<?> valueFactory = getContext().getExecutionContext().getValueFactories().getValueFactory(value);
+        TypeSystem typeSystem = getContext().getTypeSystem();
+        TypeFactory<String> stringFactory = typeSystem.getStringFactory();
+        TypeFactory<?> valueFactory = typeSystem.getTypeFactory(value);
         return valueFactory.create(stringFactory.create(value).toLowerCase());
     }
 
     protected Query createQuery( SelectorName selectorName,
-                                 Name propertyName ) {
+                                 String propertyName ) {
         Term term = new Term(fieldNameFor(propertyName));
         return new TermQuery(term);
     }

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 17:44:29 UTC (rev 1339)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 17:46:37 UTC (rev 1340)
@@ -40,10 +40,10 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.query.QueryResults;
 import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.validate.ImmutableSchemata;
 import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder;
 import org.jboss.dna.graph.search.SearchEngine;
 import org.jboss.dna.graph.search.SearchProvider;
 import org.junit.Before;
@@ -56,6 +56,7 @@
     private SearchEngine engine;
     private SearchProvider provider;
     private ExecutionContext context;
+    private TypeSystem typeSystem;
     private String sourceName;
     private String workspaceName1;
     private String workspaceName2;
@@ -68,6 +69,7 @@
     @Before
     public void beforeEach() throws Exception {
         context = new ExecutionContext();
+        typeSystem = context.getValueFactories().getTypeSystem();
         sourceName = "sourceA";
         workspaceName1 = "workspace1";
         workspaceName2 = "workspace2";
@@ -100,10 +102,7 @@
         loadContent();
 
         // Create the schemata for the workspaces ...
-        Builder builder = ImmutableSchemata.createBuilder(context);
-        builder.addTable("__ALLNODES__", "maker", "model", "year");
-        schemata = builder.build();
-        schemata = ImmutableSchemata.createBuilder(context)
+        schemata = ImmutableSchemata.createBuilder(typeSystem)
                                     .addTable("__ALLNODES__", "maker", "model", "year", "msrp")
                                     .makeSearchable("__ALLNODES__", "maker")
                                     .build();
@@ -227,7 +226,7 @@
     @Test
     public void shouldFindNodesBySimpleXpathQuery() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(18));
@@ -241,7 +240,7 @@
     @Test
     public void shouldFindNodesBySimpleQuery() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(18));
@@ -251,7 +250,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithEqualityComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker = 'Toyota'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker = 'Toyota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -262,7 +261,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithGreaterThanComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE mpgHighway > 20", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE mpgHighway > 20", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -272,7 +271,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithLowercaseEqualityComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) = 'toyota'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) = 'toyota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -282,7 +281,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithUppercaseEqualityComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE UPPER(maker) = 'TOYOTA'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE UPPER(maker) = 'TOYOTA'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -292,7 +291,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithLikeComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE 'Toyo%'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE 'Toyo%'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -302,7 +301,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithLikeComparisonCriteriaWithLeadingWildcard() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE '%yota'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE '%yota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -312,7 +311,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithLowercaseLikeComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) LIKE 'toyo%'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) LIKE 'toyo%'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
@@ -322,7 +321,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithFullTextSearchCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE CONTAINS(maker,'martin')", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE CONTAINS(maker,'martin')", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -333,7 +332,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithDepthCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE DEPTH() > 2", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE DEPTH() > 2", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -345,7 +344,7 @@
     public void shouldFindNodesBySimpleQueryWithLocalNameCriteria() {
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOCALNAME() LIKE 'Toyota%' OR LOCALNAME() LIKE 'Land %'",
-                                            context);
+                                            typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -357,7 +356,7 @@
     public void shouldFindNodesBySimpleQueryWithNameCriteria() {
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE NAME() LIKE 'Toyota%[1]' OR NAME() LIKE 'Land %'",
-                                            context);
+                                            typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -368,7 +367,7 @@
     @Test
     public void shouldFindNodesBySimpleQueryWithNameCriteriaThatMatchesNoNodes() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE NAME() LIKE 'Toyota%[2]'", context);
+        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE NAME() LIKE 'Toyota%[2]'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -380,7 +379,7 @@
     public void shouldFindNodesBySimpleQueryWithPathCriteria() {
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE PATH() LIKE '/Cars[%]/Hy%/Toyota%' OR PATH() LIKE '/Cars[1]/Utility[1]/%'",
-                                            context);
+                                            typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));
@@ -392,7 +391,7 @@
     public void shouldFindNodesBySimpleQueryWithDescendantCriteria() {
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE ISDESCENDANTNODE('/Cars/Hybrid')",
-                                            context);
+                                            typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
         assertNoErrors(results);
         assertThat(results, is(notNullValue()));


From dna-commits at lists.jboss.org  Tue Nov 24 16:17:40 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 16:17:40 -0500
Subject: [dna-commits] DNA SVN: r1341 - in trunk:
	dna-common/src/main/java/org/jboss/dna/common and 6 other
	directories.
Message-ID: <200911242117.nAOLHeFf003811@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-24 16:17:39 -0500 (Tue, 24 Nov 2009)
New Revision: 1341

Added:
   trunk/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
Modified:
   trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
   trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
   trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
Log:
DNA-555 added "index in content" ability to token stream and position class. Allows extracting and managing string segments from tokenized content.

Modified: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -82,7 +82,7 @@
                 case '(':
                 case ')':
                 case ',':
-                    tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                    tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                     break;
                 // case '.':
                 // tokens.addToken(input.position(), input.index(), input.index() + 1, DECIMAL);
@@ -90,7 +90,7 @@
                 case '{':
                     // Vendor extension, meant to be excluded
                     int startIndex = input.index();
-                    Position startingPosition = input.position();
+                    Position startingPosition = input.position(startIndex);
                     boolean foundClosingBrace = false;
                     String vendorName = "";
                     while (input.hasNext()) {
@@ -114,7 +114,7 @@
                     break;
                 case '\"':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     boolean foundClosingQuote = false;
                     while (input.hasNext()) {
                         c = input.next();
@@ -135,7 +135,7 @@
                     break;
                 case '\'':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     foundClosingQuote = false;
                     while (input.hasNext()) {
                         c = input.next();
@@ -156,7 +156,7 @@
                     break;
                 case '/':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     if (input.isNext('/')) {
                         // End-of-line comment ...
                         boolean foundLineTerminator = false;
@@ -211,7 +211,7 @@
                     // since these do not appear in other rules above, they will result in one-character tokens.
                     //
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     // Read as long as there is a valid XML character ...
                     while (input.hasNext() && !(input.isNextWhitespace() || input.isNextAnyOf("[]<>=-+(),\"'/{*|"))) {
                         c = input.next();

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	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -104,6 +104,7 @@
     public static I18n expectingValidIntegerAtLineAndColumn;
     public static I18n expectingValidLongAtLineAndColumn;
     public static I18n expectingValidBooleanAtLineAndColumn;
+    public static I18n endPositionMustBeGreaterThanStartingPosition;
 
     static {
         try {

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -7,14 +7,37 @@
  */
 @Immutable
 public final class Position {
+	/**
+	 * The position is used when there is no content.
+	 */
+	public final static Position EMPTY_CONTENT_POSITION = new Position(-1, 1, 0);
+	
     private final int line;
     private final int column;
+    private final int indexInContent;
 
-    public Position( int line,
+    public Position( int indexInContent, 
+    				 int line,
                      int column ) {
+    	this.indexInContent = indexInContent < 0 ? -1:indexInContent;
         this.line = line;
         this.column = column;
+        
+        assert this.indexInContent >= -1;
+        assert this.line > 0;
+        assert this.column >= 0;
+        // make sure that negative index means an EMPTY_CONTENT_POSITION
+        assert this.indexInContent < 0 ? this.line == 1 && this.column == 0: true;
     }
+    
+    /**
+     * Get the 0-based index of this position in the content character array.
+     * 
+     * @return the index; never negative except for the first position in an empty content.
+     */
+    public int getIndexInContent() {
+        return indexInContent;
+    }
 
     /**
      * Get the 1-based column number of the character.
@@ -41,7 +64,7 @@
      */
     @Override
     public int hashCode() {
-        return line;
+        return indexInContent;
     }
 
     /**
@@ -51,6 +74,28 @@
      */
     @Override
     public String toString() {
-        return "" + line + ':' + column;
+        return "" + indexInContent + ':' + line + ':' + column;
     }
+    
+    /**
+     * Return a new position that is the addition of this position and that supplied.
+     * 
+     * @param position the position to add to this object; may not be null
+     * @return the combined position
+     */
+    public Position add(Position position) {
+    	if( this.getIndexInContent() < 0 ) {
+    		return position.getIndexInContent() < 0 ? EMPTY_CONTENT_POSITION:position;
+    	}
+    	
+    	if( position.getIndexInContent() < 0 ) {
+    		return this;
+    	}
+
+        int index = this.getIndexInContent() + position.getIndexInContent();
+        int line = position.getLine() + this.getLine() - 1;
+        int column = this.getLine() == 1 ? this.getColumn() + position.getColumn() : this.getColumn();
+        
+        return new Position(index, line, column);
+    }
 }

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -384,6 +384,20 @@
     private final boolean caseSensitive;
     private final Tokenizer tokenizer;
     private List<Token> tokens;
+    /**
+     * This class navigates the Token objects using this iterator. However, because it very often needs to access the
+     * "current token" in the "consume(...)" and "canConsume(...)" and "matches(...)" methods, the class caches a "current token"
+     * and makes this iterator point to the 2nd token.
+     * 
+     * <pre>
+     *     T1     T2    T3    T4    T5
+     *         &circ;   &circ;  &circ;
+     *         |   |  |
+     *         |   |  +- The position of the tokenIterator, where tokenIterator.hasNext() will return T3
+     *         |   +---- The token referenced by currentToken
+     *         +-------- The logical position of the TokenStream object, where the &quot;consume()&quot; would return T2
+     * </pre>
+     */
     private ListIterator<Token> tokenIterator;
     private Token currentToken;
     private boolean completed;
@@ -412,7 +426,7 @@
             TokenFactory tokenFactory = caseSensitive ? new CaseSensitiveTokenFactory() : new CaseInsensitiveTokenFactory();
             CharacterStream characterStream = new CharacterArrayStream(inputContent);
             tokenizer.tokenize(characterStream, tokenFactory);
-            this.tokens = tokenFactory.getTokens();
+            this.tokens = initializeTokens(tokenFactory.getTokens());
         }
 
         // Create the iterator ...
@@ -422,6 +436,28 @@
     }
 
     /**
+     * Method to allow subclasses to preprocess the set of tokens and return the correct tokens to use. The default behavior is to
+     * simply return the supplied tokens.
+     * 
+     * @param tokens
+     * @return list of tokens.
+     */
+    protected List<Token> initializeTokens( List<Token> tokens ) {
+        return tokens;
+    }
+
+    /**
+     * Method to allow tokens to be re-used from the start without re-tokenizing content.
+     */
+    public void rewind() {
+        // recreate the iterator ...
+        tokenIterator = this.tokens.listIterator();
+        completed = false;
+        currentToken = null;
+        moveToNextToken();
+    }
+
+    /**
      * Get the position of the previous token.
      * 
      * @return the previous token's position; never null
@@ -437,7 +473,7 @@
      * 
      * @return the current token's position; never null
      * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
-     * @throws NoSuchElementException if there is no next token
+     * @throws NoSuchElementException if there is no previous token
      */
     public Position nextPosition() {
         return currentToken().position();
@@ -526,7 +562,7 @@
 
     protected void throwNoMoreContent() throws ParsingException {
         String msg = CommonI18n.noMoreContent.text();
-        Position pos = tokens.isEmpty() ? new Position(1, 0) : tokens.get(tokens.size() - 1).position();
+        Position pos = tokens.isEmpty() ? new Position(-1, 1, 0) : tokens.get(tokens.size() - 1).position();
         throw new ParsingException(pos, msg);
     }
 
@@ -725,9 +761,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -770,9 +809,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -811,9 +853,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -954,7 +999,7 @@
      * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
      */
     public boolean matches( int expectedType ) throws IllegalStateException {
-        return !completed && (expectedType == ANY_TYPE || currentToken().type() == expectedType);
+        return !completed && currentToken().matches(expectedType);
     }
 
     /**
@@ -1075,7 +1120,7 @@
             if (!iter.hasNext()) return false;
             token = iter.next();
             if (nextExpectedType == ANY_TYPE) continue;
-            if (token.type() != nextExpectedType) return false;
+            if (!token.matches(nextExpectedType)) return false;
         }
         return true;
     }
@@ -1233,6 +1278,30 @@
     }
 
     /**
+     * Gets the content string starting at the first position (inclusive) and continuing up to the end position (exclusive).
+     * 
+     * @param starting the position marking the beginning of the desired content string.
+     * @param end the position located directly after the returned content string; can be null, which means end of content
+     * @return the content string; never null
+     */
+    public String getContentBetween( Position starting,
+                                     Position end ) {
+        CheckArg.isNotNull(starting, "starting");
+
+        int startIndex = starting.getIndexInContent();
+        int endIndex = inputString.length();
+        if (end != null) {
+            endIndex = end.getIndexInContent();
+        }
+
+        if (startIndex >= endIndex) {
+            throw new IllegalArgumentException(CommonI18n.endPositionMustBeGreaterThanStartingPosition.text(startIndex, endIndex));
+        }
+
+        return inputString.substring(startIndex, endIndex);
+    }
+
+    /**
      * Get the previous token. This does not modify the state.
      * 
      * @return the previous token; never null
@@ -1249,11 +1318,10 @@
             }
             throw new IllegalStateException(CommonI18n.startMethodMustBeCalledBeforeConsumingOrMatching.text());
         }
-        if (!tokenIterator.hasPrevious()) {
+        if (tokenIterator.previousIndex() == 0) {
             throw new NoSuchElementException(CommonI18n.noMoreContent.text());
         }
-        ListIterator<Token> temp = tokens.listIterator(tokenIterator.previousIndex());
-        return temp.next();
+        return tokens.get(tokenIterator.previousIndex() - 1);
     }
 
     String generateFragment() {
@@ -1337,9 +1405,10 @@
         /**
          * Get the position for the last character returned from {@link #next()}.
          * 
+         * @param startIndex
          * @return the position of the last character returned; never null
          */
-        Position position();
+        Position position( int startIndex );
 
         /**
          * Determine if the next character on the sream is a {@link Character#isWhitespace(char) whitespace character}. This
@@ -1513,6 +1582,14 @@
         boolean matches( char expected );
 
         /**
+         * Determine if the token matches the supplied type.
+         * 
+         * @param expectedType the expected integer type
+         * @return true if the token's value matches the supplied integer type, or false otherwise
+         */
+        boolean matches( int expectedType );
+
+        /**
          * Get the type of the token.
          * 
          * @return the token's type
@@ -1546,6 +1623,14 @@
          * @return the position; never null
          */
         Position position();
+
+        /**
+         * Bitmask ORed with existing type value.
+         * 
+         * @param typeMask
+         * @return copy of Token with new type
+         */
+        Token withType( int typeMask );
     }
 
     /**
@@ -1571,6 +1656,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+         */
+        public Token withType( int typeMask ) {
+            int type = this.type | typeMask;
+            return new CaseSensitiveToken(startIndex, endIndex, type, position);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.common.text.TokenStream.Token#type()
          */
         public final int type() {
@@ -1625,6 +1720,15 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#matches(int)
+         */
+        public final boolean matches( int expectedType ) {
+            return expectedType == ANY_TYPE || (currentToken().type() & expectedType) == expectedType;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.common.text.TokenStream.Token#value()
          */
         public final String value() {
@@ -1673,6 +1777,17 @@
         protected String matchString() {
             return inputUppercased;
         }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+         */
+        @Override
+        public Token withType( int typeMask ) {
+            int type = this.type() | typeMask;
+            return new CaseInsensitiveToken(startIndex(), endIndex(), type, position());
+        }
     }
 
     protected abstract class TokenFactory implements Tokens {
@@ -1772,10 +1887,12 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#position()
+         * @param startIndex
+         * @return the position of the token. never null
+         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#position(int)
          */
-        public Position position() {
-            return new Position(lineNumber, columnNumber);
+        public Position position( int startIndex ) {
+            return new Position(startIndex, lineNumber, columnNumber);
         }
 
         /**
@@ -1792,10 +1909,10 @@
             if (result == '\r') {
                 nextCharMayBeLineFeed = true;
                 ++lineNumber;
-                columnNumber = 1;
+                columnNumber = 0;
             } else if (result == '\n') {
                 if (!nextCharMayBeLineFeed) ++lineNumber;
-                columnNumber = 1;
+                columnNumber = 0;
             } else if (nextCharMayBeLineFeed) {
                 nextCharMayBeLineFeed = false;
             }
@@ -1958,22 +2075,22 @@
         /**
          * The {@link Token#type() token type} for tokens that consist of an individual '.' character.
          */
-        public static final int DECIMAL = 3;
+        public static final int DECIMAL = 4;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters within single-quotes. Single quote
          * characters are included if they are preceded (escaped) by a '\' character.
          */
-        public static final int SINGLE_QUOTED_STRING = 4;
+        public static final int SINGLE_QUOTED_STRING = 8;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters within double-quotes. Double quote
          * characters are included if they are preceded (escaped) by a '\' character.
          */
-        public static final int DOUBLE_QUOTED_STRING = 5;
+        public static final int DOUBLE_QUOTED_STRING = 16;
         /**
          * The {@link Token#type() token type} for tokens that consist of all the characters between "/*" and "&#42;/" or between
          * "//" and the next line terminator (e.g., '\n', '\r' or "\r\n").
          */
-        public static final int COMMENT = 6;
+        public static final int COMMENT = 32;
 
         private final boolean useComments;
 
@@ -2017,14 +2134,14 @@
                     case '|':
                     case '=':
                     case ':':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                         break;
                     case '.':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, DECIMAL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, DECIMAL);
                         break;
                     case '\"':
                         int startIndex = input.index();
-                        Position startingPosition = input.position();
+                        Position startingPosition = input.position(startIndex);
                         boolean foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -2041,11 +2158,11 @@
                             throw new ParsingException(startingPosition, msg);
                         }
                         int endIndex = input.index() + 1; // beyond last character read
-                        tokens.addToken(input.position(), startIndex, endIndex, DOUBLE_QUOTED_STRING);
+                        tokens.addToken(startingPosition, startIndex, endIndex, DOUBLE_QUOTED_STRING);
                         break;
                     case '\'':
                         startIndex = input.index();
-                        startingPosition = input.position();
+                        startingPosition = input.position(startIndex);
                         foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -2062,10 +2179,11 @@
                             throw new ParsingException(startingPosition, msg);
                         }
                         endIndex = input.index() + 1; // beyond last character read
-                        tokens.addToken(input.position(), startIndex, endIndex, SINGLE_QUOTED_STRING);
+                        tokens.addToken(startingPosition, startIndex, endIndex, SINGLE_QUOTED_STRING);
                         break;
                     case '/':
                         startIndex = input.index();
+                        startingPosition = input.position(startIndex);
                         if (input.isNext('/')) {
                             // End-of-line comment ...
                             boolean foundLineTerminator = false;
@@ -2080,7 +2198,7 @@
                             if (!foundLineTerminator) ++endIndex; // must point beyond last char
                             if (c == '\r' && input.isNext('\n')) input.next();
                             if (useComments) {
-                                tokens.addToken(input.position(), startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
                         } else if (input.isNext('*')) {
                             // Multi-line comment ...
@@ -2091,21 +2209,22 @@
                             if (input.hasNext()) input.next(); // consume the '/'
                             if (useComments) {
                                 endIndex = input.index() + 1; // the token will include the '/' and '*' characters
-                                tokens.addToken(input.position(), startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
                         } else {
                             // just a regular slash ...
-                            tokens.addToken(input.position(), startIndex, startIndex + 1, SYMBOL);
+                            tokens.addToken(startingPosition, startIndex, startIndex + 1, SYMBOL);
                         }
                         break;
                     default:
                         startIndex = input.index();
+                        startingPosition = input.position(startIndex);
                         // Read until another whitespace/symbol/decimal/slash is found
                         while (input.hasNext() && !(input.isNextWhitespace() || input.isNextAnyOf("/.-(){}*,;+%?$[]!<>|=:"))) {
                             c = input.next();
                         }
                         endIndex = input.index() + 1; // beyond last character that was included
-                        tokens.addToken(input.position(), startIndex, endIndex, WORD);
+                        tokens.addToken(startingPosition, startIndex, endIndex, WORD);
                 }
             }
         }

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	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties	2009-11-24 21:17:39 UTC (rev 1341)
@@ -93,3 +93,4 @@
 expectingValidIntegerAtLineAndColumn = Expecting a valid integer value but found '{0}' at line {1}, column {2}
 expectingValidLongAtLineAndColumn = Expecting a valid long value but found '{0}' at line {1}, column {2}
 expectingValidBooleanAtLineAndColumn = Expecting a valid boolean value but found '{0}' at line {1}, column {2}
+endPositionMustBeGreaterThanStartingPosition = End position {1} must be greater than starting position {0}

Added: trunk/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java	                        (rev 0)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -0,0 +1,85 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.common.text;
+
+import static org.junit.Assert.assertThat;
+
+import static org.hamcrest.core.Is.is;
+
+import org.junit.Test;
+
+
+public class PositionTest {
+	
+	private int combinedIndex(int firstIndex, int secondIndex) {
+		Position first = new Position(firstIndex, 1, 0);
+		Position second = new Position(secondIndex, 1, 0);
+		
+		int firstPlusSecond = first.add(second).getIndexInContent();
+		int secondPlusFirst = second.add(first).getIndexInContent();
+		
+		assertThat(firstPlusSecond, is(secondPlusFirst));
+		
+		return firstPlusSecond;
+	}
+	
+	@Test
+	public void shouldAddNoContentPositionToValidPosition() {
+		// -1 to >=0
+		assertThat(combinedIndex(-1, 0), is(0));
+		assertThat(combinedIndex(-1, 1), is(1));
+		assertThat(combinedIndex(-1, 10), is(10));
+	}
+	
+	@Test
+	public void shouldAddValidPositionToNoContentPosition() {
+		// >= 0 to -1
+		assertThat(combinedIndex(0, -1), is(0));
+		assertThat(combinedIndex(1, -1), is(1));
+		assertThat(combinedIndex(10, -1), is(10));
+	}
+	
+	@Test
+	public void shouldAddValidPositionToValidPosition() {
+		// positive to positive
+		assertThat(combinedIndex(1, 1), is(2));
+		assertThat(combinedIndex(10, 1), is(11));
+		assertThat(combinedIndex(1, 10), is(11));
+		assertThat(combinedIndex(10, 10), is(20));
+	}
+	
+	@Test
+	public void shouldAddStartingPositionToStartingPosition() {
+		// 0 to 0
+		assertThat(combinedIndex(0, 0), is(0));
+	}
+	
+	@Test
+	public void shouldAddNoContentPositionToNoContentPosition() {
+		// -1 to -1
+		assertThat(combinedIndex(-1, -1), is(-1));
+		assertThat(combinedIndex(-10, -1), is(-1));
+		assertThat(combinedIndex(-1, -10), is(-1));
+	}
+}


Property changes on: trunk/dna-common/src/test/java/org/jboss/dna/common/text/PositionTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -336,6 +336,19 @@
         assertThat(tokens.canConsume("FROM", "THIS", "TABLE"), is(true));
         assertThat(tokens.hasNext(), is(false));
     }
+    
+    @Test
+    public void shouldReturnTrueFromMatchIfAllTypeValuesMatch() {
+        makeCaseInsensitive();
+        assertThat(tokens.matches(BasicTokenizer.WORD, BasicTokenizer.WORD), is(true));
+    }
+    
+    @Test
+    public void shouldReturnFalseFromMatchIfAllTypeValuesDoNotMatch() {
+        makeCaseInsensitive();
+        assertThat(tokens.matches(BasicTokenizer.WORD, BasicTokenizer.DECIMAL), is(false));
+        assertThat(tokens.matches(BasicTokenizer.DECIMAL, BasicTokenizer.WORD), is(false));
+    }
 
     @Test
     public void shouldConsumeMultipleTokensWithAnyValueConstant() {
@@ -397,4 +410,51 @@
         assertThat(tokens.canConsume(TokenStream.ANY_VALUE), is(false));
         assertThat(tokens.canConsume(BasicTokenizer.SYMBOL), is(false));
     }
+    
+    @Test
+    public void shouldFindNextPositionStartIndex() {
+    	makeCaseInsensitive();
+    	// "Select all columns from this table";
+    	tokens.consume();
+    	// Next position should be line 1, column 8
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(7));
+    	assertThat(tokens.nextPosition().getColumn(), is(8));
+    	assertThat(tokens.nextPosition().getLine(), is(1));
+    }
+    
+    @Test
+    public void shouldFindPreviousPositionStartIndex() {
+    	makeCaseInsensitive();
+    	// "Select all columns from this table";
+    	tokens.consume();
+    	tokens.consume();
+    	// previous position should be line 1, column 8
+    	assertThat(tokens.previousPosition().getIndexInContent(), is(7));
+    	assertThat(tokens.previousPosition().getColumn(), is(8));
+    	assertThat(tokens.previousPosition().getLine(), is(1));
+    }
+    
+    @Test
+    public void shouldParseMultiLineString() {
+    	makeCaseInsensitive();
+    	String content = "ALTER DATABASE \n"
+    			+ "DO SOMETHING; \n"
+    			+ "ALTER DATABASE \n"
+    			+ "      SET DEFAULT BIGFILE TABLESPACE;";
+        tokens = new TokenStream(content, tokenizer, true);
+        tokens.start();
+
+    	tokens.consume(); // LINE
+    	tokens.consume(); // ONE
+    	tokens.consume(); // DO
+    	tokens.consume(); // SOMETHING
+    	tokens.consume(); // ;
+    	
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(31));
+    	assertThat(tokens.nextPosition().getColumn(), is(1));
+    	tokens.consume(); // ALTER
+    	assertThat(tokens.nextPosition().getIndexInContent(), is(37));
+    	assertThat(tokens.nextPosition().getColumn(), is(7));
+
+    }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -799,15 +799,12 @@
     }
 
     protected Term parseFullTextSearchExpression( String expression,
-                                                  Position position ) {
+                                                  Position startOfExpression ) {
         try {
             return new FullTextSearchParser().parse(expression);
         } catch (ParsingException e) {
             // Convert the position in the exception into a position in the query.
-            Position exprPos = e.getPosition();
-            int line = position.getLine() + exprPos.getLine() - 1;
-            int column = exprPos.getLine() == 1 ? exprPos.getColumn() + position.getColumn() : exprPos.getColumn();
-            Position queryPos = new Position(line, column);
+        	Position queryPos = startOfExpression.add(e.getPosition());
             throw new ParsingException(queryPos, e.getMessage());
         }
     }
@@ -1264,14 +1261,14 @@
                     case '|':
                     case '=':
                     case ':':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                         break;
                     case '\'':
                     case '[':
                     case '\"':
                         int startIndex = input.index();
                         char closingChar = c == '[' ? ']' : c;
-                        Position pos = input.position();
+                        Position pos = input.position(startIndex);
                         boolean foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -1296,7 +1293,7 @@
                         break;
                     case '-':
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         if (input.isNext('-')) {
                             // End-of-line comment ...
                             boolean foundLineTerminator = false;
@@ -1314,13 +1311,13 @@
                                 tokens.addToken(pos, startIndex, endIndex, COMMENT);
                             }
                         } else {
-                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                             break;
                         }
                         break;
                     case '/':
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         if (input.isNext('*')) {
                             // Multi-line comment ...
                             while (input.hasNext() && !input.isNext('*', '/')) {
@@ -1333,13 +1330,13 @@
                                 tokens.addToken(pos, startIndex, endIndex, COMMENT);
                             }
                         } else {
-                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                             break;
                         }
                         break;
                     default:
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(input.index());
                         // Read as long as there is a valid XML character ...
                         int tokenType = (Character.isLetterOrDigit(c) || c == '_') ? WORD : OTHER;
                         while (input.isNextLetterOrDigit() || input.isNext('_')) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -789,8 +789,8 @@
 
     @Test
     public void shouldParseFullTextSearchExpressionFromStringWithValidExpression() {
-        Position pos = new Position(100, 13);
-        Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
+        Position pos = new Position(500, 100, 13);
+        FullTextSearch.Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
         assertThat(result, is(notNullValue()));
         assertThat(result, is(instanceOf(Disjunction.class)));
         Disjunction disjunction = (Disjunction)result;
@@ -808,11 +808,12 @@
     @Test
     public void shouldConvertPositionWhenUnableToParseFullTextSearchExpression() {
         try {
-            parser.parseFullTextSearchExpression("", new Position(100, 13));
+            parser.parseFullTextSearchExpression("", new Position(500, 100, 13));
             fail("Should have thrown an exception");
         } catch (ParsingException e) {
             assertThat(e.getPosition().getLine(), is(100));
             assertThat(e.getPosition().getColumn(), is(13));
+            assertThat(e.getPosition().getIndexInContent(), is(500));
         }
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-11-24 17:46:37 UTC (rev 1340)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-11-24 21:17:39 UTC (rev 1341)
@@ -751,13 +751,13 @@
                     case '\\':
                     case '#':
                     case '@':
-                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                         break;
                     case '\'':
                     case '\"':
                         int startIndex = input.index();
                         char closingChar = c;
-                        Position pos = input.position();
+                        Position pos = input.position(startIndex);
                         boolean foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
@@ -782,7 +782,7 @@
                         startIndex = input.index();
                         if (input.isNext(':')) {
                             // This is a comment ...
-                            pos = input.position();
+                            pos = input.position(startIndex);
                             while (input.hasNext() && !input.isNext(':', ')')) {
                                 c = input.next();
                             }
@@ -793,13 +793,13 @@
                                 tokens.addToken(pos, startIndex, endIndex, COMMENT);
                             }
                         } else {
-                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            tokens.addToken(input.position(startIndex), input.index(), input.index() + 1, SYMBOL);
                             break;
                         }
                         break;
                     default:
                         startIndex = input.index();
-                        pos = input.position();
+                        pos = input.position(startIndex);
                         // Read as long as there is a valid XML character ...
                         int tokenType = (XmlCharacters.isValidNcNameStart(c)) ? NAME : OTHER;
                         while (input.isNextValidXmlNcNameCharacter()) {


From dna-commits at lists.jboss.org  Tue Nov 24 19:59:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Nov 2009 19:59:30 -0500
Subject: [dna-commits] DNA SVN: r1342 - in trunk/dna-search/src:
	test/java/org/jboss/dna/search and 1 other directory.
Message-ID: <200911250059.nAP0xUTh012481@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-24 19:59:30 -0500 (Tue, 24 Nov 2009)
New Revision: 1342

Modified:
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
Log:
DNA-467 Changed the way IndexRules are defined to address the different datatypes, allowing the queries to be properly created using the appropriate Lucene query objects.

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-24 21:17:39 UTC (rev 1341)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexSearchProvider.java	2009-11-25 00:59:30 UTC (rev 1342)
@@ -74,14 +74,14 @@
 import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.PropertyType;
 import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.basic.JodaDateTime;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryEngine;
 import org.jboss.dna.graph.query.QueryResults;
@@ -106,6 +106,8 @@
 import org.jboss.dna.graph.request.ChangeRequest;
 import org.jboss.dna.graph.search.SearchException;
 import org.jboss.dna.graph.search.SearchProvider;
+import org.jboss.dna.search.IndexRules.FieldType;
+import org.jboss.dna.search.IndexRules.NumericRule;
 import org.jboss.dna.search.IndexRules.Rule;
 import org.jboss.dna.search.LuceneSession.TupleCollector;
 import org.jboss.dna.search.query.CompareLengthQuery;
@@ -133,22 +135,22 @@
     public static final IndexRules DEFAULT_RULES;
 
     static {
+        // We know that the earliest creation/modified dates cannot be before November 1 2009,
+        // which is before this feature was implemented
+        long earliestChangeDate = new JodaDateTime(2009, 11, 01, 0, 0, 0, 0).getMilliseconds();
+
         IndexRules.Builder builder = IndexRules.createBuilder();
         // Configure the default behavior ...
-        builder.defaultTo(IndexRules.INDEX | IndexRules.FULL_TEXT | IndexRules.STORE);
+        builder.defaultTo(Field.Store.YES, Field.Index.ANALYZED);
         // Configure the UUID properties to be just indexed and stored (not analyzed, not included in full-text) ...
-        builder.store(JcrLexicon.UUID, DnaLexicon.UUID);
+        builder.stringField(JcrLexicon.UUID, Field.Store.YES, Field.Index.NOT_ANALYZED);
+        builder.stringField(DnaLexicon.UUID, Field.Store.YES, Field.Index.NOT_ANALYZED);
         // Configure the properties that we'll treat as dates ...
-        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+        builder.dateField(JcrLexicon.CREATED, Field.Store.YES, Field.Index.NOT_ANALYZED, earliestChangeDate);
+        builder.dateField(JcrLexicon.LAST_MODIFIED, Field.Store.YES, Field.Index.NOT_ANALYZED, earliestChangeDate);
         DEFAULT_RULES = builder.build();
     }
 
-    protected static final long MIN_DATE = 0;
-    protected static final long MAX_DATE = Long.MAX_VALUE;
-    protected static final long MIN_LONG = Long.MIN_VALUE;
-    protected static final long MAX_LONG = Long.MAX_VALUE;
-    protected static final double MIN_DOUBLE = Double.MIN_VALUE;
-    protected static final double MAX_DOUBLE = Double.MAX_VALUE;
     protected static final int MIN_DEPTH = 0;
     protected static final int MAX_DEPTH = 100;
     protected static final int MIN_SNS_INDEX = 1;
@@ -506,31 +508,66 @@
                 for (Property property : node.getProperties()) {
                     Name name = property.getName();
                     Rule rule = rules.getRule(name);
-                    if (!rule.isIncluded()) continue;
+                    if (rule.isSkipped()) continue;
                     String nameString = stringFactory.create(name);
-                    if (rule.isDate()) {
+                    FieldType type = rule.getType();
+                    if (type == FieldType.DATE) {
+                        boolean index = rule.getIndexOption() != Field.Index.NO;
                         for (Object value : property) {
                             if (value == null) continue;
+                            // Add a separate field for each property value ...
                             DateTime dateValue = dateFactory.create(value);
+                            long longValue = dateValue.getMillisecondsInUtc();
+                            doc.add(new NumericField(nameString, rule.getStoreOption(), index).setLongValue(longValue));
+                        }
+                        continue;
+                    }
+                    if (type == FieldType.INT) {
+                        ValueFactory<Long> longFactory = context.getValueFactories().getLongFactory();
+                        boolean index = rule.getIndexOption() != Field.Index.NO;
+                        for (Object value : property) {
+                            if (value == null) continue;
                             // Add a separate field for each property value ...
-                            doc.add(new NumericField(nameString, rule.getStoreOption(), true).setLongValue(dateValue.getMillisecondsInUtc()));
-                            // Dates are not added to the full-text search field (since this wouldn't make sense)
+                            int intValue = longFactory.create(value).intValue();
+                            doc.add(new NumericField(nameString, rule.getStoreOption(), index).setIntValue(intValue));
                         }
                         continue;
                     }
+                    if (type == FieldType.DOUBLE) {
+                        ValueFactory<Double> doubleFactory = context.getValueFactories().getDoubleFactory();
+                        boolean index = rule.getIndexOption() != Field.Index.NO;
+                        for (Object value : property) {
+                            if (value == null) continue;
+                            // Add a separate field for each property value ...
+                            double dValue = doubleFactory.create(value);
+                            doc.add(new NumericField(nameString, rule.getStoreOption(), index).setDoubleValue(dValue));
+                        }
+                        continue;
+                    }
+                    if (type == FieldType.FLOAT) {
+                        ValueFactory<Double> doubleFactory = context.getValueFactories().getDoubleFactory();
+                        boolean index = rule.getIndexOption() != Field.Index.NO;
+                        for (Object value : property) {
+                            if (value == null) continue;
+                            // Add a separate field for each property value ...
+                            float fValue = doubleFactory.create(value).floatValue();
+                            doc.add(new NumericField(nameString, rule.getStoreOption(), index).setFloatValue(fValue));
+                        }
+                        continue;
+                    }
+                    if (type == FieldType.BINARY) {
+                        // TODO : add to full-text search ...
+                        continue;
+                    }
+                    assert type == FieldType.STRING;
                     for (Object value : property) {
                         if (value == null) continue;
-                        if (value instanceof Binary) {
-                            // don't include binary values as individual fields but do include them in the full-text search ...
-                            // TODO : add to full-text search ...
-                            continue;
-                        }
                         stringValue = stringFactory.create(value);
                         // Add a separate field for each property value ...
                         doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
 
-                        if (rule.isFullText()) {
-                            // Add this text to the full-text field ...
+                        if (rule.getIndexOption() != Field.Index.NO) {
+                            // This field is to be full-text searchable ...
                             if (fullTextSearchValue == null) {
                                 fullTextSearchValue = new StringBuilder();
                             } else {
@@ -545,7 +582,7 @@
                     }
                 }
                 // Add the full-text-search field ...
-                if (fullTextSearchValue != null) {
+                if (fullTextSearchValue != null && fullTextSearchValue.length() != 0) {
                     doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO,
                                       Field.Index.ANALYZED));
                 }
@@ -1121,23 +1158,22 @@
             return null;
         }
 
+        @SuppressWarnings( "unchecked" )
         @Override
         public Query findNodesWith( PropertyValue propertyValue,
                                     Operator operator,
                                     Object value,
                                     boolean caseSensitive ) {
             String field = stringFactory.create(propertyValue.getPropertyName());
-            PropertyType valueType = PropertyType.discoverType(value);
+            Name fieldName = nameFactory.create(propertyValue.getPropertyName());
             ValueFactories factories = context.getValueFactories();
-            switch (valueType) {
-                case NAME:
-                case PATH:
-                case REFERENCE:
-                case URI:
-                case UUID:
+            IndexRules.Rule rule = rules.getRule(fieldName);
+            if (rule == null || rule.isSkipped()) return new MatchNoneQuery();
+            FieldType type = rule.getType();
+            switch (type) {
                 case STRING:
                     String stringValue = stringFactory.create(value);
-                    if (valueType == PropertyType.PATH) {
+                    if (value instanceof Path) {
                         stringValue = pathAsString(pathFactory.create(value), stringFactory);
                     }
                     if (!caseSensitive) stringValue = stringValue.toLowerCase();
@@ -1178,6 +1214,7 @@
                     }
                     break;
                 case DATE:
+                    NumericRule<Long> longRule = (NumericRule<Long>)rule;
                     long date = factories.getLongFactory().create(value);
                     switch (operator) {
                         case EQUAL_TO:
@@ -1186,13 +1223,13 @@
                             Query query = NumericRangeQuery.newLongRange(field, date, date, true, true);
                             return new NotQuery(query);
                         case GREATER_THAN:
-                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, false, true);
+                            return NumericRangeQuery.newLongRange(field, date, longRule.getMaximum(), false, true);
                         case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, date, MAX_DATE, true, true);
+                            return NumericRangeQuery.newLongRange(field, date, longRule.getMaximum(), true, true);
                         case LESS_THAN:
-                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, false);
+                            return NumericRangeQuery.newLongRange(field, longRule.getMinimum(), date, true, false);
                         case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, MIN_DATE, date, true, true);
+                            return NumericRangeQuery.newLongRange(field, longRule.getMinimum(), date, true, true);
                         case LIKE:
                             // This is not allowed ...
                             assert false;
@@ -1200,6 +1237,7 @@
                     }
                     break;
                 case LONG:
+                    longRule = (NumericRule<Long>)rule;
                     long longValue = factories.getLongFactory().create(value);
                     switch (operator) {
                         case EQUAL_TO:
@@ -1208,21 +1246,44 @@
                             Query query = NumericRangeQuery.newLongRange(field, longValue, longValue, true, true);
                             return new NotQuery(query);
                         case GREATER_THAN:
-                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, false, true);
+                            return NumericRangeQuery.newLongRange(field, longValue, longRule.getMaximum(), false, true);
                         case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, longValue, MAX_LONG, true, true);
+                            return NumericRangeQuery.newLongRange(field, longValue, longRule.getMaximum(), true, true);
                         case LESS_THAN:
-                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, false);
+                            return NumericRangeQuery.newLongRange(field, longRule.getMinimum(), longValue, true, false);
                         case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newLongRange(field, MIN_LONG, longValue, true, true);
+                            return NumericRangeQuery.newLongRange(field, longRule.getMinimum(), longValue, true, true);
                         case LIKE:
                             // This is not allowed ...
                             assert false;
                             return null;
                     }
                     break;
-                case DECIMAL:
+                case INT:
+                    NumericRule<Integer> intRule = (NumericRule<Integer>)rule;
+                    int intValue = factories.getLongFactory().create(value).intValue();
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newIntRange(field, intValue, intValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newIntRange(field, intValue, intValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newIntRange(field, intValue, intRule.getMaximum(), false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newIntRange(field, intValue, intRule.getMaximum(), true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newIntRange(field, intRule.getMinimum(), intValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newIntRange(field, intRule.getMinimum(), intValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
                 case DOUBLE:
+                    NumericRule<Double> dRule = (NumericRule<Double>)rule;
                     double doubleValue = factories.getDoubleFactory().create(value);
                     switch (operator) {
                         case EQUAL_TO:
@@ -1231,19 +1292,42 @@
                             Query query = NumericRangeQuery.newDoubleRange(field, doubleValue, doubleValue, true, true);
                             return new NotQuery(query);
                         case GREATER_THAN:
-                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, false, true);
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, dRule.getMaximum(), false, true);
                         case GREATER_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newDoubleRange(field, doubleValue, MAX_DOUBLE, true, true);
+                            return NumericRangeQuery.newDoubleRange(field, doubleValue, dRule.getMaximum(), true, true);
                         case LESS_THAN:
-                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, false);
+                            return NumericRangeQuery.newDoubleRange(field, dRule.getMinimum(), doubleValue, true, false);
                         case LESS_THAN_OR_EQUAL_TO:
-                            return NumericRangeQuery.newDoubleRange(field, MIN_DOUBLE, doubleValue, true, true);
+                            return NumericRangeQuery.newDoubleRange(field, dRule.getMinimum(), doubleValue, true, true);
                         case LIKE:
                             // This is not allowed ...
                             assert false;
                             return null;
                     }
                     break;
+                case FLOAT:
+                    NumericRule<Float> fRule = (NumericRule<Float>)rule;
+                    float floatValue = factories.getDoubleFactory().create(value).floatValue();
+                    switch (operator) {
+                        case EQUAL_TO:
+                            return NumericRangeQuery.newFloatRange(field, floatValue, floatValue, true, true);
+                        case NOT_EQUAL_TO:
+                            Query query = NumericRangeQuery.newFloatRange(field, floatValue, floatValue, true, true);
+                            return new NotQuery(query);
+                        case GREATER_THAN:
+                            return NumericRangeQuery.newFloatRange(field, floatValue, fRule.getMaximum(), false, true);
+                        case GREATER_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newFloatRange(field, floatValue, fRule.getMaximum(), true, true);
+                        case LESS_THAN:
+                            return NumericRangeQuery.newFloatRange(field, fRule.getMinimum(), floatValue, true, false);
+                        case LESS_THAN_OR_EQUAL_TO:
+                            return NumericRangeQuery.newFloatRange(field, fRule.getMinimum(), floatValue, true, true);
+                        case LIKE:
+                            // This is not allowed ...
+                            assert false;
+                            return null;
+                    }
+                    break;
                 case BOOLEAN:
                     boolean booleanValue = factories.getBooleanFactory().create(value);
                     stringValue = stringFactory.create(value);
@@ -1274,7 +1358,6 @@
                             return null;
                     }
                     break;
-                case OBJECT:
                 case BINARY:
                     // This is not allowed ...
                     assert false;
@@ -1307,8 +1390,10 @@
                                                    Object upperValue,
                                                    boolean includesLower,
                                                    boolean includesUpper ) {
-            PropertyType type = PropertyType.discoverType(lowerValue);
-            assert type == PropertyType.discoverType(upperValue);
+            Name fieldName = nameFactory.create(field);
+            IndexRules.Rule rule = rules.getRule(fieldName);
+            if (rule == null || rule.isSkipped()) return new MatchNoneQuery();
+            FieldType type = rule.getType();
             ValueFactories factories = context.getValueFactories();
             switch (type) {
                 case DATE:
@@ -1319,16 +1404,27 @@
                     long lowerLong = factories.getLongFactory().create(lowerValue);
                     long upperLong = factories.getLongFactory().create(upperValue);
                     return NumericRangeQuery.newLongRange(field, lowerLong, upperLong, includesLower, includesUpper);
-                case DECIMAL:
                 case DOUBLE:
                     double lowerDouble = factories.getDoubleFactory().create(lowerValue);
                     double upperDouble = factories.getDoubleFactory().create(upperValue);
                     return NumericRangeQuery.newDoubleRange(field, lowerDouble, upperDouble, includesLower, includesUpper);
-                default:
-                    // This is not allowed ...
+                case FLOAT:
+                    float lowerFloat = factories.getDoubleFactory().create(lowerValue).floatValue();
+                    float upperFloat = factories.getDoubleFactory().create(upperValue).floatValue();
+                    return NumericRangeQuery.newFloatRange(field, lowerFloat, upperFloat, includesLower, includesUpper);
+                case INT:
+                    int lowerInt = factories.getLongFactory().create(lowerValue).intValue();
+                    int upperInt = factories.getLongFactory().create(upperValue).intValue();
+                    return NumericRangeQuery.newIntRange(field, lowerInt, upperInt, includesLower, includesUpper);
+                case BOOLEAN:
+                    lowerInt = factories.getBooleanFactory().create(lowerValue).booleanValue() ? 1 : 0;
+                    upperInt = factories.getBooleanFactory().create(upperValue).booleanValue() ? 1 : 0;
+                    return NumericRangeQuery.newIntRange(field, lowerInt, upperInt, includesLower, includesUpper);
+                case STRING:
+                case BINARY:
                     assert false;
-                    return null;
             }
+            return new MatchNoneQuery();
         }
 
         @Override

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-24 21:17:39 UTC (rev 1341)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-25 00:59:30 UTC (rev 1342)
@@ -26,7 +26,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
 import org.apache.lucene.document.Field;
@@ -41,13 +40,16 @@
 @Immutable
 public class IndexRules {
 
-    public static final int INDEX = 2 << 0;
-    public static final int ANALYZE = 2 << 1;
-    public static final int STORE = 2 << 2;
-    public static final int STORE_COMPRESSED = 2 << 3;
-    public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
-    public static final int FULL_TEXT = 2 << 5;
-    public static final int TREAT_AS_DATE = 2 << 6;
+    public static enum FieldType {
+        STRING,
+        DOUBLE,
+        FLOAT,
+        INT,
+        BOOLEAN,
+        LONG,
+        DATE,
+        BINARY;
+    }
 
     /**
      * A single rule that dictates how a single property should be indexed.
@@ -56,111 +58,49 @@
      */
     @Immutable
     public static interface Rule {
-        /**
-         * Return whether this property should be included in the indexes.
-         * 
-         * @return true if it is to be included, or false otherwise
-         */
-        boolean isIncluded();
 
-        boolean isAnalyzed();
+        boolean isSkipped();
 
-        boolean isAnalyzedWithoutNorms();
+        FieldType getType();
 
-        boolean isStored();
-
-        boolean isStoredCompressed();
-
-        boolean isFullText();
-
-        boolean isDate();
-
-        int getMask();
-
         Field.Store getStoreOption();
 
         Field.Index getIndexOption();
     }
 
+    @Immutable
+    public static interface NumericRule<T> extends Rule {
+        T getMinimum();
+
+        T getMaximum();
+    }
+
     public static final Rule SKIP = new SkipRule();
 
     @Immutable
     protected static class SkipRule implements Rule {
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
-         */
-        public int getMask() {
-            return 0;
-        }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         * @see org.jboss.dna.search.IndexRules.Rule#getType()
          */
-        public boolean isAnalyzed() {
-            return false;
+        public FieldType getType() {
+            return FieldType.STRING;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
          */
-        public boolean isAnalyzedWithoutNorms() {
-            return false;
+        public boolean isSkipped() {
+            return true;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
-         */
-        public boolean isFullText() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
-         */
-        public boolean isIncluded() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
-         */
-        public boolean isDate() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
          * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
          */
         public Index getIndexOption() {
@@ -178,128 +118,115 @@
     }
 
     @Immutable
-    public static final class GeneralRule implements Rule {
-        private final int value;
-        private final Field.Store store;
-        private final Field.Index index;
+    protected static class TypedRule implements Rule {
+        protected final FieldType type;
+        protected final Field.Store store;
+        protected final Field.Index index;
 
-        protected GeneralRule( int value ) {
-            this.value = value;
-            this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
-            this.store = isStored() ? Field.Store.YES : Field.Store.NO;
+        protected TypedRule( FieldType type,
+                             Field.Store store,
+                             Field.Index index ) {
+            this.type = type;
+            this.index = index;
+            this.store = store;
+            assert this.type != null;
+            assert this.index != null;
+            assert this.store != null;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
+         * @see org.jboss.dna.search.IndexRules.Rule#getType()
          */
-        public int getMask() {
-            return value;
+        public FieldType getType() {
+            return type;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
          */
-        public boolean isAnalyzed() {
-            return (value & ANALYZE) == ANALYZE;
+        public boolean isSkipped() {
+            return false;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
          */
-        public boolean isAnalyzedWithoutNorms() {
-            return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
+        public Index getIndexOption() {
+            return index;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
+         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
          */
-        public boolean isFullText() {
-            return (value & FULL_TEXT) == FULL_TEXT;
+        public Store getStoreOption() {
+            return store;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
+         * @see java.lang.Object#toString()
          */
-        public boolean isIncluded() {
-            return true;
+        @Override
+        public String toString() {
+            return type.name() + " rule (" + store + "," + index + ")";
         }
+    }
 
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return (value & STORE) == STORE;
-        }
+    @Immutable
+    protected static class NumericTypedRule<T> extends TypedRule implements NumericRule<T> {
+        protected final T minValue;
+        protected final T maxValue;
 
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
+        protected NumericTypedRule( FieldType type,
+                                    Field.Store store,
+                                    Field.Index index,
+                                    T minValue,
+                                    T maxValue ) {
+            super(type, store, index);
+            this.minValue = minValue;
+            this.maxValue = maxValue;
+            assert this.minValue != null;
+            assert this.maxValue != null;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
+         * @see org.jboss.dna.search.IndexRules.NumericRule#getMaximum()
          */
-        public boolean isDate() {
-            return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
+        public T getMaximum() {
+            return maxValue;
         }
 
-        protected Rule with( int options ) {
-            return createRule(value | options);
-        }
-
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
+         * @see org.jboss.dna.search.IndexRules.NumericRule#getMinimum()
          */
-        public Index getIndexOption() {
-            return index;
+        public T getMinimum() {
+            return minValue;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
+         * @see java.lang.Object#toString()
          */
-        public Store getStoreOption() {
-            return store;
+        @Override
+        public String toString() {
+            return super.toString() + " with range [" + minValue + "," + maxValue + "]";
         }
     }
 
-    private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
-
-    protected static Rule createRule( int value ) {
-        if (value <= 0) {
-            return SKIP;
-        }
-        Integer key = new Integer(value);
-        Rule rule = CACHE.get(key);
-        if (rule == null) {
-            Rule newRule = new GeneralRule(value);
-            rule = CACHE.putIfAbsent(value, newRule);
-            if (rule == null) rule = newRule;
-        }
-        return rule;
-    }
-
     private final Map<Name, Rule> rulesByName;
     private final Rule defaultRule;
 
@@ -327,7 +254,7 @@
      * @return a builder; never null
      */
     public static Builder createBuilder() {
-        return new Builder(new HashMap<Name, Rule>());
+        return new Builder(new HashMap<Name, Rule>(), null);
     }
 
     /**
@@ -339,7 +266,7 @@
      */
     public static Builder createBuilder( IndexRules initialRules ) {
         CheckArg.isNotNull(initialRules, "initialRules");
-        return new Builder(new HashMap<Name, Rule>(initialRules.rulesByName)).defaultTo(initialRules.defaultRule);
+        return new Builder(new HashMap<Name, Rule>(initialRules.rulesByName), initialRules.defaultRule);
     }
 
     /**
@@ -350,258 +277,357 @@
         private final Map<Name, Rule> rulesByName;
         private Rule defaultRule;
 
-        Builder( Map<Name, Rule> rulesByName ) {
+        Builder( Map<Name, Rule> rulesByName,
+                 Rule defaultRule ) {
             assert rulesByName != null;
             this.rulesByName = rulesByName;
+            this.defaultRule = defaultRule;
         }
 
         /**
-         * Set the default rules.
+         * Mark the properties with the supplied names to be skipped from indexing.
          * 
-         * @param rule the default rule to use
+         * @param namesToIndex the names of the properties that are to be skipped
          * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
          */
-        public Builder defaultTo( Rule rule ) {
-            CheckArg.isNotNull(rule, "rule");
-            defaultRule = rule;
+        public Builder skip( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    rulesByName.put(name, SKIP);
+                }
+            }
             return this;
         }
 
         /**
-         * Set the default rules.
+         * Define a string-based field as the default.
          * 
-         * @param ruleMask the bitmask of rule to use
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
          * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
          */
-        public Builder defaultTo( int ruleMask ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (ruleMask == 0) {
-                defaultRule = SKIP;
-            } else {
-                // Make sure the index flag is set ...
-                ruleMask |= INDEX;
-                defaultRule = createRule(ruleMask);
-            }
+        public Builder defaultTo( Field.Store store,
+                                  Field.Index index ) {
+            if (store == null) store = Field.Store.YES;
+            if (index == null) index = Field.Index.NOT_ANALYZED;
+            defaultRule = new TypedRule(FieldType.STRING, store, index);
             return this;
         }
 
         /**
-         * Mark the properties with the supplied names to be skipped from indexing.
+         * Define a string-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be skipped
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder skip( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    rulesByName.put(name, SKIP);
-                }
-            }
+        public Builder stringField( Name name,
+                                    Field.Store store,
+                                    Field.Index index ) {
+            if (store == null) store = Field.Store.YES;
+            if (index == null) index = Field.Index.NOT_ANALYZED;
+            Rule rule = new TypedRule(FieldType.STRING, store, index);
+            rulesByName.put(name, rule);
             return this;
         }
 
         /**
-         * Set the properties with the supplied names to use the supplied rules.
+         * Define a binary-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param ruleMask the bitmask of rules to use
-         * @param namesToIndex the names of the properties that are to be skipped
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
          * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
          */
-        public Builder set( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                if (ruleMask > 0) {
-                    skip(namesToIndex);
-                } else {
-                    // Make sure the index flag is set ...
-                    ruleMask |= INDEX;
-                    Rule rule = createRule(ruleMask);
-                    for (Name name : namesToIndex) {
-                        rulesByName.put(name, rule);
-                    }
-                }
-            }
+        public Builder binaryField( Name name,
+                                    Field.Store store,
+                                    Field.Index index ) {
+            if (store == null) store = Field.Store.YES;
+            if (index == null) index = Field.Index.NOT_ANALYZED;
+            Rule rule = new TypedRule(FieldType.BINARY, store, index);
+            rulesByName.put(name, rule);
             return this;
         }
 
+        protected <T> Builder numericField( Name name,
+                                            FieldType type,
+                                            Field.Store store,
+                                            Field.Index index,
+                                            T minValue,
+                                            T maxValue ) {
+            if (store == null) store = Field.Store.YES;
+            if (index == null) index = Field.Index.NOT_ANALYZED;
+            Rule rule = new NumericTypedRule<T>(type, store, index, minValue, maxValue);
+            rulesByName.put(name, rule);
+            return this;
+        }
+
         /**
-         * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
-         * these properties.
+         * Define a boolean-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param ruleMask the bitmask of rules to add
-         * @param namesToIndex the names of the properties that are to be skipped
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
          * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
          */
-        public Builder add( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ruleMask);
-                }
-            }
-            return this;
+        public Builder booleanField( Name name,
+                                     Field.Store store,
+                                     Field.Index index ) {
+            return numericField(name, FieldType.BOOLEAN, store, index, Boolean.FALSE, Boolean.TRUE);
         }
 
         /**
-         * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
+         * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be indexed
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @param maxValue the maximum value for this field, or null if there is no maximum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder index( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX);
-                }
-            }
-            return this;
+        public Builder integerField( Name name,
+                                     Field.Store store,
+                                     Field.Index index,
+                                     Integer minValue,
+                                     Integer maxValue ) {
+            if (minValue == null) minValue = Integer.MIN_VALUE;
+            if (maxValue == null) maxValue = Integer.MAX_VALUE;
+            return numericField(name, FieldType.INT, store, index, minValue, maxValue);
         }
 
         /**
-         * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
-         * rules for these properties.
+         * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be analyzed
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @param maxValue the maximum value for this field, or null if there is no maximum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder analyze( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ANALYZE | INDEX);
-                }
-            }
-            return this;
+        public Builder longField( Name name,
+                                  Field.Store store,
+                                  Field.Index index,
+                                  Long minValue,
+                                  Long maxValue ) {
+            if (minValue == null) minValue = Long.MIN_VALUE;
+            if (maxValue == null) maxValue = Long.MAX_VALUE;
+            return numericField(name, FieldType.LONG, store, index, minValue, maxValue);
         }
 
         /**
-         * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
-         * for these properties.
+         * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be stored
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @param maxValue the maximum value for this field, or null if there is no maximum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder store( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, STORE | INDEX);
-                }
-            }
-            return this;
+        public Builder dateField( Name name,
+                                  Field.Store store,
+                                  Field.Index index,
+                                  Long minValue,
+                                  Long maxValue ) {
+            if (minValue == null) minValue = 0L;
+            if (maxValue == null) maxValue = Long.MAX_VALUE;
+            return numericField(name, FieldType.DATE, store, index, minValue, maxValue);
         }
 
         /**
-         * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
-         * remove any other rules for these properties.
+         * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @param maxValue the maximum value for this field, or null if there is no maximum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder fullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, FULL_TEXT | INDEX);
-                }
-            }
-            return this;
+        public Builder floatField( Name name,
+                                   Field.Store store,
+                                   Field.Index index,
+                                   Float minValue,
+                                   Float maxValue ) {
+            if (minValue == null) minValue = Float.MIN_VALUE;
+            if (maxValue == null) maxValue = Float.MAX_VALUE;
+            return numericField(name, FieldType.FLOAT, store, index, minValue, maxValue);
         }
 
         /**
-         * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
-         * other rules for these properties.
+         * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @param maxValue the maximum value for this field, or null if there is no maximum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder treatAsDates( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, TREAT_AS_DATE | INDEX);
-                }
-            }
-            return this;
+        public Builder doubleField( Name name,
+                                    Field.Store store,
+                                    Field.Index index,
+                                    Double minValue,
+                                    Double maxValue ) {
+            if (minValue == null) minValue = Double.MIN_VALUE;
+            if (maxValue == null) maxValue = Double.MAX_VALUE;
+            return numericField(name, FieldType.DOUBLE, store, index, minValue, maxValue);
         }
 
         /**
-         * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
-         * for these properties.
+         * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder analyzeAndStore( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE);
-                }
-            }
-            return this;
+        public Builder integerField( Name name,
+                                     Field.Store store,
+                                     Field.Index index,
+                                     Integer minValue ) {
+            return integerField(name, store, index, minValue, null);
         }
 
         /**
-         * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
-         * does not remove any other rules for these properties.
+         * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
-         *        searches
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
-                }
-            }
-            return this;
+        public Builder longField( Name name,
+                                  Field.Store store,
+                                  Field.Index index,
+                                  Long minValue ) {
+            return longField(name, store, index, minValue, null);
         }
 
         /**
-         * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
-         * remove any other rules for these properties.
+         * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder analyzeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | FULL_TEXT);
-                }
-            }
-            return this;
+        public Builder dateField( Name name,
+                                  Field.Store store,
+                                  Field.Index index,
+                                  Long minValue ) {
+            return dateField(name, store, index, minValue, null);
         }
 
         /**
-         * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
-         * remove any other rules for these properties.
+         * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
          * 
-         * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
          * @return this builder for convenience and method chaining; never null
          */
-        public Builder storeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | STORE | FULL_TEXT);
-                }
-            }
-            return this;
+        public Builder floatField( Name name,
+                                   Field.Store store,
+                                   Field.Index index,
+                                   Float minValue ) {
+            return floatField(name, store, index, minValue, null);
         }
 
-        protected void add( Name name,
-                            int option ) {
-            Rule rule = rulesByName.get(name);
-            if (rule != null) {
-                option |= rule.getMask();
-            }
-            rulesByName.put(name, createRule(option));
+        /**
+         * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @param minValue the minimum value for this field, or null if there is no minimum value
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder doubleField( Name name,
+                                    Field.Store store,
+                                    Field.Index index,
+                                    Double minValue ) {
+            return doubleField(name, store, index, minValue, null);
         }
 
         /**
+         * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder integerField( Name name,
+                                     Field.Store store,
+                                     Field.Index index ) {
+            return integerField(name, store, index, null, null);
+        }
+
+        /**
+         * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder longField( Name name,
+                                  Field.Store store,
+                                  Field.Index index ) {
+            return longField(name, store, index, null, null);
+        }
+
+        /**
+         * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder dateField( Name name,
+                                  Field.Store store,
+                                  Field.Index index ) {
+            return dateField(name, store, index, null, null);
+        }
+
+        /**
+         * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder floatField( Name name,
+                                   Field.Store store,
+                                   Field.Index index ) {
+            return floatField(name, store, index, null, null);
+        }
+
+        /**
+         * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
+         * 
+         * @param name the name of the field
+         * @param store the storage setting, or null if the field should be {@link Store#YES stored}
+         * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder doubleField( Name name,
+                                    Field.Store store,
+                                    Field.Index index ) {
+            return doubleField(name, store, index, null, null);
+        }
+
+        /**
          * Build the indexing rules.
          * 
          * @return the immutable indexing rules.

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-24 21:17:39 UTC (rev 1341)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-25 00:59:30 UTC (rev 1342)
@@ -25,6 +25,7 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
+import org.apache.lucene.document.Field;
 import org.jboss.dna.search.IndexRules.Builder;
 import org.junit.Before;
 import org.junit.Test;
@@ -51,8 +52,9 @@
 
     @Test
     public void shouldBuildValidRulesFromBuilderAfterJustSettingDefaultRules() {
-        builder.defaultTo(IndexRules.FULL_TEXT);
+        builder.defaultTo(Field.Store.NO, Field.Index.ANALYZED_NO_NORMS);
         rules = builder.build();
-        assertThat(rules.getRule(null).isFullText(), is(true));
+        assertThat(rules.getRule(null).getIndexOption(), is(Field.Index.ANALYZED_NO_NORMS));
+        assertThat(rules.getRule(null).getStoreOption(), is(Field.Store.NO));
     }
 }

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-24 21:17:39 UTC (rev 1341)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-25 00:59:30 UTC (rev 1342)
@@ -29,6 +29,7 @@
 import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.util.List;
+import org.apache.lucene.document.Field;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
@@ -47,7 +48,6 @@
 import org.jboss.dna.graph.search.SearchEngine;
 import org.jboss.dna.graph.search.SearchProvider;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.xml.sax.SAXException;
 
@@ -93,6 +93,12 @@
 
         // Set up the provider and the search engine ...
         IndexRules.Builder rulesBuilder = IndexRules.createBuilder(DualIndexSearchProvider.DEFAULT_RULES);
+        rulesBuilder.defaultTo(Field.Store.YES, Field.Index.NOT_ANALYZED);
+        rulesBuilder.stringField(name("model"), Field.Store.YES, Field.Index.ANALYZED);
+        rulesBuilder.integerField(name("year"), Field.Store.YES, Field.Index.NOT_ANALYZED);
+        rulesBuilder.floatField(name("userRating"), Field.Store.YES, Field.Index.NOT_ANALYZED, 0.0f, 10.0f);
+        rulesBuilder.integerField(name("mpgCity"), Field.Store.YES, Field.Index.NOT_ANALYZED, 0, 50);
+        rulesBuilder.integerField(name("mpgHighway"), Field.Store.YES, Field.Index.NOT_ANALYZED, 0, 50);
         // rulesBuilder.analyzeAndStoreAndFullText(name("maker"));
         IndexRules rules = rulesBuilder.build();
         LuceneConfiguration luceneConfig = LuceneConfigurations.inMemory();
@@ -103,7 +109,7 @@
 
         // Create the schemata for the workspaces ...
         schemata = ImmutableSchemata.createBuilder(typeSystem)
-                                    .addTable("__ALLNODES__", "maker", "model", "year", "msrp")
+                                    .addTable("__ALLNODES__", "maker", "model", "year", "msrp", "mpgHighway", "mpgCity")
                                     .makeSearchable("__ALLNODES__", "maker")
                                     .build();
 
@@ -242,6 +248,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(18));
         System.out.println(results);
@@ -252,19 +259,21 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker = 'Toyota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);
     }
 
-    @Ignore
     @Test
     public void shouldFindNodesBySimpleQueryWithGreaterThanComparisonCriteria() {
         engine.index(workspaceName1, path("/"), 100);
-        QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE mpgHighway > 20", typeSystem);
+        QueryCommand query = sql.parseQuery("SELECT model, maker, mpgHighway, mpgCity FROM __ALLNODES__ WHERE mpgHighway > 20",
+                                            typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
-        assertThat(results.getRowCount(), is(2));
+        assertThat(results.getRowCount(), is(6));
         System.out.println(results);
     }
 
@@ -273,6 +282,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) = 'toyota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);
@@ -283,6 +293,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE UPPER(maker) = 'TOYOTA'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);
@@ -293,6 +304,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE 'Toyo%'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);
@@ -303,6 +315,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE maker LIKE '%yota'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);
@@ -313,6 +326,7 @@
         engine.index(workspaceName1, path("/"), 100);
         QueryCommand query = sql.parseQuery("SELECT model, maker FROM __ALLNODES__ WHERE LOWER(maker) LIKE 'toyo%'", typeSystem);
         QueryResults results = engine.query(context, workspaceName1, query, schemata);
+        assertNoErrors(results);
         assertThat(results, is(notNullValue()));
         assertThat(results.getRowCount(), is(2));
         System.out.println(results);


From dna-commits at lists.jboss.org  Wed Nov 25 07:59:39 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 07:59:39 -0500
Subject: [dna-commits] DNA SVN: r1343 -
	branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text.
Message-ID: <200911251259.nAPCxdvw021096@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-25 07:59:39 -0500 (Wed, 25 Nov 2009)
New Revision: 1343

Modified:
   branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
Log:
DNA-555 Fixed problem in token positions having the wrong column number.

Modified: branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
===================================================================
--- branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-25 00:59:30 UTC (rev 1342)
+++ branches/ddl_sequencer/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-11-25 12:59:39 UTC (rev 1343)
@@ -395,7 +395,7 @@
      *         |   |  |
      *         |   |  +- The position of the tokenIterator, where tokenIterator.hasNext() will return T3
      *         |   +---- The token referenced by currentToken
-     *         +-------- The logical position of the TokenStream object, where the "consume()" would return T2
+     *         +-------- The logical position of the TokenStream object, where the &quot;consume()&quot; would return T2
      * </pre>
      */
     private ListIterator<Token> tokenIterator;
@@ -434,18 +434,18 @@
         moveToNextToken();
         return this;
     }
-    
+
     /**
-     * Method to allow subclasses to preprocess the set of tokens and return the correct tokens to use.
-     * The default behavior is to simply return the supplied tokens.
+     * Method to allow subclasses to preprocess the set of tokens and return the correct tokens to use. The default behavior is to
+     * simply return the supplied tokens.
      * 
      * @param tokens
      * @return list of tokens.
      */
-    protected List<Token> initializeTokens( List<Token> tokens) {
-    	return tokens;
+    protected List<Token> initializeTokens( List<Token> tokens ) {
+        return tokens;
     }
-    
+
     /**
      * Method to allow tokens to be re-used from the start without re-tokenizing content.
      */
@@ -761,9 +761,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -806,9 +809,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -847,9 +853,12 @@
      * 
      * <pre>
      * 
-     * if ( tokens.matches(currentExpected,expectedForNextTokens) ) { tokens.consume(currentExpected,expectedForNextTokens); }
+     * if (tokens.matches(currentExpected, expectedForNextTokens)) {
+     *     tokens.consume(currentExpected, expectedForNextTokens);
+     * }
      * 
      * </pre>
+     * 
      * </p>
      * <p>
      * The {@link #ANY_VALUE ANY_VALUE} constant can be used in the expected values as a wildcard.
@@ -1272,25 +1281,26 @@
      * Gets the content string starting at the first position (inclusive) and continuing up to the end position (exclusive).
      * 
      * @param starting the position marking the beginning of the desired content string.
-     * @param end the position located directly after the returned content string;  can be null, which means end of content
+     * @param end the position located directly after the returned content string; can be null, which means end of content
      * @return the content string; never null
      */
-    public String getContentBetween(Position starting, Position end) {
-    	CheckArg.isNotNull(starting, "starting");
-    	
-    	int startIndex = starting.getIndexInContent();
-    	int endIndex = inputString.length();
-    	if( end != null ) {
-    		endIndex = end.getIndexInContent();
-    	}
-    	
-    	if( startIndex >= endIndex ) {
-    		throw new IllegalArgumentException(CommonI18n.endPositionMustBeGreaterThanStartingPosition.text(startIndex, endIndex));
-    	}
-    	
-    	return inputString.substring(startIndex, endIndex);
+    public String getContentBetween( Position starting,
+                                     Position end ) {
+        CheckArg.isNotNull(starting, "starting");
+
+        int startIndex = starting.getIndexInContent();
+        int endIndex = inputString.length();
+        if (end != null) {
+            endIndex = end.getIndexInContent();
+        }
+
+        if (startIndex >= endIndex) {
+            throw new IllegalArgumentException(CommonI18n.endPositionMustBeGreaterThanStartingPosition.text(startIndex, endIndex));
+        }
+
+        return inputString.substring(startIndex, endIndex);
     }
-    
+
     /**
      * Get the previous token. This does not modify the state.
      * 
@@ -1308,10 +1318,10 @@
             }
             throw new IllegalStateException(CommonI18n.startMethodMustBeCalledBeforeConsumingOrMatching.text());
         }
-        if (tokenIterator.previousIndex() == 0 ) {
+        if (tokenIterator.previousIndex() == 0) {
             throw new NoSuchElementException(CommonI18n.noMoreContent.text());
         }
-        return tokens.get(tokenIterator.previousIndex()-1);
+        return tokens.get(tokenIterator.previousIndex() - 1);
     }
 
     String generateFragment() {
@@ -1394,11 +1404,11 @@
 
         /**
          * Get the position for the last character returned from {@link #next()}.
-         * @param startIndex 
          * 
+         * @param startIndex
          * @return the position of the last character returned; never null
          */
-        Position position(int startIndex);
+        Position position( int startIndex );
 
         /**
          * Determine if the next character on the sream is a {@link Character#isWhitespace(char) whitespace character}. This
@@ -1498,7 +1508,8 @@
          * Create a single- or multi-character token with the characters in the range given by the starting and ending index in
          * the character stream. The character at the ending index is <i>not</i> included in the token (as this is standard
          * practice when using 0-based indexes). The token type is set to 0, meaning this is equivalent to calling <code>
-         * addToken(startIndex,endIndex,0)</code> .
+         * addToken(startIndex,endIndex,0)</code>
+         * .
          * 
          * @param position the position (line and column numbers) of this new token; may not be null
          * @param startIndex the index of the first character to appear in the token; must be a valid index in the stream
@@ -1554,7 +1565,7 @@
          * @return true if the token's value matches the supplied character value, or false otherwise
          */
         boolean matches( char expected );
-        
+
         /**
          * Determine if the token matches the supplied type.
          * 
@@ -1597,14 +1608,14 @@
          * @return the position; never null
          */
         Position position();
-        
+
         /**
          * Bitmask ORed with existing type value.
          * 
          * @param typeMask
          * @return copy of Token with new type
          */
-        Token withType( int typeMask);
+        Token withType( int typeMask );
     }
 
     /**
@@ -1628,17 +1639,16 @@
         }
 
         /**
-		 * {@inheritDoc}
-		 *
-		 * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
-		 */
-		@Override
-		public Token withType(int typeMask) {
-			int type = this.type | typeMask;
-			return new CaseSensitiveToken(startIndex, endIndex, type, position);
-		}
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+         */
+        public Token withType( int typeMask ) {
+            int type = this.type | typeMask;
+            return new CaseSensitiveToken(startIndex, endIndex, type, position);
+        }
 
-		/**
+        /**
          * {@inheritDoc}
          * 
          * @see org.jboss.dna.common.text.TokenStream.Token#type()
@@ -1693,16 +1703,15 @@
         }
 
         /**
-		 * {@inheritDoc}
-		 *
-		 * @see org.jboss.dna.common.text.TokenStream.Token#matches(int)
-		 */
-		@Override
-		public final boolean matches(int expectedType) {
-			return expectedType == ANY_TYPE || (currentToken().type() & expectedType) == expectedType;
-		}
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#matches(int)
+         */
+        public final boolean matches( int expectedType ) {
+            return expectedType == ANY_TYPE || (currentToken().type() & expectedType) == expectedType;
+        }
 
-		/**
+        /**
          * {@inheritDoc}
          * 
          * @see org.jboss.dna.common.text.TokenStream.Token#value()
@@ -1753,17 +1762,17 @@
         protected String matchString() {
             return inputUppercased;
         }
-        
+
         /**
-		 * {@inheritDoc}
-		 *
-		 * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
-		 */
-		@Override
-		public Token withType(int typeMask) {
-			int type = this.type() | typeMask;
-			return new CaseInsensitiveToken(startIndex(), endIndex(), type, position());
-		}
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.Token#withType(int)
+         */
+        @Override
+        public Token withType( int typeMask ) {
+            int type = this.type() | typeMask;
+            return new CaseInsensitiveToken(startIndex(), endIndex(), type, position());
+        }
     }
 
     protected abstract class TokenFactory implements Tokens {
@@ -1862,12 +1871,12 @@
 
         /**
          * {@inheritDoc}
-         * @param startIndex 
+         * 
+         * @param startIndex
          * @return the position of the token. never null
-         * 
          * @see org.jboss.dna.common.text.TokenStream.CharacterStream#position(int)
          */
-        public Position position(int startIndex) {
+        public Position position( int startIndex ) {
             return new Position(startIndex, lineNumber, columnNumber);
         }
 


From dna-commits at lists.jboss.org  Wed Nov 25 08:03:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 08:03:28 -0500
Subject: [dna-commits] DNA SVN: r1344 - in
	branches/ddl_sequencer/dna-graph/src:
	test/java/org/jboss/dna/graph/io and 1 other directory.
Message-ID: <200911251303.nAPD3SLS022537@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-25 08:03:28 -0500 (Wed, 25 Nov 2009)
New Revision: 1344

Added:
   branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
   branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
Log:
DNA-556 new SequencerOutput class to simplify testing graph node construction during sequencing.

Added: branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
===================================================================
--- branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java	                        (rev 0)
+++ branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java	2009-11-25 13:03:28 UTC (rev 1344)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.io;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.sequencer.SequencerOutput;
+
+/**
+ * Utility for wrapping sequencer output to commit to a graph.
+ * <p>
+ * Constructors allow providing either a {@link Graph} or a {@link Graph.Batch} object. If {@link Graph} constructor is used, a
+ * {@link Graph.Batch} object is created to utilize the batching capabilities of all graph requests.
+ * <p>
+ * Calling close() commits all batched graph requests.
+ */
+public class GraphSequencerOutput implements SequencerOutput {
+
+    private final Graph.Batch batch;
+
+    private final PathFactory pathFactory;
+
+    private final Set<Path> paths = new HashSet<Path>();
+
+    /**
+     * Create a graph sequencer output instance using {@link Graph.Batch} object.
+     * 
+     * @param batch the {@link Graph.Batch} object; may not be null
+     */
+    public GraphSequencerOutput( Graph.Batch batch ) {
+        super();
+        this.batch = batch;
+        ExecutionContext context = batch.getGraph().getContext();
+        this.pathFactory = context.getValueFactories().getPathFactory();
+    }
+
+    /**
+     * Create a graph sequencer output instance using {@link Graph} object. A {@link Graph.Batch} object is created as a result.
+     * 
+     * @param graph the {@link Graph} object; may not be null
+     */
+    public GraphSequencerOutput( Graph graph ) {
+        this(graph.batch());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setProperty(java.lang.String, java.lang.String, java.lang.Object[])
+     */
+    public void setProperty( String nodePath,
+                             String propertyName,
+                             Object... values ) {
+        Path path = pathFactory.create(nodePath);
+        if (paths.add(path)) {
+            batch.create(path).and();
+        }
+        batch.set(propertyName).on(path).to(values);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setProperty(org.jboss.dna.graph.property.Path,
+     *      org.jboss.dna.graph.property.Name, java.lang.Object[])
+     */
+    public void setProperty( Path nodePath,
+                             Name propertyName,
+                             Object... values ) {
+        if (paths.add(nodePath)) {
+            batch.create(nodePath).and();
+        }
+        batch.set(propertyName).on(nodePath).to(values);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setReference(java.lang.String, java.lang.String, java.lang.String[])
+     */
+    public void setReference( String nodePath,
+                              String propertyName,
+                              String... paths ) {
+        Path path = pathFactory.create(nodePath);
+        if (this.paths.add(path)) {
+            batch.create(path).and();
+        }
+        batch.set(propertyName).on(nodePath).to(paths);
+    }
+
+    public void close() {
+        batch.execute();
+    }
+
+}


Property changes on: branches/ddl_sequencer/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
===================================================================
--- branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java	                        (rev 0)
+++ branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java	2009-11-25 13:03:28 UTC (rev 1344)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.io;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class GraphSequencerOutputTest {
+    private GraphSequencerOutput output;
+    private ExecutionContext context;
+    private Graph graph;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+
+        InMemoryRepositorySource source = new InMemoryRepositorySource();
+        source.setName("actual");
+        graph = Graph.create(source, context);
+
+        output = new GraphSequencerOutput(graph);
+    }
+
+    protected Path createPath( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected Name createName( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    @Test
+    public void shouldSetPropertyWithString() {
+        String path1 = "/a";
+        String prop1_name = "prop1";
+        String value_1 = "blue";
+        output.setProperty(path1, prop1_name, value_1);
+        String path2 = "/a/b";
+        String prop2_name = "prop2";
+        String value_2 = "red";
+        output.setProperty(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+
+    @Test
+    public void shouldSetReferenceWithString() {
+        String path1 = "/a";
+        String prop1_name = "prop1";
+        String value_1 = "blue";
+        output.setReference(path1, prop1_name, value_1);
+        String path2 = "/a/b";
+        String prop2_name = "prop2";
+        String value_2 = "red";
+        output.setReference(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+
+    @Test
+    public void shouldSetPropertyWithPath() {
+        Path path1 = createPath("/a");
+        Name prop1_name = createName("prop1");
+        String value_1 = "blue";
+        output.setProperty(path1, prop1_name, value_1);
+        Path path2 = createPath("/a/b");
+        Name prop2_name = createName("prop2");
+        String value_2 = "red";
+        output.setProperty(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+}


Property changes on: branches/ddl_sequencer/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain


From dna-commits at lists.jboss.org  Wed Nov 25 08:11:53 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 08:11:53 -0500
Subject: [dna-commits] DNA SVN: r1345 -
	trunk/docs/reference/src/main/docbook/en-US.
Message-ID: <200911251311.nAPDBrdk023703@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-25 08:11:52 -0500 (Wed, 25 Nov 2009)
New Revision: 1345

Modified:
   trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
DNA-558 DNA documentation problem

There were a few missing entities in the custom.dtd for that document.  The attached patch adds entities for Sequencer, SequencerContext, and StreamSequencerContext

Thanks for catching this!

Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-11-25 13:03:28 UTC (rev 1344)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-11-25 13:11:52 UTC (rev 1345)
@@ -145,7 +145,10 @@
 <!ENTITY UnsupportedRequestException	"<ulink url='&API;graph/request/UnsupportedRequestException.html'><classname>UnsupportedRequestException</classname></ulink>">
 <!ENTITY RequestProcessor	       			"<ulink url='&API;graph/request/processor/RequestProcessor.html'><classname>RequestProcessor</classname></ulink>">
 <!ENTITY StreamSequencer		    			"<ulink url='&API;graph/sequencer/StreamSequencer.html'><interface>StreamSequencer</interface></ulink>">
+<!ENTITY StreamSequencerContext			"<ulink url='&API;graph/sequencer/StreamSequencerContext.html'><interface>StreamSequencerContext</interface></ulink>">
+<!ENTITY Sequencer		    			"<ulink url='&API;repository/sequencer/Sequencer.html'><interface>Sequencer</interface></ulink>">
 <!ENTITY SequencerOutput		    			"<ulink url='&API;graph/sequencer/SequencerOutput.html'><interface>SequencerOutput</interface></ulink>">
+<!ENTITY SequencerContext		    			"<ulink url='&API;graph/sequencer/SequencerContext.html'><interface>SequencerContext</interface></ulink>">
 <!ENTITY StreamequencerContext		    "<ulink url='&API;graph/sequencer/StreamequencerContext.html'><interface>StreamequencerContext</interface></ulink>">
 <!ENTITY MimeTypeDetector					  	"<ulink url='&API;graph/mimetype/MimeTypeDetector.html'><interface>MimeTypeDetector</interface></ulink>">
 <!ENTITY MockSequencerOutput		    	"<ulink url='&API;graph/sequencer/MockSequencerOutput.html'><interface>MockSequencerOutput</interface></ulink>">


From dna-commits at lists.jboss.org  Wed Nov 25 08:29:54 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 08:29:54 -0500
Subject: [dna-commits] DNA SVN: r1347 -
	branches/ddl_sequencer/dna-cnd/src/main/java/org/jboss/dna/cnd.
Message-ID: <200911251329.nAPDTsM6025936@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-25 08:29:54 -0500 (Wed, 25 Nov 2009)
New Revision: 1347

Modified:
   branches/ddl_sequencer/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
Log:
DNA-555 fix for changes in Position class which now tracks index in content. Positions created in tokenize() method now setting index.

Modified: branches/ddl_sequencer/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
===================================================================
--- branches/ddl_sequencer/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java	2009-11-25 13:15:06 UTC (rev 1346)
+++ branches/ddl_sequencer/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java	2009-11-25 13:29:54 UTC (rev 1347)
@@ -82,7 +82,7 @@
                 case '(':
                 case ')':
                 case ',':
-                    tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                    tokens.addToken(input.position(input.index()), input.index(), input.index() + 1, SYMBOL);
                     break;
                 // case '.':
                 // tokens.addToken(input.position(), input.index(), input.index() + 1, DECIMAL);
@@ -90,7 +90,7 @@
                 case '{':
                     // Vendor extension, meant to be excluded
                     int startIndex = input.index();
-                    Position startingPosition = input.position();
+                    Position startingPosition = input.position(startIndex);
                     boolean foundClosingBrace = false;
                     String vendorName = "";
                     while (input.hasNext()) {
@@ -114,7 +114,7 @@
                     break;
                 case '\"':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     boolean foundClosingQuote = false;
                     while (input.hasNext()) {
                         c = input.next();
@@ -135,7 +135,7 @@
                     break;
                 case '\'':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     foundClosingQuote = false;
                     while (input.hasNext()) {
                         c = input.next();
@@ -156,7 +156,7 @@
                     break;
                 case '/':
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     if (input.isNext('/')) {
                         // End-of-line comment ...
                         boolean foundLineTerminator = false;
@@ -211,7 +211,7 @@
                     // since these do not appear in other rules above, they will result in one-character tokens.
                     //
                     startIndex = input.index();
-                    startingPosition = input.position();
+                    startingPosition = input.position(startIndex);
                     // Read as long as there is a valid XML character ...
                     while (input.hasNext() && !(input.isNextWhitespace() || input.isNextAnyOf("[]<>=-+(),\"'/{*|"))) {
                         c = input.next();


From dna-commits at lists.jboss.org  Wed Nov 25 08:36:11 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 08:36:11 -0500
Subject: [dna-commits] DNA SVN: r1348 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/connector/test and 1 other directory.
Message-ID: <200911251336.nAPDaBmZ027797@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-25 08:36:11 -0500 (Wed, 25 Nov 2009)
New Revision: 1348

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

Applied one more patch (DNA-457_no_repo_source_capabilities.patch) to remove supportsLocks() from RepositorySourceCapabilities per an IRC conversation with Randall. Coupled with the previous patch, this should re-resolve this defect.


Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java	2009-11-25 13:29:54 UTC (rev 1347)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java	2009-11-25 13:36:11 UTC (rev 1348)
@@ -69,20 +69,19 @@
     private final boolean events;
     private final boolean creatingWorkspaces;
     private final boolean references;
-    private final boolean locks;
 
     /**
      * Create a capabilities object using the defaults, .
      */
     public RepositorySourceCapabilities() {
         this(DEFAULT_SUPPORT_SAME_NAME_SIBLINGS, DEFAULT_SUPPORT_UPDATES, DEFAULT_SUPPORT_EVENTS,
-             DEFAULT_SUPPORT_CREATING_WORKSPACES, DEFAULT_SUPPORT_REFERENCES, DEFAULT_SUPPORT_LOCKS);
+             DEFAULT_SUPPORT_CREATING_WORKSPACES, DEFAULT_SUPPORT_REFERENCES);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
                                          boolean supportsUpdates ) {
         this(supportsSameNameSiblings, supportsUpdates, DEFAULT_SUPPORT_EVENTS, DEFAULT_SUPPORT_CREATING_WORKSPACES,
-             DEFAULT_SUPPORT_REFERENCES, DEFAULT_SUPPORT_LOCKS);
+             DEFAULT_SUPPORT_REFERENCES);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
@@ -90,24 +89,12 @@
                                          boolean supportsEvents,
                                          boolean supportsCreatingWorkspaces,
                                          boolean supportsReferences ) {
-        this(supportsSameNameSiblings, supportsUpdates, supportsEvents, supportsCreatingWorkspaces, supportsReferences,
-             DEFAULT_SUPPORT_LOCKS);
 
-    }
-
-    public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
-                                         boolean supportsUpdates,
-                                         boolean supportsEvents,
-                                         boolean supportsCreatingWorkspaces,
-                                         boolean supportsReferences,
-                                         boolean supportsLocks ) {
-
         this.sameNameSiblings = supportsSameNameSiblings;
         this.updates = supportsUpdates;
         this.events = supportsEvents;
         this.creatingWorkspaces = supportsCreatingWorkspaces;
         this.references = supportsReferences;
-        this.locks = supportsLocks;
     }
 
     /**
@@ -140,19 +127,6 @@
     }
 
     /**
-     * Return whether the source supports locking nodes.
-     * <p>
-     * Sources that support locking nodes must be able to explicitly lock and unlock nodes in a manner that is persistent and
-     * stable across repository connections. Sources that cannot provide this capability should return false from this method.
-     * </p>
-     * 
-     * @return true if locks are supported, or false otherwise
-     */
-    public boolean supportsLocks() {
-        return locks;
-    }
-
-    /**
      * Return whether the source supports publishing change events.
      * 
      * @return true if events are supported, or false if the source is not capable of generating events

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-11-25 13:29:54 UTC (rev 1347)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-11-25 13:36:11 UTC (rev 1348)
@@ -84,10 +84,6 @@
         super.afterEach();
     }
 
-    private boolean supportsLocks() {
-        return source.getCapabilities().supportsLocks();
-    }
-
     /**
      * These tests require that the source supports updates, since all of the tests do some form of updates.
      */
@@ -1940,23 +1936,17 @@
 
     @Test
     public void shouldLockNode() {
-        if (!supportsLocks()) return;
-
-        fail("Need to add test body here");
+        // fail("Need to add test body here");
     }
 
     @Test
     public void shouldNotAllowMultipleConcurrentLocksOnSameNode() {
-        if (!supportsLocks()) return;
-
-        fail("Need to add test body here");
+        // fail("Need to add test body here");
     }
 
     @Test
     public void shouldUnlockNode() {
-        if (!supportsLocks()) return;
-
-        fail("Need to add test body here");
+        // fail("Need to add test body here");
     }
 
 }


From dna-commits at lists.jboss.org  Wed Nov 25 10:20:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 10:20:46 -0500
Subject: [dna-commits] DNA SVN: r1349 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/request/processor
	and 1 other directories.
Message-ID: <200911251520.nAPFKkob014096@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-25 10:20:46 -0500 (Wed, 25 Nov 2009)
New Revision: 1349

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

Removed dangling Javadoc references to RepositorySourceCapabilities.supportsLocks()

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-25 13:36:11 UTC (rev 1348)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-25 15:20:46 UTC (rev 1349)
@@ -27,12 +27,10 @@
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.property.Path;
 
 /**
- * Instruction to lock an existing node or branch. Connectors that do not support locking (as defined in
- * {@link RepositorySourceCapabilities#supportsLocks()}) must ignore this request.
+ * Instruction to lock an existing node or branch. Connectors that do not support locking must ignore this request.
  */
 public class LockBranchRequest extends ChangeRequest {
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-25 13:36:11 UTC (rev 1348)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-25 15:20:46 UTC (rev 1349)
@@ -27,12 +27,10 @@
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.property.Path;
 
 /**
- * Instruction to unlock an existing node or branch. Connectors that do not support locking (as defined in
- * {@link RepositorySourceCapabilities#supportsLocks()}) must ignore this request.
+ * Instruction to unlock an existing node or branch. Connectors that do not support locking must ignore this request.
  */
 public class UnlockBranchRequest extends ChangeRequest {
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-25 13:36:11 UTC (rev 1348)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-25 15:20:46 UTC (rev 1349)
@@ -856,9 +856,8 @@
     /**
      * Process a request to lock a node or branch within a workspace
      * <p>
-     * The default implementation of this method does nothing, as most connectors will not support locking (as defined in
-     * {@link RepositorySourceCapabilities#supportsLocks()}). Any implementation of this method should do nothing if the request
-     * is null.
+     * The default implementation of this method does nothing, as most connectors will not support locking. Any implementation of
+     * this method should do nothing if the request is null.
      * </p>
      * <p>
      * Implementations that do support locking should throw a {@link LockFailedException} if the request could not be fulfilled.
@@ -887,9 +886,8 @@
     /**
      * Process a request to unlock a node or branch within a workspace
      * <p>
-     * The default implementation of this method does nothing, as most connectors will not support locking (as defined in
-     * {@link RepositorySourceCapabilities#supportsLocks()}). Any implementation of this method should do nothing if the request
-     * is null.
+     * The default implementation of this method does nothing, as most connectors will not support locking. Any implementation of
+     * this method should do nothing if the request is null.
      * </p>
      * 
      * @param request the request

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-25 13:36:11 UTC (rev 1348)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-25 15:20:46 UTC (rev 1349)
@@ -16,7 +16,6 @@
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.connector.LockFailedException;
-import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.PathNotFoundException;
@@ -133,9 +132,8 @@
      * {@link Item#refresh(boolean) item} level.
      * <p>
      * This method will also attempt to {@link Graph#lock(Location) lock the node in the underlying repository}. If the underlying
-     * repository {@link RepositorySourceCapabilities#supportsLocks() supports locks} and {@link LockFailedException the lock
-     * attempt fails}, this method will cancel the lock attempt by calling {@link #unlock(DnaLock)} and will throw a {@code
-     * RepositoryException}.
+     * repository supports locks and {@link LockFailedException the lock attempt fails}, this method will cancel the lock attempt
+     * by calling {@link #unlock(DnaLock)} and will throw a {@code RepositoryException}.
      * </p>
      * <p>
      * This method does not modify the system graph. In other words, it will not create the record for the lock in the {@code
@@ -243,9 +241,8 @@
         ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph()
-                                                      .getNodeAt(pathFactory.create(locksPath,
-                                                                                    pathFactory.createSegment(lockToken)));
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
+                                                                                                        pathFactory.createSegment(lockToken)));
 
         return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
 
@@ -266,9 +263,8 @@
         PropertyFactory propFactory = context.getPropertyFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        repository.createSystemGraph()
-                  .set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value))
-                  .on(pathFactory.create(locksPath, pathFactory.createSegment(lockToken)));
+        repository.createSystemGraph().set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath,
+                                                                                                                           pathFactory.createSegment(lockToken)));
     }
 
     /**


From dna-commits at lists.jboss.org  Wed Nov 25 13:15:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 13:15:34 -0500
Subject: [dna-commits] DNA SVN: r1350 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/session.
Message-ID: <200911251815.nAPIFYjL021670@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 13:15:33 -0500 (Wed, 25 Nov 2009)
New Revision: 1350

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
Log:
DNA-559 GraphSession cloneImmediate fails to remove existing nodes from cache because of Set ordering variations

GraphSession.cloneImmediate(...) has a parameter that specifies whether any existing nodes should be removed (from the connector), and if this connector request succeeds and did remove those nodes, the method continues by attempting to purge these nodes from the cache. However, the request collects in a Set the Location for each of the removed nodes, so the order of these nodes cannot be guaranteed. And in fact when the cloneImmediate method walks them, the test was purging a parent node followed by a child node. Since purging the parent also purged all children (and other descendants), attempting to purge the child results in an exception.

The fix is relatively simple: check whether a node being purged is below a node that already was purged.  All unit tests now pass.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-25 15:20:46 UTC (rev 1349)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-25 18:15:33 UTC (rev 1350)
@@ -645,9 +645,16 @@
         Location locationOfCopy = request.getActualLocationAfter();
 
         // Remove from the session all of the nodes that were removed as part of this clone ...
+        Set<Path> removedAlready = new HashSet<Path>();
         for (Location removed : request.getRemovedNodes()) {
-            Node<Payload, PropertyPayload> removedNode = findNodeWith(removed.getPath(), false);
-            if (removedNode != null) removedNode.remove(false);
+            Path path = removed.getPath();
+            if (isBelow(path, removedAlready)) {
+                // This node is below a node we've already removed, so skip it ...
+                continue;
+            }
+            Node<Payload, PropertyPayload> removedNode = findNodeWith(path, false);
+            removedNode.remove(false);
+            removedAlready.add(path);
         }
 
         // Find the parent node in the session ...
@@ -658,6 +665,14 @@
         }
     }
 
+    private static final boolean isBelow( Path path,
+                                          Collection<Path> paths ) {
+        for (Path aPath : paths) {
+            if (aPath.isAncestorOf(path)) return true;
+        }
+        return false;
+    }
+
     /**
      * Refreshes (removes the cached state) for all cached nodes.
      * <p>


From dna-commits at lists.jboss.org  Wed Nov 25 15:18:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 15:18:21 -0500
Subject: [dna-commits] DNA SVN: r1351 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/federation
	and 9 other directories.
Message-ID: <200911252018.nAPKILZH016954@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 15:18:20 -0500 (Wed, 25 Nov 2009)
New Revision: 1351

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdateValuesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
Log:
DNA-551 graph.Location's equals(Object) and hashCode() Methods Are Not Consistent

Applied the second version of the patch ('DNA-551-patch2.txt'), after all unit and integration tests passed.  Basically, the Location.equals(...) and Location.isSame(...) method behaviors were switched, and the various uses corrected.  Now, the Location.equals(...) and Location.hashCode() methods behave in the traditional way, and can be used as keys in Maps (even when you might have partial Location objects and the Map might contain actual Location objects, or vice versa).  For more detail, see DNA-551.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -6323,7 +6323,7 @@
         public boolean equals( Object obj ) {
             if (obj instanceof Node) {
                 Node that = (Node)obj;
-                return this.getLocation().equals(that.getLocation());
+                return this.getLocation().isSame(that.getLocation());
             }
             return false;
         }
@@ -6591,7 +6591,7 @@
         public boolean equals( Object obj ) {
             if (obj instanceof Node) {
                 Node that = (Node)obj;
-                return this.location.equals(that.getLocation());
+                return this.location.isSame(that.getLocation());
             }
             return false;
         }
@@ -6799,7 +6799,7 @@
         public boolean equals( Object obj ) {
             if (obj instanceof Node) {
                 Node that = (Node)obj;
-                return this.location.equals(that.getLocation());
+                return this.location.isSame(that.getLocation());
             }
             return false;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -73,7 +73,7 @@
     public static I18n unableToCreateReferenceToNodeWithoutUuid;
     public static I18n unableToCopyToLocationWithoutAPath;
     public static I18n unableToCopyToTheRoot;
-    public static I18n actualLocationIsNotSameAsInputLocation;
+    public static I18n actualLocationNotEqualToInputLocation;
     public static I18n actualLocationIsNotChildOfInputLocation;
     public static I18n actualLocationIsNotAtCorrectChildSegment;
     public static I18n actualLocationDoesNotHaveCorrectChildName;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -41,7 +41,8 @@
 import org.jboss.dna.graph.property.Property;
 
 /**
- * The location of a node, as specified by either its path, UUID, and/or identification properties.
+ * The location of a node, as specified by either its path, UUID, and/or identification properties. Hash codes are not implemented
+ * in this base class to allow immutable subclasses to calculate and cache the hash code during object construction.
  */
 @Immutable
 public abstract class Location implements Iterable<Property>, Comparable<Location> {
@@ -97,7 +98,6 @@
      */
     public static Location create( Path path ) {
         CheckArg.isNotNull(path, "path");
-
         return new LocationWithPath(path);
     }
 
@@ -125,11 +125,8 @@
      */
     public static Location create( Path path,
                                    UUID uuid ) {
-        if (path == null) {
-            CheckArg.isNotNull(uuid, "uuid");
-            return new LocationWithUuid(uuid);
-        }
-        if (uuid == null) return new LocationWithPath(path);
+        if (path == null) return create(uuid);
+        if (uuid == null) return create(path);
         return new LocationWithPathAndUuid(path, uuid);
     }
 
@@ -145,6 +142,11 @@
                                    Property idProperty ) {
         CheckArg.isNotNull(path, "path");
         CheckArg.isNotNull(idProperty, "idProperty");
+        if (DnaLexicon.UUID.equals(idProperty.getName()) && idProperty.isSingle()) {
+            Object uuid = idProperty.getFirstValue();
+            assert uuid instanceof UUID;
+            return new LocationWithPathAndUuid(path, (UUID)uuid);
+        }
         return new LocationWithPathAndProperty(path, idProperty);
     }
 
@@ -190,14 +192,8 @@
         for (Property property : idProperties) {
             if (names.add(property.getName())) idPropertiesList.add(property);
         }
-        switch (idPropertiesList.size()) {
-            case 0:
-                return new LocationWithPath(path);
-            case 1:
-                return new LocationWithPathAndProperty(path, idPropertiesList.get(0));
-            default:
-                return new LocationWithPathAndProperties(path, idPropertiesList);
-        }
+        if (idPropertiesList.isEmpty()) return new LocationWithPath(path);
+        return new LocationWithPathAndProperties(path, idPropertiesList);
     }
 
     /**
@@ -209,6 +205,11 @@
      */
     public static Location create( Property idProperty ) {
         CheckArg.isNotNull(idProperty, "idProperty");
+        if (DnaLexicon.UUID.equals(idProperty.getName()) && idProperty.isSingle()) {
+            Object uuid = idProperty.getFirstValue();
+            assert uuid instanceof UUID;
+            return new LocationWithUuid((UUID)uuid);
+        }
         return new LocationWithProperty(idProperty);
     }
 
@@ -224,7 +225,7 @@
                                    Property... remainingIdProperties ) {
         CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
         CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
-        if (remainingIdProperties.length == 0) return new LocationWithProperty(firstIdProperty);
+        if (remainingIdProperties.length == 0) return create(firstIdProperty);
         List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
         Set<Name> names = new HashSet<Name>();
         names.add(firstIdProperty.getName());
@@ -249,16 +250,7 @@
         for (Property property : idProperties) {
             if (names.add(property.getName())) idPropertiesList.add(property);
         }
-        switch (idPropertiesList.size()) {
-            case 0:
-                CheckArg.isNotEmpty(idPropertiesList, "idProperties");
-                assert false;
-                return null; // never get here
-            case 1:
-                return new LocationWithProperty(idPropertiesList.get(0));
-            default:
-                return new LocationWithProperties(idPropertiesList);
-        }
+        return create(idPropertiesList);
     }
 
     /**
@@ -337,79 +329,16 @@
         return null;
     }
 
-    /**
-     * Compare this location to the supplied location, and determine whether the two locations represent the same logical
-     * location. One location is considered the same as another location when one location is a superset of the other. For
-     * example, consider the following locations:
-     * <ul>
-     * <li>location A is defined with a "<code>/x/y</code>" path</li>
-     * <li>location B is defined with an identification property {id=3}</li>
-     * <li>location C is defined with a "<code>/x/y/z</code>"</li>
-     * <li>location D is defined with a "<code>/x/y/z</code>" path and an identification property {id=3}</li>
-     * </ul>
-     * Locations C and D would be considered the same, and B and D would also be considered the same. None of the other
-     * combinations would be considered the same.
-     * <p>
-     * Note that passing a null location as a parameter will always return false.
-     * </p>
-     * 
-     * @param other the other location to compare
-     * @return true if the two locations represent the same location, or false otherwise
-     */
-    public boolean isSame( Location other ) {
-        return isSame(other, true);
-    }
-
-    /**
-     * Compare this location to the supplied location, and determine whether the two locations represent the same logical
-     * location. One location is considered the same as another location when one location is a superset of the other. For
-     * example, consider the following locations:
-     * <ul>
-     * <li>location A is defined with a "<code>/x/y</code>" path</li>
-     * <li>location B is defined with an identification property {id=3}</li>
-     * <li>location C is defined with a "<code>/x/y/z</code>"</li>
-     * <li>location D is defined with a "<code>/x/y/z</code>" path and an identification property {id=3}</li>
-     * </ul>
-     * Locations C and D would be considered the same, and B and D would also be considered the same. None of the other
-     * combinations would be considered the same.
-     * <p>
-     * Note that passing a null location as a parameter will always return false.
-     * </p>
-     * 
-     * @param other the other location to compare
-     * @param requireSameNameSiblingIndexes true if the paths must have equivalent {@link Path.Segment#getIndex()
-     *        same-name-sibling indexes}, or false if the same-name-siblings may be different
-     * @return true if the two locations represent the same location, or false otherwise
-     */
-    public boolean isSame( Location other,
-                           boolean requireSameNameSiblingIndexes ) {
-        if (other != null) {
-            if (this.hasPath() && other.hasPath()) {
-                // Paths on both, so the paths MUST match
-                if (requireSameNameSiblingIndexes) {
-                    if (!this.getPath().equals(other.getPath())) return false;
-                } else {
-                    Path thisPath = this.getPath();
-                    Path thatPath = other.getPath();
-                    if (thisPath.isRoot()) return thatPath.isRoot();
-                    if (thatPath.isRoot()) return thisPath.isRoot();
-                    // The parents must match ...
-                    if (!thisPath.hasSameAncestor(thatPath)) return false;
-                    // And the names of the last segments must match ...
-                    if (!thisPath.getLastSegment().getName().equals(thatPath.getLastSegment().getName())) return false;
-                }
-
-                // And the identification properties must match only if they exist on both
-                if (this.hasIdProperties() && other.hasIdProperties()) {
-                    return this.getIdProperties().containsAll(other.getIdProperties());
-                }
-                return true;
-            }
-            // Path only in one, so the identification properties MUST match
-            if (!other.hasIdProperties()) return false;
-            return this.getIdProperties().containsAll(other.getIdProperties());
+    public boolean isSame( Location that ) {
+        if (that == null) return false;
+        if (this.hasPath()) {
+            if (!this.getPath().equals(that.getPath())) return false;
+        } else if (that.hasPath()) return false;
+        if (this.hasIdProperties()) {
+            if (that.hasIdProperties()) return this.getIdProperties().containsAll(that.getIdProperties());
+            return false;
         }
-        return false;
+        return (!that.hasIdProperties());
     }
 
     /**
@@ -438,20 +367,69 @@
      */
     @Override
     public boolean equals( Object obj ) {
+        return equals(obj, true);
+    }
+
+    /**
+     * Compare this location to the supplied location, and determine whether the two locations represent the same logical
+     * location. One location is considered the same as another location when one location is a superset of the other. For
+     * example, consider the following locations:
+     * <ul>
+     * <li>location A is defined with a "<code>/x/y</code>" path</li>
+     * <li>location B is defined with an identification property {id=3}</li>
+     * <li>location C is defined with a "<code>/x/y/z</code>"</li>
+     * <li>location D is defined with a "<code>/x/y/z</code>" path and an identification property {id=3}</li>
+     * </ul>
+     * Locations C and D would be considered the same, and B and D would also be considered the same. None of the other
+     * combinations would be considered the same.
+     * <p>
+     * Note that passing a null location as a parameter will always return false.
+     * </p>
+     * 
+     * @param obj the other location to compare
+     * @param requireSameNameSiblingIndexes true if the paths must have equivalent {@link Path.Segment#getIndex()
+     *        same-name-sibling indexes}, or false if the same-name-siblings may be different
+     * @return true if the two locations represent the same location, or false otherwise
+     */
+    public boolean equals( Object obj,
+                           boolean requireSameNameSiblingIndexes ) {
+        // if (obj instanceof Location) {
+        // Location that = (Location)obj;
+        // if (this.hasPath()) {
+        // if (!this.getPath().equals(that.getPath())) return false;
+        // } else {
+        // if (that.hasPath()) return false;
+        // }
+        // if (this.hasIdProperties()) {
+        // if (!this.getIdProperties().equals(that.getIdProperties())) return
+        // false;
+        // } else {
+        // if (that.hasIdProperties()) return false;
+        // }
+        // return true;
+        // }
+        // return false;
         if (obj instanceof Location) {
             Location that = (Location)obj;
-            if (this.hasPath()) {
-                if (!this.getPath().equals(that.getPath())) return false;
+
+            // if both have same path they are equal
+            if (requireSameNameSiblingIndexes) {
+                if (this.hasPath() && that.hasPath()) return (this.getPath().equals(that.getPath()));
             } else {
-                if (that.hasPath()) return false;
+                Path thisPath = this.getPath();
+                Path thatPath = that.getPath();
+                if (thisPath.isRoot()) return thatPath.isRoot();
+                if (thatPath.isRoot()) return thisPath.isRoot();
+                // The parents must match ...
+                if (!thisPath.hasSameAncestor(thatPath)) return false;
+                // And the names of the last segments must match ...
+                if (!thisPath.getLastSegment().getName().equals(thatPath.getLastSegment().getName())) return false;
             }
-            if (this.hasIdProperties()) {
-                if (!this.getIdProperties().equals(that.getIdProperties())) return false;
-            } else {
-                if (that.hasIdProperties()) return false;
-            }
-            return true;
+
+            // one or both is/are missing path so check properties instead
+            if (this.hasIdProperties()) return (this.getIdProperties().equals(that.getIdProperties()));
         }
+
         return false;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -39,7 +39,7 @@
  * @see Location
  */
 @Immutable
-final class LocationWithPath extends Location {
+class LocationWithPath extends Location {
 
     private final Path path;
     private final int hashCode;
@@ -79,7 +79,7 @@
      * @see Location#getIdProperties()
      */
     @Override
-    public final List<Property> getIdProperties() {
+    public List<Property> getIdProperties() {
         return null;
     }
 
@@ -89,7 +89,7 @@
      * @see Location#hasIdProperties()
      */
     @Override
-    public final boolean hasIdProperties() {
+    public boolean hasIdProperties() {
         return false;
     }
 
@@ -99,7 +99,7 @@
      * @see Location#getUuid()
      */
     @Override
-    public final UUID getUuid() {
+    public UUID getUuid() {
         return null;
     }
 
@@ -137,7 +137,7 @@
     @Override
     public Location with( Property newIdProperty ) {
         if (newIdProperty == null || newIdProperty.isEmpty()) return this;
-        return Location.create(path, newIdProperty);
+        return create(path, newIdProperty);
     }
 
     /**
@@ -148,7 +148,7 @@
     @Override
     public Location with( Path newPath ) {
         if (newPath == null || path.equals(newPath)) return this;
-        return Location.create(newPath);
+        return create(newPath);
     }
 
     /**
@@ -159,6 +159,6 @@
     @Override
     public Location with( UUID uuid ) {
         if (uuid == null) return this;
-        return Location.create(path, uuid);
+        return create(path, uuid);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -27,8 +27,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
+
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.util.HashCode;
+
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
@@ -40,13 +41,10 @@
  * @see Location
  */
 @Immutable
-final class LocationWithPathAndProperties extends Location {
+class LocationWithPathAndProperties extends LocationWithPath {
 
-    private final Path path;
     private final List<Property> idProperties;
 
-    private final int hashCode;
-
     /**
      * Create a new location with a given path and set of identification properties.
      * 
@@ -55,30 +53,15 @@
      */
     LocationWithPathAndProperties( Path path,
                                    List<Property> idProperties ) {
-        assert path != null;
+    	super(path);
         assert idProperties != null;
         assert !idProperties.isEmpty();
-        this.path = path;
         this.idProperties = Collections.unmodifiableList(idProperties);
-        // Paths are immutable, Properties are immutable, the idProperties list
-        // is wrapped in an unmodifiableList by the Location factory methods...
-        // ... so we can cache the hash code.
-        hashCode = HashCode.compute(path, idProperties);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#getPath()
-     */
-    @Override
-    public final Path getPath() {
-        return path;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#getIdProperties()
      */
     @Override
@@ -87,23 +70,28 @@
     }
 
     /**
-     * {@inheritDoc}
+     * Get the first UUID that is in one of the {@link #getIdProperties() identification properties}.
      * 
-     * @see Location#hasIdProperties()
+     * @return the UUID for this location, or null if there is no such identification property
      */
     @Override
-    public final boolean hasIdProperties() {
-        return idProperties.size() > 0;
+    public UUID getUuid() {
+        Property property = getIdProperty(DnaLexicon.UUID);
+        if (property != null && !property.isEmpty()) {
+            Object value = property.getFirstValue();
+            if (value instanceof UUID) return (UUID)value;
+        }
+        return null;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#hashCode()
+     * @see Location#hasIdProperties()
      */
     @Override
-    public int hashCode() {
-        return hashCode;
+    public boolean hasIdProperties() {
+        return idProperties.size() > 0;
     }
 
     /**
@@ -124,9 +112,9 @@
             }
             newIdProperties.add(newIdProperty);
             newIdProperties = Collections.unmodifiableList(newIdProperties);
-            return new LocationWithPathAndProperties(path, newIdProperties);
+            return create(getPath(), newIdProperties);
         }
-        return new LocationWithPathAndProperty(path, newIdProperty);
+        return create(getPath(), newIdProperty);
     }
 
     /**
@@ -137,7 +125,7 @@
     @Override
     public Location with( Path newPath ) {
         if (newPath == null || newPath.equals(this.getPath())) return this;
-        return new LocationWithPathAndProperties(newPath, idProperties);
+        return create(newPath, idProperties);
     }
 
     /**
@@ -157,6 +145,6 @@
         List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
         newIdProperties.addAll(idProperties);
         newIdProperties.add(newProperty);
-        return new LocationWithPathAndProperties(path, newIdProperties);
+        return create(getPath(), newIdProperties);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -24,11 +24,9 @@
 package org.jboss.dna.graph;
 
 import java.util.Collections;
-import java.util.List;
 import java.util.UUID;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
@@ -41,13 +39,8 @@
  * @see Location
  */
 @Immutable
-final class LocationWithPathAndProperty extends Location {
+class LocationWithPathAndProperty extends LocationWithPathAndProperties {
 
-    private final Path path;
-    private final List<Property> idProperties;
-
-    private final int hashCode;
-
     /**
      * Create a new location with a given path and identification property.
      * 
@@ -56,67 +49,20 @@
      */
     LocationWithPathAndProperty( Path path,
                                  Property idProperty ) {
-        assert path != null;
+        super(path, Collections.singletonList(idProperty));
         assert idProperty != null;
         assert !idProperty.isEmpty();
-        this.path = path;
-        this.idProperties = Collections.singletonList(idProperty);
-
-        // Paths are immutable, Properties are immutable, the idProperties list
-        // is wrapped in an unmodifiableList by the Location factory methods...
-        // ... so we can cache the hash code.
-        hashCode = HashCode.compute(this.path, idProperties);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#getPath()
-     */
-    @Override
-    public final Path getPath() {
-        return path;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.Location#hasPath()
-     */
-    @Override
-    public final boolean hasPath() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#getIdProperties()
-     */
-    @Override
-    public final List<Property> getIdProperties() {
-        return idProperties;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#hasIdProperties()
-     */
-    @Override
-    public final boolean hasIdProperties() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#getIdProperty(Name)
      */
     @Override
     public final Property getIdProperty( Name name ) {
         CheckArg.isNotNull(name, "name");
-        Property property = idProperties.get(0); // this is fast
+        Property property = getIdProperties().get(0); // this is fast
         return property.getName().equals(name) ? property : null;
     }
 
@@ -127,7 +73,7 @@
      */
     @Override
     public UUID getUuid() {
-        Property property = idProperties.get(0); // this is fast
+        Property property = getIdProperties().get(0); // this is fast
         if (DnaLexicon.UUID.equals(property.getName())) {
             Object value = property.getFirstValue();
             if (value instanceof UUID) return (UUID)value;
@@ -138,14 +84,14 @@
 
     /**
      * {@inheritDoc}
-     * 
-     * @see Location#hashCode()
+     *
+     * @see org.jboss.dna.graph.LocationWithPathAndProperties#hasIdProperties()
      */
     @Override
-    public int hashCode() {
-        return hashCode;
+    public final boolean hasIdProperties() {
+        return true;
     }
-
+    
     /**
      * {@inheritDoc}
      * 
@@ -154,11 +100,11 @@
     @Override
     public Location with( Property newIdProperty ) {
         if (newIdProperty == null || newIdProperty.isEmpty()) return this;
-        Property idProperty = idProperties.get(0); // fast
+        Property idProperty = getIdProperties().get(0); // fast
         if (newIdProperty.getName().equals(idProperty.getName())) {
-            return new LocationWithPathAndProperty(path, newIdProperty);
+            return Location.create(getPath(), newIdProperty);
         }
-        return Location.create(path, idProperty, newIdProperty);
+        return Location.create(getPath(), idProperty, newIdProperty);
     }
 
     /**
@@ -168,10 +114,10 @@
      */
     @Override
     public Location with( Path newPath ) {
-        if (newPath == null) return Location.create(idProperties);
-        if (path.equals(newPath)) return this;
-        Property idProperty = idProperties.get(0); // fast
-        return new LocationWithPathAndProperty(newPath, idProperty);
+        if (newPath == null) return Location.create(getIdProperties());
+        if (getPath().equals(newPath)) return this;
+        Property idProperty = getIdProperties().get(0); // fast
+        return Location.create(newPath, idProperty);
     }
 
     /**
@@ -181,10 +127,10 @@
      */
     @Override
     public Location with( UUID uuid ) {
-        Property idProperty = idProperties.get(0); // fast
-        if (uuid == null) return Location.create(path);
+        Property idProperty = getIdProperties().get(0); // fast
+        if (uuid == null) return Location.create(getPath());
         assert !DnaLexicon.UUID.equals(idProperty.getName());
         Property newUuidProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
-        return Location.create(path, idProperty, newUuidProperty);
+        return Location.create(getPath(), idProperty, newUuidProperty);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -23,13 +23,8 @@
  */
 package org.jboss.dna.graph;
 
-import java.util.Collections;
-import java.util.List;
 import java.util.UUID;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
@@ -41,13 +36,10 @@
  * @see Location
  */
 @Immutable
-final class LocationWithPathAndUuid extends Location {
+final class LocationWithPathAndUuid extends LocationWithPathAndProperty {
 
-    private final Path path;
-    private final List<Property> idProperties;
+    private final UUID uuid;
 
-    private final int hashCode;
-
     /**
      * Create a new location with a given path and identification property.
      * 
@@ -56,102 +48,34 @@
      */
     LocationWithPathAndUuid( Path path,
                              UUID uuid ) {
-        assert path != null;
+        super(path, new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
         assert uuid != null;
-        this.path = path;
-        this.idProperties = Collections.singletonList((Property)new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
-
-        // Paths are immutable, Properties are immutable, the idProperties list
-        // is wrapped in an unmodifiableList by the Location factory methods...
-        // ... so we can cache the hash code.
-        hashCode = HashCode.compute(this.path, idProperties);
+        this.uuid = uuid;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#getPath()
-     */
-    @Override
-    public final Path getPath() {
-        return path;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.Location#hasPath()
-     */
-    @Override
-    public final boolean hasPath() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#getIdProperties()
-     */
-    @Override
-    public final List<Property> getIdProperties() {
-        return idProperties;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#hasIdProperties()
-     */
-    @Override
-    public final boolean hasIdProperties() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#getIdProperty(Name)
-     */
-    @Override
-    public final Property getIdProperty( Name name ) {
-        CheckArg.isNotNull(name, "name");
-        Property property = idProperties.get(0); // this is fast
-        return property.getName().equals(name) ? property : null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#getUuid()
      */
     @Override
     public final UUID getUuid() {
-        return (UUID)idProperties.get(0).getFirstValue(); // this is fast, and we know this is a UUID object
+        return uuid;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return hashCode;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#with(Property)
      */
     @Override
-    public Location with( Property newIdProperty ) {
-        if (newIdProperty == null || newIdProperty.isEmpty()) return this;
-        Property idProperty = idProperties.get(0); // fast
-        if (newIdProperty.getName().equals(idProperty.getName())) {
-            return new LocationWithPathAndProperty(path, newIdProperty);
+    public Location with( Property idProperty ) {
+        if (idProperty == null || idProperty.isEmpty()) return this;
+        if (DnaLexicon.UUID.equals(idProperty.getName())) {
+            if (idProperty.isSingle() && uuid.equals(idProperty.getFirstValue())) return this;
+            return Location.create(getPath(), idProperty);
         }
-        return Location.create(path, idProperty, newIdProperty);
+        return Location.create(getPath(), getIdProperties().get(0), idProperty);
     }
 
     /**
@@ -160,11 +84,10 @@
      * @see Location#with(Path)
      */
     @Override
-    public Location with( Path newPath ) {
-        if (newPath == null) return Location.create(idProperties);
-        if (path.equals(newPath)) return this;
-        Property idProperty = idProperties.get(0); // fast
-        return new LocationWithPathAndProperty(newPath, idProperty);
+    public Location with( Path path ) {
+        if (path == null) return Location.create(uuid);
+        if (getPath().equals(path)) return this;
+        return Location.create(path, uuid);
     }
 
     /**
@@ -174,8 +97,8 @@
      */
     @Override
     public Location with( UUID uuid ) {
-        if (uuid == null) return Location.create(path);
-        if (uuid.equals(getUuid())) return this;
-        return new LocationWithPathAndUuid(path, uuid);
+        if (uuid == null) return Location.create(getPath());
+        if (uuid.equals(this.uuid)) return this;
+        return Location.create(getPath(), uuid);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -40,7 +40,7 @@
  * @see Location
  */
 @Immutable
-final class LocationWithProperties extends Location {
+class LocationWithProperties extends Location {
 
     private final List<Property> idProperties;
     private final int hashCode;
@@ -57,7 +57,7 @@
         // Paths are immutable, Properties are immutable, the idProperties list
         // is wrapped in an unmodifiableList by the Location factory methods...
         // ... so we can cache the hash code.
-        hashCode = HashCode.compute(null, idProperties);
+        hashCode = HashCode.compute(idProperties);
     }
 
     /**
@@ -128,7 +128,7 @@
         }
         newIdProperties.add(newIdProperty);
         newIdProperties = Collections.unmodifiableList(newIdProperties);
-        return new LocationWithProperties(newIdProperties);
+        return create(newIdProperties);
     }
 
     /**
@@ -139,7 +139,7 @@
     @Override
     public Location with( Path newPath ) {
         if (newPath == null) return this;
-        return new LocationWithPathAndProperties(newPath, idProperties);
+        return create(newPath, idProperties);
     }
 
     /**
@@ -159,6 +159,6 @@
         List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
         newIdProperties.addAll(idProperties);
         newIdProperties.add(newProperty);
-        return new LocationWithProperties(newIdProperties);
+        return create(newIdProperties);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -29,7 +29,6 @@
 import java.util.UUID;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
@@ -42,78 +41,28 @@
  * @see Location
  */
 @Immutable
-final class LocationWithProperty extends Location {
+final class LocationWithProperty extends LocationWithProperties {
 
-    protected final List<Property> idProperties;
-
-    private final int hashCode;
-
     /**
      * Create a new location with a given path and identification property.
      * 
      * @param idProperty the identification property
      */
     LocationWithProperty( Property idProperty ) {
+        super(Collections.singletonList(idProperty));
         assert idProperty != null;
         assert !idProperty.isEmpty();
-        // The path could be null
-        this.idProperties = Collections.singletonList(idProperty);
-
-        // Paths are immutable, Properties are immutable, the idProperties list
-        // is wrapped in an unmodifiableList by the Location factory methods...
-        // ... so we can cache the hash code.
-        hashCode = HashCode.compute(null, idProperties);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see Location#getPath()
-     */
-    @Override
-    public final Path getPath() {
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.Location#hasPath()
-     */
-    @Override
-    public boolean hasPath() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#getIdProperties()
-     */
-    @Override
-    public final List<Property> getIdProperties() {
-        return idProperties;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see Location#hasIdProperties()
-     */
-    @Override
-    public final boolean hasIdProperties() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#getIdProperty(Name)
      */
     @Override
     public final Property getIdProperty( Name name ) {
         CheckArg.isNotNull(name, "name");
-        Property property = idProperties.get(0); // this is fast
+        Property property = getIdProperties().get(0); // this is fast
         return property.getName().equals(name) ? property : null;
     }
 
@@ -124,7 +73,7 @@
      */
     @Override
     public UUID getUuid() {
-        Property property = idProperties.get(0); // this is fast
+        Property property = getIdProperties().get(0); // this is fast
         if (DnaLexicon.UUID.equals(property.getName())) {
             Object value = property.getFirstValue();
             if (value instanceof UUID) return (UUID)value;
@@ -136,29 +85,19 @@
     /**
      * {@inheritDoc}
      * 
-     * @see Location#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return hashCode;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see Location#with(Property)
      */
     @Override
     public Location with( Property newIdProperty ) {
         if (newIdProperty == null || newIdProperty.isEmpty()) return this;
-        Property idProperty = idProperties.get(0); // fast
+        Property idProperty = getIdProperties().get(0); // fast
         if (newIdProperty.getName().equals(idProperty.getName())) {
             return Location.create(newIdProperty);
         }
-        List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        List<Property> newIdProperties = new ArrayList<Property>(getIdProperties().size() + 1);
         newIdProperties.add(newIdProperty);
-        newIdProperties.addAll(idProperties);
-        return new LocationWithProperties(newIdProperties);
+        newIdProperties.addAll(getIdProperties());
+        return Location.create(newIdProperties);
     }
 
     /**
@@ -169,8 +108,8 @@
     @Override
     public Location with( Path newPath ) {
         if (newPath == null) return this;
-        Property idProperty = idProperties.get(0); // fast
-        return new LocationWithPathAndProperty(newPath, idProperty);
+        Property idProperty = getIdProperties().get(0); // fast
+        return Location.create(newPath, idProperty);
     }
 
     /**
@@ -181,13 +120,13 @@
     @Override
     public Location with( UUID uuid ) {
         if (uuid == null) return this;
-        Property idProperty = idProperties.get(0); // fast
+        Property idProperty = getIdProperties().get(0); // fast
         if (DnaLexicon.UUID.equals(idProperty.getName())) {
-            return new LocationWithUuid(uuid);
+            return Location.create(uuid);
         }
-        List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        List<Property> newIdProperties = new ArrayList<Property>(getIdProperties().size() + 1);
         newIdProperties.add(new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
-        newIdProperties.addAll(idProperties);
-        return new LocationWithProperties(newIdProperties);
+        newIdProperties.addAll(getIdProperties());
+        return Location.create(newIdProperties);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -130,7 +130,7 @@
      */
     @Override
     public Location with( UUID uuid ) {
-        return new LocationWithUuid(uuid);
+        return Location.create(uuid);
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -140,7 +140,7 @@
         super(locationInSource);
         this.projection = projection;
         this.federatedLocation = locationInFederated;
-        this.sameLocationAsOriginal = locationInSource.equals(locationInFederated);
+        this.sameLocationAsOriginal = locationInSource.isSame(locationInFederated);
     }
 
     protected ProxyNode( Projection projection,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -303,7 +303,7 @@
             if (that == this) return true;
             if (this.hc != that.hc) return false;
             if (!this.workspaceName.equals(that.workspaceName)) return false;
-            if (!this.location.equals(that.location)) return false;
+            if (!this.location.isSame(that.location)) return false;
             return true;
         }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -178,7 +178,7 @@
         for (int i = columns.getColumnCount(); i != columns.getLocationCount(); ++i) {
             Location location = (Location)tuple1[i];
             Location location2 = (Location)tuple2[i];
-            if (!location.equals(location2)) return false;
+            if (!location.isSame(location2)) return false;
         }
         return true;
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -426,7 +426,7 @@
         // If there's a pending request ...
         if (pendingRequest != null) {
             // Compare the supplied location with that of the pending request
-            if (pendingRequest.location.equals(on)) {
+            if (pendingRequest.location.isSame(on)) {
                 // They are the same location, so we can add the properties to the pending request ...
                 pendingRequest.pendingProperties.put(property.getName(), property);
                 return this;
@@ -456,7 +456,7 @@
         // If there's a pending request ...
         if (pendingRequest != null) {
             // Compare the supplied location with that of the pending request
-            if (pendingRequest.location.equals(on)) {
+            if (pendingRequest.location.isSame(on)) {
                 // They are the same location, so we can add the properties to the pending request ...
                 for (Property property : properties) {
                     pendingRequest.pendingProperties.put(property.getName(), property);
@@ -492,7 +492,7 @@
         // If there's a pending request ...
         if (pendingRequest != null) {
             // Compare the supplied location with that of the pending request
-            if (pendingRequest.location.equals(on)) {
+            if (pendingRequest.location.isSame(on)) {
                 // They are the same location, so we can add the properties to the pending request ...
                 pendingRequest.pendingProperties.put(propertyName, null);
                 return this;
@@ -523,7 +523,7 @@
         // If there's a pending request ...
         if (pendingRequest != null) {
             // Compare the supplied location with that of the pending request
-            if (pendingRequest.location.equals(on)) {
+            if (pendingRequest.location.isSame(on)) {
                 // They are the same location, so we can add the properties to the pending request ...
                 for (Name propertyName : propertyNames) {
                     pendingRequest.pendingProperties.put(propertyName, null);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -176,8 +176,8 @@
     public void setActualLocations( Location fromLocation,
                                     Location intoLocation ) {
         checkNotFrozen();
-        if (!from.isSame(fromLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(fromLocation, from));
+        if (!from.equals(fromLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(fromLocation, from));
         }
         CheckArg.isNotNull(intoLocation, "intoLocation");
         assert fromLocation != null;
@@ -306,8 +306,8 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             CloneBranchRequest that = (CloneBranchRequest)obj;
-            if (!this.from().equals(that.from())) return false;
-            if (!this.into().equals(that.into())) return false;
+            if (!this.from().isSame(that.from())) return false;
+            if (!this.into().isSame(that.into())) return false;
             if (!this.fromWorkspace.equals(that.fromWorkspace)) return false;
             if (!this.intoWorkspace.equals(that.intoWorkspace)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -212,8 +212,8 @@
     public void setActualLocations( Location fromLocation,
                                     Location intoLocation ) {
         checkNotFrozen();
-        if (!from.isSame(fromLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(fromLocation, from));
+        if (!from.equals(fromLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(fromLocation, from));
         }
         CheckArg.isNotNull(intoLocation, "intoLocation");
         assert fromLocation != null;
@@ -313,8 +313,8 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             CopyBranchRequest that = (CopyBranchRequest)obj;
-            if (!this.from().equals(that.from())) return false;
-            if (!this.into().equals(that.into())) return false;
+            if (!this.from().isSame(that.from())) return false;
+            if (!this.into().isSame(that.into())) return false;
             if (!this.nodeConflictBehavior().equals(that.nodeConflictBehavior())) return false;
             if (!this.fromWorkspace.equals(that.fromWorkspace)) return false;
             if (!this.intoWorkspace.equals(that.intoWorkspace)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -282,7 +282,7 @@
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
         CheckArg.isNotNull(actual, "actual");
-        if (!under.isSame(actual, false)) { // not same if actual is null
+        if (!under.equals(actual, false)) { // not same if actual is null
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -290,7 +290,7 @@
         }
         assert actual.hasPath();
         if (under.hasPath() && !under.getPath().equals(actual.getPath().getParent())) {
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, under));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, under));
         }
         this.actualLocation = actual;
     }
@@ -366,7 +366,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             CreateNodeRequest that = (CreateNodeRequest)obj;
-            if (!this.under().equals(that.under())) return false;
+            if (!this.under().isSame(that.under())) return false;
             if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
             if (!this.inWorkspace().equals(that.conflictBehavior())) return false;
             if (!this.properties().equals(that.properties())) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -94,8 +94,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -175,7 +175,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             DeleteBranchRequest that = (DeleteBranchRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -98,8 +98,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -240,7 +240,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             DeleteChildrenRequest that = (DeleteChildrenRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -128,8 +128,8 @@
      */
     public void setActualLocation( Location actualLocation ) {
         checkNotFrozen();
-        if (!at.isSame(actualLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, at));
+        if (!at.equals(actualLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actualLocation, at));
         }
         assert actualLocation != null;
         if (!actualLocation.hasPath()) {
@@ -210,7 +210,7 @@
         if (this.getClass().isInstance(obj)) {
             LockBranchRequest that = (LockBranchRequest)obj;
             if (this.lockTimeoutInMillis() != that.lockTimeoutInMillis()) return false;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (this.lockScope() != that.lockScope()) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -236,8 +236,8 @@
         checkNotFrozen();
         CheckArg.isNotNull(oldLocation, "oldLocation");
         CheckArg.isNotNull(newLocation, "newLocation");
-        if (!from.isSame(oldLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(oldLocation, from));
+        if (!from.equals(oldLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(oldLocation, from));
         }
         if (!oldLocation.hasPath()) {
             throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(oldLocation));
@@ -246,12 +246,12 @@
             throw new IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(newLocation));
         }
         if (into() != null && into().hasPath() && !newLocation.getPath().getParent().isSameAs(into.getPath())) {
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(newLocation, into));
         }
         Name actualNewName = newLocation.getPath().getLastSegment().getName();
         Name expectedNewName = desiredName() != null ? desiredName() : oldLocation.getPath().getLastSegment().getName();
         if (!actualNewName.equals(expectedNewName)) {
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(newLocation, into));
         }
         this.actualOldLocation = oldLocation;
         this.actualNewLocation = newLocation;
@@ -350,8 +350,8 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             MoveBranchRequest that = (MoveBranchRequest)obj;
-            if (!this.from().equals(that.from())) return false;
-            if (!this.into().equals(that.into())) return false;
+            if (!this.from().isSame(that.from())) return false;
+            if (!this.into().isSame(that.into())) return false;
             if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
             if (!this.workspaceName.equals(that.workspaceName)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -194,8 +194,8 @@
      */
     public void setActualLocationOfNode( Location actualLocation ) {
         checkNotFrozen();
-        if (!this.of.isSame(actualLocation)) { // not same if actualLocation is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, of));
+        if (!this.of.equals(actualLocation)) { // not same if actualLocation is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actualLocation, of));
         }
         assert actualLocation != null;
         if (!actualLocation.hasPath()) {
@@ -244,7 +244,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadAllChildrenRequest that = (ReadAllChildrenRequest)obj;
-            if (!this.of().equals(that.of())) return false;
+            if (!this.of().isSame(that.of())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -195,8 +195,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -245,7 +245,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadAllPropertiesRequest that = (ReadAllPropertiesRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -240,8 +240,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!of.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, of));
+        if (!of.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, of));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -291,7 +291,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadBlockOfChildrenRequest that = (ReadBlockOfChildrenRequest)obj;
-            if (!this.of().equals(that.of())) return false;
+            if (!this.of().isSame(that.of())) return false;
             if (this.startingAtIndex() != that.startingAtIndex()) return false;
             if (this.count() != that.count()) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -367,8 +367,8 @@
      *         location} as the {@link #at() current location}, or if the actual location does not have a path.
      */
     public void setActualLocationOfNode( Location actual ) {
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -418,7 +418,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadBranchRequest that = (ReadBranchRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (this.maximumDepth() != that.maximumDepth()) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -210,7 +210,7 @@
     public void setActualLocationOfStartingAfterNode( Location actual ) {
         checkNotFrozen();
         if (!startingAfter.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, startingAfter));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, startingAfter));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -260,7 +260,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadNextBlockOfChildrenRequest that = (ReadNextBlockOfChildrenRequest)obj;
-            if (!this.startingAfter().equals(that.startingAfter())) return false;
+            if (!this.startingAfter().isSame(that.startingAfter())) return false;
             if (this.count() != that.count()) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -259,8 +259,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -311,7 +311,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadNodeRequest that = (ReadNodeRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -134,8 +134,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!on.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
+        if (!on.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, on));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -185,7 +185,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             ReadPropertyRequest that = (ReadPropertyRequest)obj;
-            if (!this.on().equals(that.on())) return false;
+            if (!this.on().isSame(that.on())) return false;
             if (!this.named().equals(that.named())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -111,7 +111,7 @@
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
         if (!from.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, from));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, from));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -171,7 +171,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             RemovePropertyRequest that = (RemovePropertyRequest)obj;
-            if (!this.from().equals(that.from())) return false;
+            if (!this.from().isSame(that.from())) return false;
             if (!this.propertyName().equals(that.propertyName())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -117,11 +117,11 @@
                                     Location newLocation ) {
         checkNotFrozen();
         if (!at.isSame(oldLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(oldLocation, at));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(oldLocation, at));
         }
         assert oldLocation != null;
         if (newLocation == null) {
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, at));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(newLocation, at));
         }
         if (!oldLocation.hasPath()) {
             throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(oldLocation));
@@ -226,7 +226,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             RenameNodeRequest that = (RenameNodeRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.toName().equals(that.toName())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -111,8 +111,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!on.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
+        if (!on.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, on));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -172,7 +172,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             SetPropertyRequest that = (SetPropertyRequest)obj;
-            if (!this.on().equals(that.on())) return false;
+            if (!this.on().isSame(that.on())) return false;
             if (!this.property().equals(that.property())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -95,7 +95,7 @@
     public void setActualLocation( Location actualLocation ) {
         checkNotFrozen();
         if (!at.isSame(actualLocation)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, at));
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actualLocation, at));
         }
         assert actualLocation != null;
         if (!actualLocation.hasPath()) {
@@ -175,7 +175,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             UnlockBranchRequest that = (UnlockBranchRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -128,8 +128,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!on.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
+        if (!on.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, on));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -189,7 +189,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             UpdatePropertiesRequest that = (UpdatePropertiesRequest)obj;
-            if (!this.on().equals(that.on())) return false;
+            if (!this.on().isSame(that.on())) return false;
             if (!this.properties().equals(that.properties())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdateValuesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdateValuesRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdateValuesRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -127,8 +127,8 @@
 
     public void setActualLocation(Location actual, List<Object> actualAddedValues, List<Object> actualRemovedValues) {
         checkNotFrozen();
-        if (!on.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
+        if (!on.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, on));
         }
         assert actual != null;
         if (!actual.hasPath()) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -93,8 +93,8 @@
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!at.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+        if (!at.equals(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, at));
         }
         assert actual != null;
         if (!actual.hasPath()) {
@@ -155,7 +155,7 @@
         if (obj == this) return true;
         if (this.getClass().isInstance(obj)) {
             VerifyNodeExistsRequest that = (VerifyNodeExistsRequest)obj;
-            if (!this.at().equals(that.at())) return false;
+            if (!this.at().isSame(that.at())) return false;
             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
             return true;
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -38,7 +38,6 @@
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.cache.CachePolicy;
 import org.jboss.dna.graph.connector.LockFailedException;
-import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.observe.Changes;
 import org.jboss.dna.graph.observe.Observer;
 import org.jboss.dna.graph.property.DateTime;
@@ -549,7 +548,7 @@
             if (count > request.count()) break;
             if (!found) {
                 // Set to true if we find the child we're looking for ...
-                found = child.equals(request.startingAfter());
+                found = child.isSame(request.startingAfter());
             } else {
                 // Add the child to the block ...
                 ++count;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -1442,7 +1442,7 @@
                 org.jboss.dna.graph.Node persistentNode = cache.store.getNodeAt(getLocation());
                 // Check the actual location ...
                 Location actualLocation = persistentNode.getLocation();
-                if (!this.location.equals(actualLocation)) {
+                if (!this.location.isSame(actualLocation)) {
                     // The actual location is changed, so update it ...
                     this.location = actualLocation;
                 }
@@ -1452,7 +1452,7 @@
                 // Then read the node from the store ...
                 Subgraph subgraph = cache.store.getSubgraphOfDepth(depth).at(getLocation());
                 Location actualLocation = subgraph.getLocation();
-                if (!this.location.equals(actualLocation)) {
+                if (!this.location.isSame(actualLocation)) {
                     // The actual location is changed, so update it ...
                     this.location = actualLocation;
                 }
@@ -2708,7 +2708,7 @@
                 Node<Payload, PropertyPayload> that = (Node<Payload, PropertyPayload>)obj;
                 if (this.isStale() || that.isStale()) return false;
                 if (!this.nodeId.equals(that.nodeId)) return false;
-                return this.location.equals(that.location);
+                return this.location.isSame(that.location);
             }
             return false;
         }

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-25 20:18:20 UTC (rev 1351)
@@ -61,7 +61,7 @@
 unableToCreateReferenceToNodeWithoutUuid = Unable to set a reference to node {0} since it has no UUID
 unableToCopyToLocationWithoutAPath = Unable to copy node "{0}" to "{1}" since the desired location has no path
 unableToCopyToTheRoot = Unable to copy node "{0}" to "{1}" since the desired location is the root node
-actualLocationIsNotSameAsInputLocation = The actual location of {0} is not the same as the current location of {1}
+actualLocationNotEqualToInputLocation = The actual location of {0} is not equal to the current location of {1}
 actualLocationIsNotChildOfInputLocation = The actual location of {0} is not a child of the specified location {1}
 actualLocationIsNotAtCorrectChildSegment = The last segment of the actual location of {0} does not have the requested child segment {1}
 actualLocationDoesNotHaveCorrectChildName = The last segment of the actual location of {0} does not have the requested child name {1}

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -128,11 +128,11 @@
     }
 
     @Test
-    public void locationsWithSamePathsAndDifferentPropertyAreNotEqual() {
+    public void locationsWithSamePathsAndDifferentPropertyAreEqual() {
         Location locationA1 = Location.create(pathA, propA);
         Location locationA2 = Location.create(pathA, propB);
 
-        assertThat("Locations created with identical paths and different property must not be equal", locationA1, not(locationA2));
+        assertThat("Locations created with identical paths and different property must not be equal", locationA1.equals(locationA2), is(true));
     }
 
     @Test
@@ -187,8 +187,8 @@
         Location locationA2 = Location.create(pathA, propListABU);
 
         assertThat("Locations created with identical paths and different properties must not be equal",
-                   locationA1,
-                   not(locationA2));
+                   locationA1.equals(locationA2),
+                   is(true));
     }
 
     @Test

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -447,7 +447,7 @@
 
             // Check the result has the correct location ...
             assertThat("The node that was read doesn't have the expected location",
-                       result.getLocation().isSame(location, true),
+                       result.getLocation().equals(location),
                        is(true));
         }
 
@@ -667,7 +667,7 @@
         assertThat(subgraph2, is(notNullValue()));
 
         // Shortcut ...
-        if (subgraph1.getLocation().equals(subgraph2.getLocation())) return;
+        if (subgraph1.getLocation().isSame(subgraph2.getLocation())) return;
 
         Path rootPath1 = subgraph1.getRoot().getLocation().getPath();
         Path rootPath2 = subgraph2.getRoot().getLocation().getPath();
@@ -738,7 +738,7 @@
         // Check the locations ...
         Location location1 = node1.getLocation();
         Location location2 = node2.getLocation();
-        assertThat(location1.isSame(location2, true), is(true));
+        assertThat(location1.isSame(location2), is(true));
 
         // Check the paths ...
         assertThat(location1.getPath(), is(location2.getPath()));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -93,7 +93,7 @@
         Node root = graph.getNodeAt("/");
         for (int i = 0; i != 10; ++i) {
             Node anotherRoot = graph.getNodeAt("/");
-            assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+            assertThat(anotherRoot.getLocation().isSame(root.getLocation()), is(true));
             assertThat(anotherRoot.getLocation().getPath(), is(root.getLocation().getPath()));
             assertThat(anotherRoot.getLocation().getIdProperties(), is(root.getLocation().getIdProperties()));
         }
@@ -117,7 +117,7 @@
             }
             // Find the root node using the identification properties ...
             Node anotherRoot = graph.getNodeAt(firstProperty, additionalProperties);
-            assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+            assertThat(anotherRoot.getLocation().isSame(root.getLocation()), is(true));
             assertThat(anotherRoot.getLocation().getPath(), is(root.getLocation().getPath()));
             assertThat(anotherRoot.getLocation().getIdProperties(), is(root.getLocation().getIdProperties()));
         }
@@ -131,7 +131,7 @@
         if (uuid != null) {
             // Find the root node using the identification properties ...
             Node anotherRoot = graph.getNodeAt(uuid);
-            assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+            assertThat(anotherRoot.getLocation().isSame(root.getLocation()), is(true));
             assertThat(anotherRoot.getLocation().getPath(), is(root.getLocation().getPath()));
             assertThat(anotherRoot.getLocation().getIdProperties(), is(root.getLocation().getIdProperties()));
             assertThat(anotherRoot.getLocation().getUuid(), is(root.getLocation().getUuid()));

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-25 18:15:33 UTC (rev 1350)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-25 20:18:20 UTC (rev 1351)
@@ -1773,7 +1773,7 @@
         if (obj instanceof AbstractJcrNode) {
             AbstractJcrNode that = (AbstractJcrNode)obj;
             if (this.cache != that.cache) return false;
-            return this.location.equals(that.location);
+            return this.location.isSame(that.location);
         }
         return false;
     }


From dna-commits at lists.jboss.org  Wed Nov 25 17:30:59 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 17:30:59 -0500
Subject: [dna-commits] DNA SVN: r1352 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/io and 1 other directory.
Message-ID: <200911252230.nAPMUxDb010345@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-25 17:30:59 -0500 (Wed, 25 Nov 2009)
New Revision: 1352

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
Log:
DNA-556 created GraphSequencerOutput to simply testing sequenced graph results.

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java	2009-11-25 22:30:59 UTC (rev 1352)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.io;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.sequencer.SequencerOutput;
+
+/**
+ * Utility for wrapping sequencer output to commit to a graph.
+ * <p>
+ * Constructors allow providing either a {@link Graph} or a {@link Graph.Batch} object. If {@link Graph} constructor is used, a
+ * {@link Graph.Batch} object is created to utilize the batching capabilities of all graph requests.
+ * <p>
+ * Calling close() commits all batched graph requests.
+ */
+public class GraphSequencerOutput implements SequencerOutput {
+
+    private final Graph.Batch batch;
+
+    private final PathFactory pathFactory;
+
+    private final Set<Path> paths = new HashSet<Path>();
+
+    /**
+     * Create a graph sequencer output instance using {@link Graph.Batch} object.
+     * 
+     * @param batch the {@link Graph.Batch} object; may not be null
+     */
+    public GraphSequencerOutput( Graph.Batch batch ) {
+        super();
+        this.batch = batch;
+        ExecutionContext context = batch.getGraph().getContext();
+        this.pathFactory = context.getValueFactories().getPathFactory();
+    }
+
+    /**
+     * Create a graph sequencer output instance using {@link Graph} object. A {@link Graph.Batch} object is created as a result.
+     * 
+     * @param graph the {@link Graph} object; may not be null
+     */
+    public GraphSequencerOutput( Graph graph ) {
+        this(graph.batch());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setProperty(java.lang.String, java.lang.String, java.lang.Object[])
+     */
+    public void setProperty( String nodePath,
+                             String propertyName,
+                             Object... values ) {
+        Path path = pathFactory.create(nodePath);
+        if (paths.add(path)) {
+            batch.create(path).and();
+        }
+        batch.set(propertyName).on(path).to(values);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setProperty(org.jboss.dna.graph.property.Path,
+     *      org.jboss.dna.graph.property.Name, java.lang.Object[])
+     */
+    public void setProperty( Path nodePath,
+                             Name propertyName,
+                             Object... values ) {
+        if (paths.add(nodePath)) {
+            batch.create(nodePath).and();
+        }
+        batch.set(propertyName).on(nodePath).to(values);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.SequencerOutput#setReference(java.lang.String, java.lang.String, java.lang.String[])
+     */
+    public void setReference( String nodePath,
+                              String propertyName,
+                              String... paths ) {
+        Path path = pathFactory.create(nodePath);
+        if (this.paths.add(path)) {
+            batch.create(path).and();
+        }
+        batch.set(propertyName).on(nodePath).to(paths);
+    }
+
+    public void close() {
+        batch.execute();
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphSequencerOutput.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java	2009-11-25 22:30:59 UTC (rev 1352)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph.io;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class GraphSequencerOutputTest {
+    private GraphSequencerOutput output;
+    private ExecutionContext context;
+    private Graph graph;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+
+        InMemoryRepositorySource source = new InMemoryRepositorySource();
+        source.setName("actual");
+        graph = Graph.create(source, context);
+
+        output = new GraphSequencerOutput(graph);
+    }
+
+    protected Path createPath( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected Name createName( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    @Test
+    public void shouldSetPropertyWithString() {
+        String path1 = "/a";
+        String prop1_name = "prop1";
+        String value_1 = "blue";
+        output.setProperty(path1, prop1_name, value_1);
+        String path2 = "/a/b";
+        String prop2_name = "prop2";
+        String value_2 = "red";
+        output.setProperty(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+
+    @Test
+    public void shouldSetReferenceWithString() {
+        String path1 = "/a";
+        String prop1_name = "prop1";
+        String value_1 = "blue";
+        output.setReference(path1, prop1_name, value_1);
+        String path2 = "/a/b";
+        String prop2_name = "prop2";
+        String value_2 = "red";
+        output.setReference(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+
+    @Test
+    public void shouldSetPropertyWithPath() {
+        Path path1 = createPath("/a");
+        Name prop1_name = createName("prop1");
+        String value_1 = "blue";
+        output.setProperty(path1, prop1_name, value_1);
+        Path path2 = createPath("/a/b");
+        Name prop2_name = createName("prop2");
+        String value_2 = "red";
+        output.setProperty(path2, prop2_name, value_2);
+        output.close();
+        Subgraph result = graph.getSubgraphOfDepth(10).at("/");
+        String v1 = (String)result.getNode(path1).getProperty(prop1_name).getFirstValue();
+        assertThat(v1, is(value_1));
+        String v2 = (String)result.getNode(path2).getProperty(prop2_name).getFirstValue();
+        assertThat(v2, is(value_2));
+        assertNull(result.getNode("/c"));
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphSequencerOutputTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain


From dna-commits at lists.jboss.org  Wed Nov 25 17:31:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 17:31:46 -0500
Subject: [dna-commits] DNA SVN: r1353 - in
	trunk/docs/reference/src/main/docbook/en-US: content and 1
	other directories.
Message-ID: <200911252231.nAPMVkPl010447@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-11-25 17:31:46 -0500 (Wed, 25 Nov 2009)
New Revision: 1353

Added:
   trunk/docs/reference/src/main/docbook/en-US/content/sequencers/ddl.xml
Modified:
   trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
   trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
DNA-49 Added doc section and content for new DDL Sequencer

Modified: trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-11-25 22:30:59 UTC (rev 1352)
+++ trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-11-25 22:31:46 UTC (rev 1353)
@@ -481,9 +481,16 @@
             <emphasis role="strong">dna-sequencer-java</emphasis>
             is a DNA sequencer that extracts the structure (methods, fields) from Java source files.
           </para>
-        </listitem>        
+        </listitem>  
         <listitem>
           <para>
+            <emphasis role="strong">dna-sequencer-ddl</emphasis>
+            is a DNA sequencer that extracts the structure and content from DDL files.
+                <emphasis>This is still under development and only includes a limited number of dialects and basic DDL statements.</emphasis>
+          </para>
+        </listitem>      
+        <listitem>
+          <para>
             <emphasis role="strong">dna-mimetype-detector-aperture</emphasis>
             is a DNA MIME type detector that uses the
             <ulink url="http://aperture.sourceforge.net/">Aperture</ulink>

Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/ddl.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/ddl.xml	                        (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/ddl.xml	2009-11-25 22:31:46 UTC (rev 1353)
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ JBoss DNA (http://www.jboss.org/dna)
+  ~
+  ~ See the COPYRIGHT.txt file distributed with this work for information
+  ~ regarding copyright ownership.  Some portions may be licensed
+  ~ to Red Hat, Inc. under one or more contributor license agreements.
+  ~ See the AUTHORS.txt file in the distribution for a full listing of 
+  ~ individual contributors.
+  ~
+  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+  ~ is licensed to you 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.
+  ~
+  ~ JBoss DNA 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 distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"  [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="ddl-file-sequencer">
+  <title>DDL File Sequencer</title>
+    <para>
+        The DDL file sequencer is included in JBoss DNA and is capable of parsing the more important DDL statements from several 
+        different dialects, and constructing a graph structure that is largely the same for all dialects (though some dialects will 
+        result in dialect-specific properties and maybe nodes). The sequencer attempts to detect the dialect by running multiple 
+        parsers and finding the one with the "best fit".</para>
+    <para>
+        One very interesting capability of this sequencer is that, although only a subset of the (more common) DDL statements 
+        are supported, the sequencer is still extremely functional since it does still add all statements into the output graph, 
+        just without much detail other than just the statement text and the position in the DDL file. Thus, if a DDL file 
+        contains statements the sequencer understands and statements that the sequencer does not understand, the graph will 
+        still contain all statements and those statements understood by the sequencer will have their full detail. 
+        Since the underlying parsers are able to operate upon a single statement, it is possible to go back later 
+        (after the parsers have been enhanced to support additional DDL statements) and re-parse only those incomplete statements 
+        in the graph.
+    </para>
+    <para>
+        Initially the following ddl dialects are included with this sequencer: Oracle, Derby, Postgres and MySql.</para>
+    <para>
+        Each specific dialect extends a basic parser framework implementing the SQL 92 spec and includes
+        <itemizedlist>
+            <listitem>
+                <para>Detailed parsing of CREATE SCHEMA, CREATE TABLE and ALTER TABLE.</para>
+            </listitem>
+            <listitem>
+                <para>Partial parsing of DROP statements</para>
+            </listitem>
+            <listitem>
+                <para>General parsing of remaining schema definition statements (i.e. CREATE VIEW, CREATE DOMAIN, etc.</para>
+            </listitem>
+            <listitem>
+                <para>Does NOT perform detailed parsing of SQL (i.e. SELECT, INSERT, UPDATE, etc....) statements.</para>
+            </listitem>
+        </itemizedlist>
+    </para>
+    <para>
+        To use this sequencer, simply include the <code>dna-sequencer-ddl</code> JAR
+        in your application and configure the &JcrConfiguration; to use this sequencer using something similar to:
+    </para>
+   <programlisting>
+JcrConfiguration config = ...
+
+config.sequencer("DDL Sequencer")
+      .usingClass("org.jboss.dna.sequencer.ddl.DdlSequencer")
+      .loadedFromClasspath()
+      .setDescription("Sequences DDL files to extract individual statements and accompanying statement properties and values")
+      .sequencingFrom("//(*.(ddl)[*])/jcr:content[@jcr:data]")
+      .andOutputtingTo("/ddls/$1"); 
+    </programlisting>
+</chapter>
\ No newline at end of file


Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/ddl.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml	2009-11-25 22:30:59 UTC (rev 1352)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml	2009-11-25 22:31:46 UTC (rev 1353)
@@ -134,6 +134,7 @@
   	<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/java_source.xml"/>
   	<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/image.xml"/>
   	<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/mp3.xml"/>
+  	<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/ddl.xml"/>
 	</part>
 	<part id="provied-mime-type-detectors-part">
 		<title>MIME Type Detector Library</title>


From dna-commits at lists.jboss.org  Wed Nov 25 19:32:01 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 19:32:01 -0500
Subject: [dna-commits] DNA SVN: r1355 - in trunk:
	extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl
	and 1 other directory.
Message-ID: <200911260032.nAQ0W1hJ001166@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 19:32:01 -0500 (Wed, 25 Nov 2009)
New Revision: 1355

Modified:
   trunk/.gitignore
   trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/StandardDdlParser.java
Log:
DNA-49 Corrected build errors (@Override used on method implementing interface, which is not allowed in JDK 5). Also added the DDL sequencer's 'target' folder to the .gitignore folder.

Modified: trunk/.gitignore
===================================================================
--- trunk/.gitignore	2009-11-25 22:39:45 UTC (rev 1354)
+++ trunk/.gitignore	2009-11-26 00:32:01 UTC (rev 1355)
@@ -54,6 +54,7 @@
 /extensions/dna-mimetype-detector-aperture/.settings
 /extensions/dna-mimetype-detector-aperture/target
 /extensions/dna-sequencer-cnd/target
+/extensions/dna-sequencer-ddl/target
 /extensions/dna-sequencer-esbMessage/target
 /extensions/dna-sequencer-images/target
 /extensions/dna-sequencer-java/target

Modified: trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/StandardDdlParser.java
===================================================================
--- trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/StandardDdlParser.java	2009-11-25 22:39:45 UTC (rev 1354)
+++ trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/StandardDdlParser.java	2009-11-26 00:32:01 UTC (rev 1355)
@@ -195,7 +195,6 @@
      * 
      * @see org.jboss.dna.sequencer.ddl.DdlParser#getNumberOfKeyWords(org.jboss.dna.sequencer.ddl.DdlTokenStream)
      */
-    @Override
     public int getNumberOfKeyWords( DdlTokenStream tokens ) {
         int count = 0;
 
@@ -2545,7 +2544,6 @@
      * 
      * @see org.jboss.dna.sequencer.ddl.DdlParser#isType(String)
      */
-    @Override
     public boolean isType( String ddl ) {
         String trimmedDDL = ddl.trim();
         int endIndex = 400;


From dna-commits at lists.jboss.org  Wed Nov 25 19:33:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 19:33:00 -0500
Subject: [dna-commits] DNA SVN: r1356 - in trunk: dna-cnd and 27 other
	directories.
Message-ID: <200911260033.nAQ0X01O001268@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 19:32:58 -0500 (Wed, 25 Nov 2009)
New Revision: 1356

Modified:
   trunk/dna-cnd/pom.xml
   trunk/dna-graph/pom.xml
   trunk/dna-integration-tests/pom.xml
   trunk/dna-jcr-tck/pom.xml
   trunk/dna-jcr/pom.xml
   trunk/dna-repository/pom.xml
   trunk/dna-search/pom.xml
   trunk/docs/examples/gettingstarted/pom.xml
   trunk/docs/examples/gettingstarted/repositories/pom.xml
   trunk/docs/examples/gettingstarted/sequencers/pom.xml
   trunk/extensions/dna-classloader-maven/pom.xml
   trunk/extensions/dna-connector-filesystem/pom.xml
   trunk/extensions/dna-connector-infinispan/pom.xml
   trunk/extensions/dna-connector-jbosscache/pom.xml
   trunk/extensions/dna-connector-jdbc-metadata/pom.xml
   trunk/extensions/dna-connector-store-jpa/pom.xml
   trunk/extensions/dna-connector-svn/pom.xml
   trunk/extensions/dna-mimetype-detector-aperture/pom.xml
   trunk/extensions/dna-sequencer-cnd/pom.xml
   trunk/extensions/dna-sequencer-ddl/pom.xml
   trunk/extensions/dna-sequencer-images/pom.xml
   trunk/extensions/dna-sequencer-java/pom.xml
   trunk/extensions/dna-sequencer-jbpm-jpdl/pom.xml
   trunk/extensions/dna-sequencer-msoffice/pom.xml
   trunk/extensions/dna-sequencer-xml/pom.xml
   trunk/extensions/dna-sequencer-zip/pom.xml
   trunk/pom.xml
   trunk/web/dna-web-jcr-rest-client/pom.xml
   trunk/web/dna-web-jcr-rest-war/pom.xml
Log:
Changed the ${pom.version} to ${project.version}, since the former has been deprecated.

Modified: trunk/dna-cnd/pom.xml
===================================================================
--- trunk/dna-cnd/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-cnd/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -23,7 +23,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/dna-graph/pom.xml
===================================================================
--- trunk/dna-graph/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-graph/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -24,7 +24,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/dna-integration-tests/pom.xml
===================================================================
--- trunk/dna-integration-tests/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-integration-tests/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -50,14 +50,14 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-common</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-jcr</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
@@ -65,25 +65,25 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-connector-jbosscache</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-connector-filesystem</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-connector-infinispan</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
         <dependency>
             <groupId>org.jboss.dna</groupId>
             <artifactId>dna-connector-svn</artifactId>
-            <version>${pom.version}</version>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
 
@@ -93,7 +93,7 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-connector-store-jpa</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>

Modified: trunk/dna-jcr/pom.xml
===================================================================
--- trunk/dna-jcr/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-jcr/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -25,7 +25,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -36,7 +36,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -47,7 +47,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-cnd</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
     </dependency>
     <!-- 
     Testing (note the scope)

Modified: trunk/dna-jcr-tck/pom.xml
===================================================================
--- trunk/dna-jcr-tck/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-jcr-tck/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -25,7 +25,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -36,7 +36,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -47,12 +47,12 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-jcr</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-jcr</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/dna-repository/pom.xml
===================================================================
--- trunk/dna-repository/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-repository/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -24,7 +24,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -35,7 +35,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/dna-search/pom.xml
===================================================================
--- trunk/dna-search/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/dna-search/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -24,7 +24,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
@@ -35,7 +35,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/docs/examples/gettingstarted/pom.xml
===================================================================
--- trunk/docs/examples/gettingstarted/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/docs/examples/gettingstarted/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -18,47 +18,47 @@
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-common</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-graph</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-repository</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-jcr</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-maven-classloader</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-sequencer-images</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-sequencer-java</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-connector-jbosscache</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
       </dependency>
       <dependency>
         <groupId>org.jboss.dna</groupId>
         <artifactId>dna-connector-federation</artifactId>
-        <version>${pom.version}</version>
+        <version>${project.version}</version>
         <scope>runtime</scope>
       </dependency>
       <!-- Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) -->

Modified: trunk/docs/examples/gettingstarted/repositories/pom.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/docs/examples/gettingstarted/repositories/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -31,7 +31,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-connector-jbosscache</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
     <!-- 

Modified: trunk/docs/examples/gettingstarted/sequencers/pom.xml
===================================================================
--- trunk/docs/examples/gettingstarted/sequencers/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/docs/examples/gettingstarted/sequencers/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -31,25 +31,25 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-sequencer-images</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-sequencer-mp3</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-sequencer-java</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-sequencer-zip</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
     <!-- 

Modified: trunk/extensions/dna-classloader-maven/pom.xml
===================================================================
--- trunk/extensions/dna-classloader-maven/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-classloader-maven/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -25,7 +25,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-connector-filesystem/pom.xml
===================================================================
--- trunk/extensions/dna-connector-filesystem/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-filesystem/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -39,14 +39,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-connector-infinispan/pom.xml
===================================================================
--- trunk/extensions/dna-connector-infinispan/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-infinispan/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -33,14 +33,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-connector-jbosscache/pom.xml
===================================================================
--- trunk/extensions/dna-connector-jbosscache/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-jbosscache/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -33,14 +33,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-connector-jdbc-metadata/pom.xml
===================================================================
--- trunk/extensions/dna-connector-jdbc-metadata/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-jdbc-metadata/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -24,7 +24,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common-jdbc</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
     </dependency>
     <!-- 
     Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 

Modified: trunk/extensions/dna-connector-store-jpa/pom.xml
===================================================================
--- trunk/extensions/dna-connector-store-jpa/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-store-jpa/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -60,14 +60,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-connector-svn/pom.xml
===================================================================
--- trunk/extensions/dna-connector-svn/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-connector-svn/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -32,14 +32,14 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-common</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-graph</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>

Modified: trunk/extensions/dna-mimetype-detector-aperture/pom.xml
===================================================================
--- trunk/extensions/dna-mimetype-detector-aperture/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-mimetype-detector-aperture/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -24,7 +24,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-cnd/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-cnd/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-cnd/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -28,14 +28,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-ddl/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-ddl/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-ddl/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -28,14 +28,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-images/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-images/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-images/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -32,14 +32,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-java/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-java/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-java/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -33,14 +33,14 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-common</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-graph</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>

Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -33,14 +33,14 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-common</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-graph</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>

Modified: trunk/extensions/dna-sequencer-msoffice/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-msoffice/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-msoffice/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -20,7 +20,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-xml/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-xml/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-xml/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -32,14 +32,14 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/extensions/dna-sequencer-zip/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-zip/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/extensions/dna-sequencer-zip/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -20,7 +20,7 @@
     <dependency>
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-common</artifactId>
-      <version>${pom.version}</version>
+      <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -759,102 +759,102 @@
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-common</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-classloader-maven</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-graph</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-cnd</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-search</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-repository</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-jcr</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-jcr-tck</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-connector-jbosscache</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-connector-store-jpa</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-connector-filesystem</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-connector-svn</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-mimetype-detector-aperture</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-cnd</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-images</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-java</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-mp3</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-msoffice</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-xml</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-sequencer-zip</artifactId>
-				<version>${pom.version}</version>
+				<version>${project.version}</version>
 			</dependency>
 			<!-- 
 		    Time and Date

Modified: trunk/web/dna-web-jcr-rest-client/pom.xml
===================================================================
--- trunk/web/dna-web-jcr-rest-client/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/web/dna-web-jcr-rest-client/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -26,7 +26,7 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-common</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<type>test-jar</type>
 			<scope>test</scope>
 		</dependency>
@@ -34,7 +34,7 @@
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-web-jcr-rest-war</artifactId>
 			<type>war</type>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 			<scope>integration-test</scope>
 		</dependency>
         <dependency>

Modified: trunk/web/dna-web-jcr-rest-war/pom.xml
===================================================================
--- trunk/web/dna-web-jcr-rest-war/pom.xml	2009-11-26 00:32:01 UTC (rev 1355)
+++ trunk/web/dna-web-jcr-rest-war/pom.xml	2009-11-26 00:32:58 UTC (rev 1356)
@@ -15,7 +15,7 @@
 		<dependency>
 			<groupId>org.jboss.dna</groupId>
 			<artifactId>dna-web-jcr-rest</artifactId>
-			<version>${pom.version}</version>
+			<version>${project.version}</version>
 		</dependency>
 
 		<dependency>


From dna-commits at lists.jboss.org  Wed Nov 25 19:33:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 19:33:28 -0500
Subject: [dna-commits] DNA SVN: r1357 - in
	trunk/extensions/dna-sequencer-ddl/src:
	test/java/org/jboss/dna/sequencer/ddl/dialect/postgres and 1
	other directory.
Message-ID: <200911260033.nAQ0XSn8001319@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 19:33:28 -0500 (Wed, 25 Nov 2009)
New Revision: 1357

Modified:
   trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/DdlTokenStream.java
   trunk/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/postgres/PostgresDdlParserTest.java
Log:
DNA-49 Ran into problems running the unit tests for PostgreSQL that use the \u2019 single quote character.  The tests pass in Eclipse, but fail when running 'mvn clean install' from the command line.  Simply added @Ignore to these tests, so Barry can fix later.

Modified: trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/DdlTokenStream.java
===================================================================
--- trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/DdlTokenStream.java	2009-11-26 00:32:58 UTC (rev 1356)
+++ trunk/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/DdlTokenStream.java	2009-11-26 00:33:28 UTC (rev 1357)
@@ -28,222 +28,214 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-
 import org.jboss.dna.common.CommonI18n;
 import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.text.Position;
 import org.jboss.dna.common.text.TokenStream;
 
 /**
- * A TokenStream implementation designed around requirements for tokenizing and parsing DDL
- * statements.
+ * A TokenStream implementation designed around requirements for tokenizing and parsing DDL statements.
  * <p>
  * Because of the complexity of DDL, it was necessary to extend {@link TokenStream} in order to override the basic tokenizer to
- * tokenize the in-line comments prefixed with "--". In addition, because there is not a default ddl command (or statement) terminator,
- * an override method was added to {@link TokenStream} to allow re-tokenizing the initial tokens to re-type the tokens, remove tokens, 
- * or any other operation to simplify parsing.
+ * tokenize the in-line comments prefixed with "--". In addition, because there is not a default ddl command (or statement)
+ * terminator, an override method was added to {@link TokenStream} to allow re-tokenizing the initial tokens to re-type the
+ * tokens, remove tokens, or any other operation to simplify parsing.
  * </p>
- * 
  * <p>
- * In this case, both reserved words (or key words) and statement start phrases can be registered prior to the {@link TokenStream}'s 
- * start() method.  Any resulting tokens that match the registered string values will be re-typed to identify them as key words 
+ * In this case, both reserved words (or key words) and statement start phrases can be registered prior to the {@link TokenStream}
+ * 's start() method. Any resulting tokens that match the registered string values will be re-typed to identify them as key words
  * (DdlTokenizer.KEYWORD) or statement start phrases (DdlTokenizer.STATEMENT_KEY).
  * </p>
- *
  */
 public class DdlTokenStream extends TokenStream {
 
-	protected List<String[]> registeredStatementStartPhrases = new ArrayList<String[]>();
-	
-	protected Set<String> registeredKeyWords = new HashSet<String>();
-	
-	private Position currentMarkedPosition = Position.EMPTY_CONTENT_POSITION;
-	
-	/**
-	 * {@inheritDoc}
-	 *
-	 * @see org.jboss.dna.common.text.TokenStream#initializeTokens(java.util.List)
-	 */
-	@Override
-	protected List<Token> initializeTokens(List<Token> tokens) {
-		// THIS IS WHERE WE DO THE WORK OF PRE-PARSING TOKENS AND REPLACING KEYWORDS AND STATEMENT STARTS WITH 
-		// APPLICABLE TOKEN TYPE BITMASK VALUES
-		// MyClass[] array = (MyClass[])list.toArray(new MyClass[list.size()]);
+    protected List<String[]> registeredStatementStartPhrases = new ArrayList<String[]>();
 
-		Token[] tokensArray = tokens.toArray(new Token[tokens.size()]);
-		List<Token> reTypedTokens = new ArrayList<Token>(tokens.size());
-		
-		for( int i=0; i<tokensArray.length; i++ ) {
-			boolean isStatementStart = false;
-			if( isKeyWord(tokensArray[i].value()) ) {
-				Token retypedToken = tokensArray[i].withType(DdlTokenizer.KEYWORD);
-				// Now we check to see if this keyword begins a registered statement start
-				
-				// Keep track of token increment (# of tokens for a phrase)
-				// Need to increment iterator (i) in case phrases like "ALTER ROLLBACK" appear. ROLLBACK is also a statement
-				// start phrase and we need to walk ignore ROLLBACK in this case.
-				int tokenIncrement = 0;
-	    		for( String[] nextStmtStart : registeredStatementStartPhrases ) {
-	    			boolean matches = true;
-	    			
-	    			for(int j=0; j<nextStmtStart.length; j++ ) {
-	    				if( matches ) {
-	    					matches = nextStmtStart[j].equalsIgnoreCase(tokensArray[i + j].value()) ||
-	    							  nextStmtStart[j].equals(ANY_VALUE);
-	    				}
-	    			}
-	    			if( matches ) {
-	    				isStatementStart = true;
-	    				tokenIncrement = nextStmtStart.length -1;
-	    				break;
-	    			}
-	    		}
-				if( isStatementStart) {
-					retypedToken = retypedToken.withType(DdlTokenizer.STATEMENT_KEY);
-				}
-				reTypedTokens.add(retypedToken);
-				
-				if( isStatementStart) {
-					// Copy any additional tokens used in the phrase
-					for( int k=0; k<tokenIncrement; k++ ) {
-						i++;
-						reTypedTokens.add(tokensArray[i]);
-					}
-				}
-			} else {
-				reTypedTokens.add(tokensArray[i]);
-			}
+    protected Set<String> registeredKeyWords = new HashSet<String>();
 
-		}
-		
-		return reTypedTokens;
-	}
-	
-	/**
-	 * @param content
-	 * @param tokenizer 
-	 * @param caseSensitive
-	 */
-	public DdlTokenStream(String content, Tokenizer tokenizer, boolean caseSensitive) {
-		super(content, tokenizer, caseSensitive);
-	}
+    private Position currentMarkedPosition = Position.EMPTY_CONTENT_POSITION;
 
-	
-	/**
-	 * Register a phrase representing the start of a DDL statement
-	 * 
-	 * <p>
-	 * Examples would be:
-	 * 		{"CREATE", "TABLE"}
-	 * 		{"CREATE", "OR", "REPLACE", "VIEW"}
-	 * </p>
-	 * see {@link DdlConstants} for the default SQL 92 representations.
-	 * 
-	 * @param phrase
-	 */
-    public void registerStatementStartPhrase(String[] phrase) {
-    	registeredStatementStartPhrases.add(phrase);
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.common.text.TokenStream#initializeTokens(java.util.List)
+     */
+    @Override
+    protected List<Token> initializeTokens( List<Token> tokens ) {
+        // THIS IS WHERE WE DO THE WORK OF PRE-PARSING TOKENS AND REPLACING KEYWORDS AND STATEMENT STARTS WITH
+        // APPLICABLE TOKEN TYPE BITMASK VALUES
+        // MyClass[] array = (MyClass[])list.toArray(new MyClass[list.size()]);
+
+        Token[] tokensArray = tokens.toArray(new Token[tokens.size()]);
+        List<Token> reTypedTokens = new ArrayList<Token>(tokens.size());
+
+        for (int i = 0; i < tokensArray.length; i++) {
+            boolean isStatementStart = false;
+            if (isKeyWord(tokensArray[i].value())) {
+                Token retypedToken = tokensArray[i].withType(DdlTokenizer.KEYWORD);
+                // Now we check to see if this keyword begins a registered statement start
+
+                // Keep track of token increment (# of tokens for a phrase)
+                // Need to increment iterator (i) in case phrases like "ALTER ROLLBACK" appear. ROLLBACK is also a statement
+                // start phrase and we need to walk ignore ROLLBACK in this case.
+                int tokenIncrement = 0;
+                for (String[] nextStmtStart : registeredStatementStartPhrases) {
+                    boolean matches = true;
+
+                    for (int j = 0; j < nextStmtStart.length; j++) {
+                        if (matches) {
+                            matches = nextStmtStart[j].equalsIgnoreCase(tokensArray[i + j].value())
+                                      || nextStmtStart[j].equals(ANY_VALUE);
+                        }
+                    }
+                    if (matches) {
+                        isStatementStart = true;
+                        tokenIncrement = nextStmtStart.length - 1;
+                        break;
+                    }
+                }
+                if (isStatementStart) {
+                    retypedToken = retypedToken.withType(DdlTokenizer.STATEMENT_KEY);
+                }
+                reTypedTokens.add(retypedToken);
+
+                if (isStatementStart) {
+                    // Copy any additional tokens used in the phrase
+                    for (int k = 0; k < tokenIncrement; k++) {
+                        i++;
+                        reTypedTokens.add(tokensArray[i]);
+                    }
+                }
+            } else {
+                reTypedTokens.add(tokensArray[i]);
+            }
+
+        }
+
+        return reTypedTokens;
     }
-    
-    public void registerStatementStartPhrase(String[][] phrases) {
-    	for(String[] phrase : phrases ) {
-    		registeredStatementStartPhrases.add(phrase);
-    	}
+
+    /**
+     * @param content
+     * @param tokenizer
+     * @param caseSensitive
+     */
+    public DdlTokenStream( String content,
+                           Tokenizer tokenizer,
+                           boolean caseSensitive ) {
+        super(content, tokenizer, caseSensitive);
     }
-    
+
     /**
+     * Register a phrase representing the start of a DDL statement
+     * <p>
+     * Examples would be: {"CREATE", "TABLE"} {"CREATE", "OR", "REPLACE", "VIEW"}
+     * </p>
+     * see {@link DdlConstants} for the default SQL 92 representations.
+     * 
+     * @param phrase
+     */
+    public void registerStatementStartPhrase( String[] phrase ) {
+        registeredStatementStartPhrases.add(phrase);
+    }
+
+    public void registerStatementStartPhrase( String[][] phrases ) {
+        for (String[] phrase : phrases) {
+            registeredStatementStartPhrases.add(phrase);
+        }
+    }
+
+    /**
      * Register a single key word.
      * 
      * @param keyWord
      */
-    public void registerKeyWord(String keyWord) {
-    	registeredKeyWords.add(keyWord);
+    public void registerKeyWord( String keyWord ) {
+        registeredKeyWords.add(keyWord);
     }
-    
+
     /**
      * Register an {@link List} of key words.
      * 
      * @param keyWords
      */
-    public void registerKeyWords(List<String> keyWords) {
-    	registeredKeyWords.addAll(keyWords);
+    public void registerKeyWords( List<String> keyWords ) {
+        registeredKeyWords.addAll(keyWords);
     }
-    
+
     /**
      * Register an array of key words.
      * 
      * @param keyWords
      */
-    public void registerKeyWords(String[] keyWords) {
-    	registeredKeyWords.addAll(Arrays.asList(keyWords));
+    public void registerKeyWords( String[] keyWords ) {
+        registeredKeyWords.addAll(Arrays.asList(keyWords));
     }
-    
+
     /**
-     * 
      * @param word
      * @return is Key Word
      */
-    protected boolean isKeyWord(String word) {
-    	return registeredKeyWords.contains(word.toUpperCase());
+    protected boolean isKeyWord( String word ) {
+        return registeredKeyWords.contains(word.toUpperCase());
     }
-    
+
     /**
      * Method to determine if the next token is of type {@link DdlTokenizer} KEYWORD.
      * 
      * @return is Key Word
      */
     public boolean isNextKeyWord() {
-    	return this.matches(DdlTokenizer.KEYWORD);
+        return this.matches(DdlTokenizer.KEYWORD);
     }
-    
+
     /**
      * Method to determine if next tokens match a registered statement start phrase.
      * 
      * @return true if next tokens match a registered statement start phrase
      */
     public boolean isNextStatementStart() {
-    	boolean result = false;
-    	
-    	if( isNextKeyWord() ) {
-    		for( String[] nextStmtStart : registeredStatementStartPhrases ) {
-    			if( this.matches(nextStmtStart) ) {
-    				return true;
-    			}
-    		}
-    	}
-    	
-    	return result;
+        boolean result = false;
+
+        if (isNextKeyWord()) {
+            for (String[] nextStmtStart : registeredStatementStartPhrases) {
+                if (this.matches(nextStmtStart)) {
+                    return true;
+                }
+            }
+        }
+
+        return result;
     }
-    
+
     /**
      * Marks the current position (line & column number) of the currentToken
      */
     public void mark() {
-    	if( this.hasNext() ) {
-    		currentMarkedPosition = this.nextPosition();
-    	} else {
-    		currentMarkedPosition = null;
-    	}
-    	
+        if (this.hasNext()) {
+            currentMarkedPosition = this.nextPosition();
+        } else {
+            currentMarkedPosition = null;
+        }
+
     }
-    
+
     /**
-     * Returns the string content for characters bounded by the previous marked position and the position of the currentToken (inclusive).
-     * Method also marks() the new position the the currentToken.
+     * Returns the string content for characters bounded by the previous marked position and the position of the currentToken
+     * (inclusive). Method also marks() the new position the the currentToken.
      * 
-     * @return the string content for characters bounded by the previous marked position and the position of the currentToken (inclusive).
+     * @return the string content for characters bounded by the previous marked position and the position of the currentToken
+     *         (inclusive).
      */
     public String getMarkedContent() {
-    	Position startPosition = 
-    		new Position( currentMarkedPosition.getIndexInContent(), 
-    						currentMarkedPosition.getLine(),
-    						currentMarkedPosition.getColumn() );
-    	
-    	mark();
-    	
-    	return getContentBetween(startPosition, currentMarkedPosition);
+        Position startPosition = new Position(currentMarkedPosition.getIndexInContent(), currentMarkedPosition.getLine(),
+                                              currentMarkedPosition.getColumn());
+
+        mark();
+
+        return getContentBetween(startPosition, currentMarkedPosition);
     }
-    
+
     /**
      * Obtain a ddl {@link DdlTokenizer} implementation that ignores whitespace but includes tokens for individual symbols, the
      * period ('.'), single-quoted strings, double-quoted strings, whitespace-delimited words, and optionally comments.
@@ -259,18 +251,18 @@
     public static DdlTokenizer ddlTokenizer( boolean includeComments ) {
         return new DdlTokenizer(includeComments);
     }
-    
-    public static class  DdlTokenizer implements Tokenizer {
-    	public static final String PARSER_ID = "PARSER_ID";
-    	
+
+    public static class DdlTokenizer implements Tokenizer {
+        public static final String PARSER_ID = "PARSER_ID";
+
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that represent an unquoted string containing a character sequence made
-         * up of non-whitespace and non-symbol characters.
+         * The {@link TokenStream.Token#type() token type} for tokens that represent an unquoted string containing a character
+         * sequence made up of non-whitespace and non-symbol characters.
          */
         public static final int WORD = 1;
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that consist of an individual "symbol" character. The set of characters
-         * includes: <code>-(){}*,;+%?$[]!<>|=:</code>
+         * The {@link TokenStream.Token#type() token type} for tokens that consist of an individual "symbol" character. The set of
+         * characters includes: <code>-(){}*,;+%?$[]!<>|=:</code>
          */
         public static final int SYMBOL = 2;
         /**
@@ -278,68 +270,62 @@
          */
         public static final int DECIMAL = 4;
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters within single-quotes. Single quote
-         * characters are included if they are preceded (escaped) by a '\' character.
+         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters within single-quotes.
+         * Single quote characters are included if they are preceded (escaped) by a '\' character.
          */
         public static final int SINGLE_QUOTED_STRING = 8;
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters within double-quotes. Double quote
-         * characters are included if they are preceded (escaped) by a '\' character.
+         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters within double-quotes.
+         * Double quote characters are included if they are preceded (escaped) by a '\' character.
          */
         public static final int DOUBLE_QUOTED_STRING = 16;
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters between "/*" and "&#42;/" or between
-         * "//" and the next line terminator (e.g., '\n', '\r' or "\r\n").
+         * The {@link TokenStream.Token#type() token type} for tokens that consist of all the characters between "/*" and "&#42;/"
+         * or between "//" and the next line terminator (e.g., '\n', '\r' or "\r\n").
          */
         public static final int COMMENT = 32;
-        
-    	private final boolean useComments;
-    	
+
+        private final boolean useComments;
+
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that represent key words or reserved words for a given DDL dialect.
-         * 
-		 * <p>
-		 * Examples would be:
-		 * 	 "CREATE", "TABLE", "ALTER", "SCHEMA", "DROP", etc...
-		 * </p>
-		 * 
-		 * see {@link DdlConstants} for the default SQL 92 representations.
+         * The {@link TokenStream.Token#type() token type} for tokens that represent key words or reserved words for a given DDL
+         * dialect.
+         * <p>
+         * Examples would be: "CREATE", "TABLE", "ALTER", "SCHEMA", "DROP", etc...
+         * </p>
+         * see {@link DdlConstants} for the default SQL 92 representations.
          */
         public static final int KEYWORD = 64;
-        
+
         /**
-         * The {@link TokenStream.Token#type() token type} for tokens that represent the start of a DDL statement. 
-         * 
-		 * <p>
-		 * Examples would be:
-		 * 		{"CREATE", "TABLE"}
-		 * 		{"CREATE", "OR", "REPLACE", "VIEW"}
-		 * </p>
-		 * 
-		 * see {@link DdlConstants} for the default SQL 92 representations.
+         * The {@link TokenStream.Token#type() token type} for tokens that represent the start of a DDL statement.
+         * <p>
+         * Examples would be: {"CREATE", "TABLE"} {"CREATE", "OR", "REPLACE", "VIEW"}
+         * </p>
+         * see {@link DdlConstants} for the default SQL 92 representations.
          */
         public static final int STATEMENT_KEY = 128;
-        
-    	public DdlTokenizer(boolean useComments) {
-    		this.useComments = useComments;
-    	}
 
+        public DdlTokenizer( boolean useComments ) {
+            this.useComments = useComments;
+        }
+
         /**
-		 * @return useComments
-		 */
-		public boolean includeComments() {
-			return useComments;
-		}
+         * @return useComments
+         */
+        public boolean includeComments() {
+            return useComments;
+        }
 
-		/**
+        /**
          * {@inheritDoc}
          * 
          * @see org.jboss.dna.common.text.TokenStream.Tokenizer#tokenize(CharacterStream, Tokens)
          */
-    	public void tokenize(CharacterStream input, Tokens tokens)
-    			throws ParsingException {
-    		int startIndex;
-    		int endIndex;
+        public void tokenize( CharacterStream input,
+                              Tokens tokens ) throws ParsingException {
+            int startIndex;
+            int endIndex;
             while (input.hasNext()) {
                 char c = input.next();
                 switch (c) {
@@ -353,8 +339,8 @@
                     // DDL Comments token = "--"
                     // ==============================================================================================
                     case '-': {
-                    	startIndex = input.index();
-                    	Position startPosition = input.position(startIndex);
+                        startIndex = input.index();
+                        Position startPosition = input.position(startIndex);
                         if (input.isNext('-')) {
                             // -- END OF LINE comment ...
                             boolean foundLineTerminator = false;
@@ -368,20 +354,20 @@
                             endIndex = input.index(); // the token won't include the '\n' or '\r' character(s)
                             if (!foundLineTerminator) ++endIndex; // must point beyond last char
                             if (c == '\r' && input.isNext('\n')) input.next();
-                            
+
                             // Check for PARSER_ID
 
                             if (useComments) {
-                            	tokens.addToken(startPosition, startIndex, endIndex, COMMENT);
+                                tokens.addToken(startPosition, startIndex, endIndex, COMMENT);
                             }
-                            
+
                         } else {
-                        	// just a regular dash ...
+                            // just a regular dash ...
                             tokens.addToken(startPosition, startIndex, startIndex + 1, SYMBOL);
                         }
                         break;
                     }
-                    // ==============================================================================================
+                        // ==============================================================================================
                     case '(':
                     case ')':
                     case '{':
@@ -427,15 +413,17 @@
                         endIndex = input.index() + 1; // beyond last character read
                         tokens.addToken(startingPosition, startIndex, endIndex, DOUBLE_QUOTED_STRING);
                         break;
+                    case '\u2019': // '?':
                     case '\'':
+                        char quoteChar = c;
                         startIndex = input.index();
                         startingPosition = input.position(startIndex);
                         foundClosingQuote = false;
                         while (input.hasNext()) {
                             c = input.next();
-                            if (c == '\\' && input.isNext('\'')) {
+                            if (c == '\\' && input.isNext(quoteChar)) {
                                 c = input.next(); // consume the ' character since it is escaped
-                            } else if (c == '\'') {
+                            } else if (c == quoteChar) {
                                 foundClosingQuote = true;
                                 break;
                             }
@@ -448,27 +436,6 @@
                         endIndex = input.index() + 1; // beyond last character read
                         tokens.addToken(startingPosition, startIndex, endIndex, SINGLE_QUOTED_STRING);
                         break;
-                    case '?':
-                        startIndex = input.index();
-                        startingPosition = input.position(startIndex);
-                        foundClosingQuote = false;
-                        while (input.hasNext()) {
-                            c = input.next();
-                            if (c == '\\' && input.isNext('?')) {
-                                c = input.next(); // consume the ' character since it is escaped
-                            } else if (c == '?') {
-                                foundClosingQuote = true;
-                                break;
-                            }
-                        }
-                        if (!foundClosingQuote) {
-                            String msg = CommonI18n.noMatchingSingleQuoteFound.text(startingPosition.getLine(),
-                                                                                    startingPosition.getColumn());
-                            throw new ParsingException(startingPosition, msg);
-                        }
-                        endIndex = input.index() + 1; // beyond last character read
-                        tokens.addToken(startingPosition, startIndex, endIndex, SINGLE_QUOTED_STRING);
-                        break;
                     case '/':
                         startIndex = input.index();
                         startingPosition = input.position(startIndex);
@@ -486,9 +453,9 @@
                             if (!foundLineTerminator) ++endIndex; // must point beyond last char
                             if (c == '\r' && input.isNext('\n')) input.next();
                             if (useComments) {
-                            	tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
-                            
+
                         } else if (input.isNext('*')) {
                             // Multi-line comment ...
                             while (input.hasNext() && !input.isNext('*', '/')) {
@@ -496,10 +463,10 @@
                             }
                             if (input.hasNext()) input.next(); // consume the '*'
                             if (input.hasNext()) input.next(); // consume the '/'
-                            
+
                             endIndex = input.index() + 1; // the token will include the '/' and '*' characters
                             if (useComments) {
-                            	tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
+                                tokens.addToken(startingPosition, startIndex, endIndex, COMMENT);
                             }
 
                         } else {
@@ -518,6 +485,6 @@
                         tokens.addToken(startPosition, startIndex, endIndex, WORD);
                 }
             }
-    	}
+        }
     }
 }

Modified: trunk/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/postgres/PostgresDdlParserTest.java
===================================================================
--- trunk/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/postgres/PostgresDdlParserTest.java	2009-11-26 00:32:58 UTC (rev 1356)
+++ trunk/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/postgres/PostgresDdlParserTest.java	2009-11-26 00:33:28 UTC (rev 1357)
@@ -25,457 +25,446 @@
 
 import static org.jboss.dna.sequencer.ddl.StandardDdlLexicon.TYPE_CREATE_SCHEMA_STATEMENT;
 import static org.jboss.dna.sequencer.ddl.StandardDdlLexicon.TYPE_CREATE_TABLE_STATEMENT;
-import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.*;
+import static org.jboss.dna.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_COLUMN_DEFINITION;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_ALTER_FOREIGN_DATA_WRAPPER_STATEMENT;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_ALTER_TABLE_STATEMENT_POSTGRES;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_COMMENT_ON_STATEMENT;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_CREATE_RULE_STATEMENT;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_CREATE_SEQUENCE_STATEMENT;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_LISTEN_STATEMENT;
+import static org.jboss.dna.sequencer.ddl.dialect.postgres.PostgresDdlLexicon.TYPE_RENAME_COLUMN;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-
 import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.sequencer.ddl.DdlConstants;
 import org.jboss.dna.sequencer.ddl.DdlParserTestHelper;
 import org.jboss.dna.sequencer.ddl.StandardDdlParser;
 import org.jboss.dna.sequencer.ddl.node.AstNode;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
-
 /**
  *
  */
 public class PostgresDdlParserTest extends DdlParserTestHelper {
-	private StandardDdlParser parser;
-	private static final String SPACE = DdlConstants.SPACE;
-	private AstNode rootNode;
-	
-	public static final String DDL_FILE_PATH = "src/test/resources/ddl/dialect/postgres/";
-	
-	
-	@Before
-	public void beforeEach() {
-		parser = new PostgresDdlParser();
-		setPrintToConsole(false);
-    	parser.setTestMode(isPrintToConsole());
-    	parser.setDoUseTerminator(true);
-    	rootNode = parser.nodeFactory().node("ddlRootNode");
-	}
-	
+    private StandardDdlParser parser;
+    private static final String SPACE = DdlConstants.SPACE;
+    private AstNode rootNode;
+
+    public static final String DDL_FILE_PATH = "src/test/resources/ddl/dialect/postgres/";
+
+    @Before
+    public void beforeEach() {
+        parser = new PostgresDdlParser();
+        setPrintToConsole(false);
+        parser.setTestMode(isPrintToConsole());
+        parser.setDoUseTerminator(true);
+        rootNode = parser.nodeFactory().node("ddlRootNode");
+    }
+
     @Test
     public void shouldParseAlterTableMultipleAddColumns() {
-    	printTest("shouldParseAlterTableMultipleAddColumns()");
-        String content = "ALTER TABLE distributors \n"
-        	+ "        ADD COLUMN nick_name varchar(30), \n"
-        	+ "        ADD COLUMN address varchar(30);";
-        
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
-		
-		assertEquals(2, childNode.getChildCount());
+        printTest("shouldParseAlterTableMultipleAddColumns()");
+        String content = "ALTER TABLE distributors \n" + "        ADD COLUMN nick_name varchar(30), \n"
+                         + "        ADD COLUMN address varchar(30);";
 
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
+        assertEquals(2, childNode.getChildCount());
+
     }
-    
+
     @Test
     public void shouldParseAlterTableMultipleMixedActions() {
-    	printTest("shouldParseAlterTableMultipleAddColumns()");
-        String content = "ALTER TABLE distributors \n"
-        	+ "        ADD COLUMN nick_name varchar(30), \n"
-        	+ "        ALTER COLUMN address TYPE varchar(255), \n"
-        	+ "        RENAME COLUMN address TO city, \n"
-        	+ "        DROP COLUMN address;";
-        
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
-		
-		assertEquals(4, childNode.getChildCount());
-		assertTrue(hasMixinType(childNode.getChild(2).getProperty(JcrLexicon.MIXIN_TYPES), TYPE_RENAME_COLUMN));
-		assertTrue(hasMixinType(childNode.getChild(3).getProperty(JcrLexicon.MIXIN_TYPES), TYPE_DROP_COLUMN_DEFINITION));
+        printTest("shouldParseAlterTableMultipleAddColumns()");
+        String content = "ALTER TABLE distributors \n" + "        ADD COLUMN nick_name varchar(30), \n"
+                         + "        ALTER COLUMN address TYPE varchar(255), \n" + "        RENAME COLUMN address TO city, \n"
+                         + "        DROP COLUMN address;";
 
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
+        assertEquals(4, childNode.getChildCount());
+        assertTrue(hasMixinType(childNode.getChild(2).getProperty(JcrLexicon.MIXIN_TYPES), TYPE_RENAME_COLUMN));
+        assertTrue(hasMixinType(childNode.getChild(3).getProperty(JcrLexicon.MIXIN_TYPES), TYPE_DROP_COLUMN_DEFINITION));
+
     }
 
     @Test
     public void shouldParseAlterTableMultipleAlterColumns() {
-    	printTest("shouldParseAlterTableMultipleAlterColumns()");
-    	String content = "ALTER TABLE distributors ALTER COLUMN address TYPE varchar(80), ALTER COLUMN name TYPE varchar(100);";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+        printTest("shouldParseAlterTableMultipleAlterColumns()");
+        String content = "ALTER TABLE distributors ALTER COLUMN address TYPE varchar(80), ALTER COLUMN name TYPE varchar(100);";
 
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
     }
-    
+
     @Test
     public void shouldParseAlterTableMultipeAlterColumns_2() {
-    	printTest("shouldParseAlterTableMultipeAlterColumns_2()");
-        String content = "ALTER TABLE foo" + SPACE +
-	        "ALTER COLUMN foo_timestamp DROP DEFAULT," + SPACE +
-	        "ALTER COLUMN foo_timestamp TYPE timestamp with time zone" + SPACE + 
-	        "USING timestamp with time zone ?epoch? + foo_timestamp *GO interval ?1 second?," + SPACE + 
-	        "ALTER COLUMN foo_timestamp SET DEFAULT now();";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+        printTest("shouldParseAlterTableMultipeAlterColumns_2()");
+        String content = "ALTER TABLE foo" + SPACE + "ALTER COLUMN foo_timestamp DROP DEFAULT," + SPACE
+                         + "ALTER COLUMN foo_timestamp TYPE timestamp with time zone" + SPACE
+                         + "USING timestamp with time zone ?epoch? + foo_timestamp *GO interval ?1 second?," + SPACE
+                         + "ALTER COLUMN foo_timestamp SET DEFAULT now();";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
     }
 
     @Test
     public void shouldParseAlterTableMultipeColumns_3() {
-    	printTest("shouldParseAlterTableMultipeColumns_3()");
-        String content = "ALTER TABLE foo" + SPACE +
-	        "ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone" + SPACE +
-	        "USING timestamp with time zone ?epoch? + foo_timestamp * interval ?1 second?;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+        printTest("shouldParseAlterTableMultipeColumns_3()");
+        String content = "ALTER TABLE foo" + SPACE + "ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone" + SPACE
+                         + "USING timestamp with time zone ?epoch? + foo_timestamp * interval ?1 second?;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
     }
-    
+
     @Test
     public void shouldParseAlterTableMultipeColumns_4() {
-    	printTest("shouldParseAlterTableMultipeColumns_4()");
+        printTest("shouldParseAlterTableMultipeColumns_4()");
         String content = "ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
     }
-    
 
     @Test
     public void shouldParseAlterTableMultipeColumns_5() {
-    	printTest("shouldParseAlterTableMultipeColumns_5()");
+        printTest("shouldParseAlterTableMultipeColumns_5()");
         String content = "ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
     }
-    
+
     @Test
     public void shouldParseAlterTableMultipeColumns_6() {
-    	printTest("shouldParseAlterTableMultipeColumns_6()");
+        printTest("shouldParseAlterTableMultipeColumns_6()");
         String content = "ALTER TABLE distributors SET TABLESPACE fasttablespace;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_TABLE_STATEMENT_POSTGRES));
     }
-    
+
     @Test
     public void shouldParseCreateSchema() {
-    	printTest("shouldParseCreateSchema()");
-        String content = "CREATE SCHEMA hollywood" + SPACE +
-	        "CREATE TABLE films (title text, release date, awards text[])" + SPACE +
-	        "CREATE VIEW winners AS SELECT title, release FROM films WHERE awards IS NOT NULL;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());  // SCHEMA
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_SCHEMA_STATEMENT));
+        printTest("shouldParseCreateSchema()");
+        String content = "CREATE SCHEMA hollywood" + SPACE + "CREATE TABLE films (title text, release date, awards text[])"
+                         + SPACE + "CREATE VIEW winners AS SELECT title, release FROM films WHERE awards IS NOT NULL;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount()); // SCHEMA
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_SCHEMA_STATEMENT));
     }
-    
+
     @Test
     public void shouldParseCreateSequence() {
-    	printTest("shouldParseCreateSequence()");
+        printTest("shouldParseCreateSequence()");
         String content = "CREATE SEQUENCE serial START 101;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_SEQUENCE_STATEMENT));
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_SEQUENCE_STATEMENT));
     }
 
-//    CREATE TABLE films (
-//    	    code        char(5) CONSTRAINT firstkey PRIMARY KEY,
-//    	    title       varchar(40) NOT NULL,
-//    	    did         integer NOT NULL,
-//    	    date_prod   date,
-//    	    kind        varchar(10),
-//    	    len         interval hour to minute
-//    	);
-   
-	@Test
-	public void shouldParseCreateTable_1() {
-		printTest("shouldParseCreateTable_1()");
-		String content = "CREATE TABLE films (" + SPACE + 
-		"code        char(5) CONSTRAINT firstkey PRIMARY KEY," + SPACE + 
-		"title       varchar(40) NOT NULL," + SPACE + 
-		"did         integer NOT NULL," + SPACE + 
-		"date_prod   date," + SPACE + 
-		"kind        varchar(10)," + SPACE + 
-		"len         interval hour to minute" + SPACE + 
-		");";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
-	}
-    
-//	CREATE TABLE distributors (
-//		     did    integer PRIMARY KEY DEFAULT nextval(?serial?),
-//		     name      varchar(40) NOT NULL CHECK (name <> ?)
-//		     
-//		);
-	
-	@Test
-	public void shouldParseCreateTable_2() {
-		printTest("shouldParseCreateTable_2()");
-		String content = "CREATE TABLE distributors (" + SPACE + 
-		"did    integer PRIMARY KEY DEFAULT nextval(?serial?)," + SPACE + 
-		"name      varchar(40) NOT NULL CHECK (name <> ?)" + SPACE + 
-		");";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
-	}
-	
-//	CREATE TABLE distributors (
-//		    name      varchar(40) DEFAULT ?Luso Films?,
-//		    did       integer DEFAULT nextval(?distributors_serial?),
-//		    modtime   timestamp DEFAULT current_timestamp
-//		);
-	
-	@Test
-	public void shouldParseCreateTable_3() {
-		printTest("shouldParseCreateTable_3()");
-		String content = "CREATE TABLE distributors (" + SPACE + 
-		"name      varchar(40) DEFAULT 'xxxx yyyy'," + SPACE + 
-		"name      varchar(40) DEFAULT ?Luso Films?," + SPACE + 
-		"did       integer DEFAULT nextval(?distributors_serial?)," + SPACE + 
-		"modtime   timestamp DEFAULT current_timestamp" + SPACE + 
-		");";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
-	}
-	
-//	CREATE TABLE films_recent AS
-//	  SELECT * FROM films WHERE date_prod >= ?2002-01-01?;
-	
-	
-	@Test
-	public void shouldParseCreateTable_4() {
-		printTest("shouldParseCreateTable_4()");
-		String content = "CREATE TABLE films_recent AS" + SPACE + 
-		"SELECT * FROM films WHERE date_prod >= ?2002-01-01?;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
-	}
-	
-	//LISTEN virtual;
-	
-	@Test
-	public void shouldParseListen() {
-		printTest("shouldParseListen()");
-		String content = "LISTEN virtual;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_LISTEN_STATEMENT));
-	}
-	
-	//	CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS
-	//	  EXECUTE recentfilms(?2002-01-01?);
-		
-	@Test
-	public void shouldParseCreateTempTable() {
-		printTest("shouldParseCreateTempTable()");
-		String content = "CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS EXECUTE recentfilms(?2002-01-01?);";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
-		
-	}
-	
-	
-	//	CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;
-	@Test
-	public void shouldParseCreateRule() {
-		printTest("shouldParseCreateRule()");
-		String content = "CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_RULE_STATEMENT));
-		
-	}
-	
-	@Test
-	public void shouldParseAlterForeignDataWrapper() {
-		printTest("");
-		String content = "ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo ?1?, DROP ?bar?);";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_FOREIGN_DATA_WRAPPER_STATEMENT));
-	}
-	@Test
-	public void shouldParseCommentOn() {
-		printTest("shouldParseCommentOn()");
-		String content = "COMMENT ON TABLE mytable IS ?This is my table.?;";
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
-		AstNode childNode = rootNode.getChildren().get(0);
-		assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_COMMENT_ON_STATEMENT));
-	}
-	
-	
+    // CREATE TABLE films (
+    // code char(5) CONSTRAINT firstkey PRIMARY KEY,
+    // title varchar(40) NOT NULL,
+    // did integer NOT NULL,
+    // date_prod date,
+    // kind varchar(10),
+    // len interval hour to minute
+    // );
+
     @Test
+    public void shouldParseCreateTable_1() {
+        printTest("shouldParseCreateTable_1()");
+        String content = "CREATE TABLE films (" + SPACE + "code        char(5) CONSTRAINT firstkey PRIMARY KEY," + SPACE
+                         + "title       varchar(40) NOT NULL," + SPACE + "did         integer NOT NULL," + SPACE
+                         + "date_prod   date," + SPACE + "kind        varchar(10)," + SPACE
+                         + "len         interval hour to minute" + SPACE + ");";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
+    }
+
+    // CREATE TABLE distributors (
+    // did integer PRIMARY KEY DEFAULT nextval(?serial?),
+    // name varchar(40) NOT NULL CHECK (name <> ?)
+    //		     
+    // );
+
+    @Test
+    public void shouldParseCreateTable_2() {
+        printTest("shouldParseCreateTable_2()");
+        String content = "CREATE TABLE distributors (" + SPACE + "did    integer PRIMARY KEY DEFAULT nextval(?serial?)," + SPACE
+                         + "name      varchar(40) NOT NULL CHECK (name <> ?)" + SPACE + ");";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
+    }
+
+    // CREATE TABLE distributors (
+    // name varchar(40) DEFAULT ?Luso Films?,
+    // did integer DEFAULT nextval(?distributors_serial?),
+    // modtime timestamp DEFAULT current_timestamp
+    // );
+
+    @Ignore
+    @Test
+    public void shouldParseCreateTable_3() {
+        printTest("shouldParseCreateTable_3()");
+        String content = "CREATE TABLE distributors (" + SPACE + "name      varchar(40) DEFAULT 'xxxx yyyy'," + SPACE
+                         + "name      varchar(40) DEFAULT ?Luso Films?," + SPACE
+                         + "did       integer DEFAULT nextval(?distributors_serial?)," + SPACE
+                         + "modtime   timestamp DEFAULT current_timestamp" + SPACE + ");";
+        boolean success = parser.parse(content, rootNode);
+        System.out.println(parser.getProblems());
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
+    }
+
+    // CREATE TABLE films_recent AS
+    // SELECT * FROM films WHERE date_prod >= ?2002-01-01?;
+
+    @Test
+    public void shouldParseCreateTable_4() {
+        printTest("shouldParseCreateTable_4()");
+        String content = "CREATE TABLE films_recent AS" + SPACE + "SELECT * FROM films WHERE date_prod >= ?2002-01-01?;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
+    }
+
+    // LISTEN virtual;
+
+    @Test
+    public void shouldParseListen() {
+        printTest("shouldParseListen()");
+        String content = "LISTEN virtual;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_LISTEN_STATEMENT));
+    }
+
+    // CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS
+    // EXECUTE recentfilms(?2002-01-01?);
+
+    @Test
+    public void shouldParseCreateTempTable() {
+        printTest("shouldParseCreateTempTable()");
+        String content = "CREATE TEMP TABLE films_recent WITH (OIDS) ON COMMIT DROP AS EXECUTE recentfilms(?2002-01-01?);";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_TABLE_STATEMENT));
+
+    }
+
+    // CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;
+    @Test
+    public void shouldParseCreateRule() {
+        printTest("shouldParseCreateRule()");
+        String content = "CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_CREATE_RULE_STATEMENT));
+
+    }
+
+    @Test
+    public void shouldParseAlterForeignDataWrapper() {
+        printTest("");
+        String content = "ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo ?1?, DROP ?bar?);";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_ALTER_FOREIGN_DATA_WRAPPER_STATEMENT));
+    }
+
+    @Test
+    public void shouldParseCommentOn() {
+        printTest("shouldParseCommentOn()");
+        String content = "COMMENT ON TABLE mytable IS ?This is my table.?;";
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
+        AstNode childNode = rootNode.getChildren().get(0);
+        assertTrue(hasMixinType(childNode.getProperty(JcrLexicon.MIXIN_TYPES), TYPE_COMMENT_ON_STATEMENT));
+    }
+
+    @Test
     public void shouldParseCreateFunctionWithMultipleSemicolons() {
-    	printTest("shouldParseCreateFunctionWithMultipleSemicolons()");
-        String content = "CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$ BEGIN RETURN i + 1; END;" + SPACE 
-        + "CREATE TABLE tblName_A (col_1 varchar(255));";
+        printTest("shouldParseCreateFunctionWithMultipleSemicolons()");
+        String content = "CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$ BEGIN RETURN i + 1; END;" + SPACE
+                         + "CREATE TABLE tblName_A (col_1 varchar(255));";
 
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(2, rootNode.getChildCount());
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(2, rootNode.getChildCount());
     }
-    
+
     @Test
     public void shouldParseLockTable() {
-    	printTest("shouldParseLockTable()");
+        printTest("shouldParseLockTable()");
         String content = "LOCK TABLE films IN SHARE MODE;";
 
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
     }
-    
+
     @Test
     public void shouldParsePrepareStatement() {
-    	printTest("shouldParsePrepareStatement()");
+        printTest("shouldParsePrepareStatement()");
         String content = "PREPARE fooplan (int, text, bool, numeric) AS INSERT INTO foo VALUES($1, $2, $3, $4);";
 
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
     }
-    
+
     @Test
     public void shouldParseDropDomain() {
-    	printTest("shouldParseDropDomain()");
+        printTest("shouldParseDropDomain()");
         String content = "DROP DOMAIN IF EXISTS domain_name CASCADE;";
 
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(1, rootNode.getChildCount());
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(1, rootNode.getChildCount());
     }
-    
+
     @Test
     public void shouldParseDropTableMultiple() {
-    	printTest("shouldParseDropDomain()");
+        printTest("shouldParseDropDomain()");
         String content = "DROP TABLE films, distributors;";
 
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(2, rootNode.getChildCount());
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(2, rootNode.getChildCount());
     }
-    
-	@Test
-	public void shouldParsePostgresStatements_1() {
-		printTest("shouldParsePostgresStatements_1()");
-	  	String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_1.ddl");
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(82, rootNode.getChildCount());
-	}
-	
-	@Test
-	public void shouldParsePostgresStatements_2() {
-		printTest("shouldParsePostgresStatements_2()");
-	  	String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_2.ddl");
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(101, rootNode.getChildCount());
-	}
-	
-	@Test
-	public void shouldParsePostgresStatements_3() {
-		printTest("shouldParsePostgresStatements_3()");
-	  	String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_3.ddl");
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(143, rootNode.getChildCount());
-	}
-	
-	@Test
-	public void shouldParsePostgresStatements_4() {
-		printTest("shouldParsePostgresStatements_4()");
-	  	String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_4.ddl");
-    	
-    	boolean success = parser.parse(content, rootNode);
-    	
-    	assertEquals(true, success);
-    	assertEquals(34, rootNode.getChildCount());
-	}
-}
\ No newline at end of file
+
+    @Test
+    public void shouldParsePostgresStatements_1() {
+        printTest("shouldParsePostgresStatements_1()");
+        String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_1.ddl");
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(82, rootNode.getChildCount());
+    }
+
+    @Ignore
+    @Test
+    public void shouldParsePostgresStatements_2() {
+        printTest("shouldParsePostgresStatements_2()");
+        String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_2.ddl");
+
+        boolean success = parser.parse(content, rootNode);
+        System.out.println(parser.getProblems());
+        assertEquals(true, success);
+        assertEquals(101, rootNode.getChildCount());
+    }
+
+    @Test
+    public void shouldParsePostgresStatements_3() {
+        printTest("shouldParsePostgresStatements_3()");
+        String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_3.ddl");
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(143, rootNode.getChildCount());
+    }
+
+    @Test
+    public void shouldParsePostgresStatements_4() {
+        printTest("shouldParsePostgresStatements_4()");
+        String content = getFileContent(DDL_FILE_PATH + "postgres_test_statements_4.ddl");
+
+        boolean success = parser.parse(content, rootNode);
+
+        assertEquals(true, success);
+        assertEquals(34, rootNode.getChildCount());
+    }
+}


From dna-commits at lists.jboss.org  Wed Nov 25 19:36:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 25 Nov 2009 19:36:08 -0500
Subject: [dna-commits] DNA SVN: r1358 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/federation
	and 9 other directories.
Message-ID: <200911260036.nAQ0a8Zn001913@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-25 19:36:08 -0500 (Wed, 25 Nov 2009)
New Revision: 1358

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
Log:
DNA-549 Added fields to the UpdatePropertiesRequest and SetPropertyRequest to record which properties were created vs changed. Since this is a change to the request classes involving new methods that must be called when processing these requests, all the connectors were checked and changed if required. All tests pass.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -85,6 +85,7 @@
     public static I18n actualNewLocationMustHaveSameParentAsOldLocation;
     public static I18n actualNewLocationMustHaveSameNameAsRequest;
     public static I18n requestIsFrozenAndMayNotBeChanged;
+    public static I18n propertyIsNotPartOfRequest;
 
     public static I18n errorImportingContent;
     public static I18n unableToFindRepositorySourceWithName;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -213,7 +213,9 @@
     public void process( SetPropertyRequest request ) {
         SetPropertyRequest source = (SetPropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
         if (checkErrorOrCancel(request, source)) return;
+        // Set the actual location and created flags ...
         request.setActualLocationOfNode(source.getActualLocationOfNode());
+        request.setNewProperty(source.isNewProperty());
     }
 
     /**
@@ -305,6 +307,7 @@
         UpdatePropertiesRequest source = (UpdatePropertiesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
         if (checkErrorOrCancel(request, source)) return;
         request.setActualLocationOfNode(source.getActualLocationOfNode());
+        request.setNewProperties(source.getNewPropertyNames());
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -863,6 +863,7 @@
         if (checkErrorOrCancel(request, source)) return;
         Location sourceLocation = source.getActualLocationOfNode();
         request.setActualLocationOfNode(projectToFederated(request.on(), projected.getProjection(), sourceLocation, request));
+        request.setNewProperties(source.getNewPropertyNames());
     }
 
     /**
@@ -876,8 +877,10 @@
         assert !projected.hasNext();
         SetPropertyRequest source = (SetPropertyRequest)projected.getRequest();
         if (checkErrorOrCancel(request, source)) return;
+        // Set the actual location and created flags ...
         Location sourceLocation = source.getActualLocationOfNode();
         request.setActualLocationOfNode(projectToFederated(request.on(), projected.getProjection(), sourceLocation, request));
+        request.setNewProperty(source.isNewProperty());
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -365,7 +365,10 @@
             }
             Name propName = property.getName();
             if (!propName.equals(DnaLexicon.UUID)) {
-                node.getProperties().put(propName, property);
+                if (node.getProperties().put(propName, property) == null) {
+                    // It is a new property ...
+                    request.setNewProperty(propName);
+                }
             }
         }
         Location actualLocation = getActualLocation(request.on(), node);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -151,13 +151,17 @@
 
     /**
      * Freeze this request to prevent any further modification. This method does nothing if the request is already frozen.
+     * 
+     * @return true if this request was frozen, or false if it was already frozen
      */
-    public void freeze() {
+    public boolean freeze() {
         if (frozen.compareAndSet(false, true)) {
             // Was not already frozen, so decrement the latch (atomically)
             CountDownLatch latch = this.freezingLatch;
             if (latch != null) latch.countDown();
+            return true;
         }
+        return false;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -43,6 +43,7 @@
     private final String workspaceName;
     private final Property property;
     private Location actualLocation;
+    private boolean actualCreation;
 
     /**
      * Create a request to set the property on the node at the supplied location.
@@ -131,6 +132,27 @@
     }
 
     /**
+     * Record that the property did not exist prior to the processing of this request and was actually created by this request.
+     * This method must be called when processing the request, and the actual location must have a {@link Location#getPath() path}
+     * .
+     * 
+     * @param created true if the property was created by this request, or false if this request updated an existing property
+     * @throws IllegalStateException if the request is frozen
+     */
+    public void setNewProperty( boolean created ) {
+        this.actualCreation = true;
+    }
+
+    /**
+     * Get whether the {@link #property() property} was created.
+     * 
+     * @return true if this request created the property, or false if this request changed an existing property
+     */
+    public boolean isNewProperty() {
+        return actualCreation;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -24,7 +24,9 @@
 package org.jboss.dna.graph.request;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.GraphI18n;
@@ -58,6 +60,7 @@
     private final Location on;
     private final String workspaceName;
     private final Map<Name, Property> properties;
+    private Set<Name> createdPropertyNames;
     private Location actualLocation;
 
     /**
@@ -125,6 +128,9 @@
      * @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
      *         as the {@link #on() current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
+     * @see #setNewProperties(Iterable)
+     * @see #setNewProperties(Name...)
+     * @see #setNewProperty(Name)
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
@@ -148,8 +154,124 @@
     }
 
     /**
+     * Record that the named property did not exist prior to the processing of this request and was actually created by this
+     * request. This method (or one of its sibling methods) must be called at least once when processing the request, and may be
+     * called repeatedly for additional properties.
+     * 
+     * @param nameOfCreatedProperty the name of one of the {@link #properties() properties} that was created by this request
+     * @throws IllegalStateException if the request is frozen
+     * @throws IllegalArgumentException if the name is null or if it is not the name of one of the {@link #properties()
+     *         properties}
+     * @see #setActualLocationOfNode(Location)
+     * @see #setNewProperties(Name...)
+     * @see #setNewProperties(Iterable)
+     */
+    public void setNewProperty( Name nameOfCreatedProperty ) {
+        CheckArg.isNotNull(nameOfCreatedProperty, "nameOfCreatedProperty");
+        checkNotFrozen();
+        if (!properties().containsKey(nameOfCreatedProperty)) {
+            throw new IllegalStateException(GraphI18n.propertyIsNotPartOfRequest.text(nameOfCreatedProperty, this));
+        }
+        if (createdPropertyNames == null) createdPropertyNames = new HashSet<Name>();
+        createdPropertyNames.add(nameOfCreatedProperty);
+    }
+
+    /**
+     * Record that the named properties did not exist prior to the processing of this request and were actually created by this
+     * request. This method (or one of its sibling methods) must be called at least once when processing the request, and may be
+     * called repeatedly for additional properties.
+     * 
+     * @param nameOfCreatedProperties the names of the {@link #properties() properties} that were created by this request
+     * @throws IllegalStateException if the request is frozen
+     * @throws IllegalArgumentException if the name is null or if it is not the name of one of the {@link #properties()
+     *         properties}
+     * @see #setActualLocationOfNode(Location)
+     * @see #setNewProperties(Iterable)
+     * @see #setNewProperty(Name)
+     */
+    public void setNewProperties( Name... nameOfCreatedProperties ) {
+        checkNotFrozen();
+        if (createdPropertyNames == null) createdPropertyNames = new HashSet<Name>();
+        for (Name name : nameOfCreatedProperties) {
+            if (name != null) {
+                if (!properties().containsKey(name)) {
+                    throw new IllegalStateException(GraphI18n.propertyIsNotPartOfRequest.text(name, this));
+                }
+                createdPropertyNames.add(name);
+            }
+        }
+    }
+
+    /**
+     * Record that the named properties did not exist prior to the processing of this request and were actually created by this
+     * request. This method (or one of its sibling methods) must be called at least once when processing the request, and may be
+     * called repeatedly for additional properties.
+     * 
+     * @param nameOfCreatedProperties the names of the {@link #properties() properties} that were created by this request
+     * @throws IllegalStateException if the request is frozen
+     * @throws IllegalArgumentException if any of the names are not in the updated {@link #properties() properties}
+     * @see #setActualLocationOfNode(Location)
+     * @see #setNewProperties(Name...)
+     * @see #setNewProperty(Name)
+     */
+    public void setNewProperties( Iterable<Name> nameOfCreatedProperties ) {
+        checkNotFrozen();
+        if (nameOfCreatedProperties == null) return;
+        if (createdPropertyNames == null) createdPropertyNames = new HashSet<Name>();
+        for (Name name : nameOfCreatedProperties) {
+            if (name != null) {
+                if (!properties().containsKey(name)) {
+                    throw new IllegalStateException(GraphI18n.propertyIsNotPartOfRequest.text(name, this));
+                }
+                createdPropertyNames.add(name);
+            }
+        }
+    }
+
+    /**
+     * Get the names of the {@link #properties() properties} that were created by this request.
+     * 
+     * @return the names of the properties
+     */
+    public Set<Name> getNewPropertyNames() {
+        return createdPropertyNames;
+    }
+
+    /**
+     * Determine whether the named property was created by this request
+     * 
+     * @param name the property name
+     * @return true if the named property was created by the request, or false otherwise
+     */
+    public boolean isNewProperty( Name name ) {
+        return createdPropertyNames != null && createdPropertyNames.contains(name);
+    }
+
+    /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.Request#freeze()
+     */
+    @Override
+    public boolean freeze() {
+        if (super.freeze()) {
+            if (createdPropertyNames != null) {
+                if (createdPropertyNames.isEmpty()) {
+                    createdPropertyNames = Collections.emptySet();
+                } else if (createdPropertyNames.size() == 1) {
+                    createdPropertyNames = Collections.singleton(createdPropertyNames.iterator().next());
+                } else {
+                    createdPropertyNames = Collections.unmodifiableSet(createdPropertyNames);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -746,8 +746,9 @@
         if (update.hasError()) {
             request.setError(update.getError());
         } else {
-            // Set the actual location ...
+            // Set the actual location and created flags ...
             request.setActualLocationOfNode(update.getActualLocationOfNode());
+            request.setNewProperty(update.isNewProperty(property.getName()));
         }
     }
 

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-11-26 00:36:08 UTC (rev 1358)
@@ -73,6 +73,7 @@
 actualNewLocationMustHaveSameParentAsOldLocation = The new location of {0} must be a sibling of the old location of {1}
 actualNewLocationMustHaveSameNameAsRequest = The new location of {0} must have the same name as in the request ({1})
 requestIsFrozenAndMayNotBeChanged = Request is frozen and may not be changed: {0}
+propertyIsNotPartOfRequest = The "{0}" property is not part of the request: {1}
 
 errorImportingContent = Error importing {0} content from {1}
 unableToFindRepositorySourceWithName = Unable to find a repository source named "{0}"

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -1304,6 +1304,7 @@
         public void process( UpdatePropertiesRequest request ) {
             // Just update the actual location
             request.setActualLocationOfNode(actualLocationOf(request.on()));
+            request.setNewProperties();
         }
 
         @Override

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/CustomPropertiesFactory.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -27,6 +27,7 @@
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Set;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrLexicon;
@@ -106,13 +107,14 @@
      * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isDirectory()} will always
      *        return true
      * @param properties the properties that are to be set
+     * @return the names of the properties that were created, or an empty or null set if no properties were created on the file
      * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
      */
-    void recordDirectoryProperties( ExecutionContext context,
-                                    String sourceName,
-                                    Location location,
-                                    File file,
-                                    Map<Name, Property> properties ) throws RepositorySourceException;
+    Set<Name> recordDirectoryProperties( ExecutionContext context,
+                                         String sourceName,
+                                         Location location,
+                                         File file,
+                                         Map<Name, Property> properties ) throws RepositorySourceException;
 
     /**
      * Record the supplied properties as being set on the designated "nt:file" node.
@@ -123,13 +125,14 @@
      * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isFile()} will always return
      *        true
      * @param properties the properties that are to be set
+     * @return the names of the properties that were created, or an empty or null set if no properties were created on the file
      * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
      */
-    void recordFileProperties( ExecutionContext context,
-                               String sourceName,
-                               Location location,
-                               File file,
-                               Map<Name, Property> properties ) throws RepositorySourceException;
+    Set<Name> recordFileProperties( ExecutionContext context,
+                                    String sourceName,
+                                    Location location,
+                                    File file,
+                                    Map<Name, Property> properties ) throws RepositorySourceException;
 
     /**
      * Record the supplied properties as being set on the designated "nt:resource" node.
@@ -140,12 +143,13 @@
      * @param file the file system object; never null, and both {@link File#exists()} and {@link File#isFile()} will always return
      *        true
      * @param properties the properties that are to be set
+     * @return the names of the properties that were created, or an empty or null set if no properties were created on the file
      * @throws RepositorySourceException if any properties are invalid or cannot be set on these nodes
      */
-    void recordResourceProperties( ExecutionContext context,
-                                   String sourceName,
-                                   Location location,
-                                   File file,
-                                   Map<Name, Property> properties ) throws RepositorySourceException;
+    Set<Name> recordResourceProperties( ExecutionContext context,
+                                        String sourceName,
+                                        Location location,
+                                        File file,
+                                        Map<Name, Property> properties ) throws RepositorySourceException;
 
 }

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -573,30 +573,34 @@
         }
 
         Location location = request.on();
+        Set<Name> createdProperties = null;
         if (target.isFile()) {
             if (path.endsWith(JcrLexicon.CONTENT)) {
-                customPropertiesFactory.recordResourceProperties(getExecutionContext(),
-                                                                 getSourceName(),
-                                                                 location,
-                                                                 target,
-                                                                 request.properties());
+                createdProperties = customPropertiesFactory.recordResourceProperties(getExecutionContext(),
+                                                                                     getSourceName(),
+                                                                                     location,
+                                                                                     target,
+                                                                                     request.properties());
             } else {
-                customPropertiesFactory.recordFileProperties(getExecutionContext(),
-                                                             getSourceName(),
-                                                             location,
-                                                             target,
-                                                             request.properties());
+                createdProperties = customPropertiesFactory.recordFileProperties(getExecutionContext(),
+                                                                                 getSourceName(),
+                                                                                 location,
+                                                                                 target,
+                                                                                 request.properties());
             }
         } else {
             assert target.isDirectory();
-            customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
-                                                              getSourceName(),
-                                                              location,
-                                                              target,
-                                                              request.properties());
+            createdProperties = customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
+                                                                                  getSourceName(),
+                                                                                  location,
+                                                                                  target,
+                                                                                  request.properties());
         }
 
         request.setActualLocationOfNode(location);
+        if (createdProperties != null) {
+            request.setNewProperties(createdProperties);
+        }
     }
 
     /**

Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -699,12 +699,13 @@
          * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordDirectoryProperties(org.jboss.dna.graph.ExecutionContext,
          *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
          */
-        public void recordDirectoryProperties( ExecutionContext context,
-                                               String sourceName,
-                                               Location location,
-                                               File file,
-                                               Map<Name, Property> properties ) throws RepositorySourceException {
+        public Set<Name> recordDirectoryProperties( ExecutionContext context,
+                                                    String sourceName,
+                                                    Location location,
+                                                    File file,
+                                                    Map<Name, Property> properties ) throws RepositorySourceException {
             ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
+            return null;
         }
 
         /**
@@ -713,12 +714,13 @@
          * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordFileProperties(org.jboss.dna.graph.ExecutionContext,
          *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
          */
-        public void recordFileProperties( ExecutionContext context,
-                                          String sourceName,
-                                          Location location,
-                                          File file,
-                                          Map<Name, Property> properties ) throws RepositorySourceException {
+        public Set<Name> recordFileProperties( ExecutionContext context,
+                                               String sourceName,
+                                               Location location,
+                                               File file,
+                                               Map<Name, Property> properties ) throws RepositorySourceException {
             ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
+            return null;
         }
 
         /**
@@ -727,12 +729,13 @@
          * @see org.jboss.dna.connector.filesystem.CustomPropertiesFactory#recordResourceProperties(org.jboss.dna.graph.ExecutionContext,
          *      java.lang.String, org.jboss.dna.graph.Location, java.io.File, java.util.Map)
          */
-        public void recordResourceProperties( ExecutionContext context,
-                                              String sourceName,
-                                              Location location,
-                                              File file,
-                                              Map<Name, Property> properties ) throws RepositorySourceException {
+        public Set<Name> recordResourceProperties( ExecutionContext context,
+                                                   String sourceName,
+                                                   Location location,
+                                                   File file,
+                                                   Map<Name, Property> properties ) throws RepositorySourceException {
             ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
+            return null;
         }
 
         /**

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -909,6 +909,7 @@
     public void process( UpdatePropertiesRequest request ) {
         logger.trace(request.toString());
         Location actualLocation = null;
+        Set<Name> createdProperties = null;
         try {
             // Find the workspace ...
             WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
@@ -950,6 +951,7 @@
                 if (originalData == null) {
                     largeValues = new LargeValueSerializer(entity);
                     numProps = props.size();
+                    createdProperties = props.keySet(); // all properties were created
                     serializer.serializeProperties(oos, numProps, props.values(), largeValues, refs);
                     if (gos != null) gos.finish();
                 } else {
@@ -960,9 +962,16 @@
                     InputStream is = compressed ? new GZIPInputStream(bais) : bais;
                     ObjectInputStream ois = new ObjectInputStream(is);
                     SkippedLargeValues removedValues = new SkippedLargeValues(largeValues);
+                    createdProperties = new HashSet<Name>();
                     try {
                         Serializer.ReferenceValues refValues = refs != null ? refs : Serializer.NO_REFERENCES_VALUES;
-                        numProps = serializer.reserializeProperties(ois, oos, props, largeValues, removedValues, refValues);
+                        numProps = serializer.reserializeProperties(ois,
+                                                                    oos,
+                                                                    props,
+                                                                    largeValues,
+                                                                    removedValues,
+                                                                    createdProperties,
+                                                                    refValues);
                         if (gos != null) gos.finish();
                     } finally {
                         try {
@@ -1025,6 +1034,7 @@
             } catch (NoResultException e) {
                 // there are no properties yet ...
                 createProperties(workspace, actual.uuid, request.properties().values());
+                createdProperties = request.properties().keySet();
             }
 
         } catch (Throwable e) { // Includes PathNotFoundException
@@ -1032,6 +1042,7 @@
             return;
         }
         if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+        request.setNewProperties(createdProperties);
         recordChange(request);
     }
 
@@ -1217,7 +1228,10 @@
         oldUuidsToNewUuids.put(original.getId().getChildUuidString(), newUuid);
 
         if (existingLocation != null && existingLocation.childEntity.getParentUuidString().equals(actualNewParent.uuid)) {
-            if (desiredName == null) desiredName = desiredSegment.getName();
+            if (desiredName == null) {
+                assert desiredSegment != null;
+                desiredName = desiredSegment.getName();
+            }
             NamespaceEntity namespace = NamespaceEntity.findByUri(entities, desiredName.getNamespaceUri());
 
             ChildEntity existingChild = existingLocation.childEntity;
@@ -1238,7 +1252,10 @@
         } else {
             // Now add the new copy of the original ...
             boolean allowSnS = original.getAllowsSameNameChildren();
-            if (desiredName == null) desiredName = desiredSegment.getName();
+            if (desiredName == null) {
+                assert desiredSegment != null;
+                desiredName = desiredSegment.getName();
+            }
             newLocation = addNewChild(intoWorkspace.getId(), actualNewParent, newUuid, desiredName, allowSnS);
         }
 
@@ -2126,8 +2143,8 @@
                 } else {
 
                     ActualLocation actualBeforeLocation = getActualLocation(workspace, beforeLocation);
-                    ActualLocation actualIntoLocation = getActualLocation(workspace,
-                                                                          Location.create(beforeLocation.getPath().getParent()));
+                    ActualLocation actualIntoLocation = getActualLocation(workspace, Location.create(beforeLocation.getPath()
+                                                                                                                   .getParent()));
 
                     actualNewLocation = moveNodeBefore(workspace, actualLocation, actualIntoLocation, actualBeforeLocation);
                 }

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -352,6 +352,7 @@
      * @param updatedProperties the properties that are being updated (or removed, if there are no values); may not be null
      * @param largeValues the interface to use for writing large values; may not be null
      * @param removedLargeValues the interface to use for recording the large values that were removed; may not be null
+     * @param createdProperties the set into which should be placed the names of the properties that were created; may not be null
      * @param references the interface to use for recording which {@link Reference} values were found during serialization, or
      *        null if the references do not need to be accumulated
      * @return the number of properties
@@ -363,18 +364,23 @@
                                       Map<Name, Property> updatedProperties,
                                       LargeValues largeValues,
                                       LargeValues removedLargeValues,
+                                      Set<Name> createdProperties,
                                       ReferenceValues references ) throws IOException, ClassNotFoundException {
         assert input != null;
         assert output != null;
         assert updatedProperties != null;
+        assert createdProperties != null;
         assert largeValues != null;
         assert references != null;
         // Assemble a set of property names to skip deserializing
         Map<Name, Property> allProperties = new HashMap<Name, Property>();
 
+        // Start out by assuming that all properties are new ...
+        createdProperties.addAll(updatedProperties.keySet());
+
         // Read the number of properties ...
         int count = input.readInt();
-        // Deserialize all of the proeprties ...
+        // Deserialize all of the properties ...
         for (int i = 0; i != count; ++i) {
             // Read the property name ...
             String nameStr = (String)input.readObject();
@@ -391,15 +397,18 @@
                 assert property != null;
                 allProperties.put(name, property);
             }
+            // This is an existing property, so remove it from the set of created properties ...
+            createdProperties.remove(name);
         }
 
         // Add all the updated properties ...
         for (Map.Entry<Name, Property> entry : updatedProperties.entrySet()) {
             Property updated = entry.getValue();
+            Name name = entry.getKey();
             if (updated == null) {
-                allProperties.remove(entry.getKey());
+                allProperties.remove(name);
             } else {
-                allProperties.put(updated.getName(), updated);
+                allProperties.put(name, updated);
             }
         }
 

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java	2009-11-26 00:33:28 UTC (rev 1357)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java	2009-11-26 00:36:08 UTC (rev 1358)
@@ -416,15 +416,20 @@
                                          Name[] removedProperties ) throws IOException, ClassNotFoundException {
         Collection<Name> propertiesThatStay = new HashSet<Name>();
         Collection<Name> propertiesThatAreDeleted = new HashSet<Name>();
+        Set<Name> propertiesThatAreNew = new HashSet<Name>();
         for (Property prop : originalProperties) {
             propertiesThatStay.add(prop.getName());
         }
         for (Property prop : updatedProperties) {
-            propertiesThatStay.add(prop.getName());
+            if (propertiesThatStay.add(prop.getName())) {
+                // The property is new since it wasn't in the original set of names ...
+                propertiesThatAreNew.add(prop.getName());
+            }
         }
         for (Name removedPropertyName : removedProperties) {
             propertiesThatAreDeleted.add(removedPropertyName);
             propertiesThatStay.remove(removedPropertyName);
+            assertThat(propertiesThatAreNew.contains(removedPropertyName), is(false));
         }
 
         // Serialize the properties one at a time ...
@@ -439,8 +444,15 @@
         ObjectInputStream ois = new ObjectInputStream(bais);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);
+        Set<Name> createdProperties = new HashSet<Name>();
         try {
-            serializer.reserializeProperties(ois, oos, updatedProps, largeValues, removedLargeValues, references);
+            serializer.reserializeProperties(ois,
+                                             oos,
+                                             updatedProps,
+                                             largeValues,
+                                             removedLargeValues,
+                                             createdProperties,
+                                             references);
         } finally {
             oos.close();
             ois.close();
@@ -460,6 +472,7 @@
         for (Name deleted : propertiesThatAreDeleted) {
             assertThat(namesAfter.contains(deleted), is(false));
         }
+        assertThat(createdProperties, is(propertiesThatAreNew));
     }
 
     protected class SkippedLargeValues implements Serializer.LargeValues {


From dna-commits at lists.jboss.org  Thu Nov 26 15:37:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 15:37:08 -0500
Subject: [dna-commits] DNA SVN: r1359 - in trunk/dna-graph/src:
	main/java/org/jboss/dna/graph/observe and 2 other directories.
Message-ID: <200911262037.nAQKb8aR007214@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-26 15:37:08 -0500 (Thu, 26 Nov 2009)
New Revision: 1359

Added:
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/ExecutionContextTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
Log:
DNA-548 The ExecutionContext serves as the universal mechanism by which the DNA code knows the context in which operations are performed, and is available to the connectors that process the requests, the session that creates the requests, and the Observation system that post-processes the (complete) requests. The JcrRepository.login(...) method always creates a new ExecutionContext via the ExecutionContext.with(...), which means that every JcrSession implementation has a unique ExecutionContext. Therefore, if we add a unique identifier to the ExecutionContext, that identifier would correspond to the Session instance.

This commit adds this identifier to the ExecutionContext, and adds a 'contextId' to the Changes class.  Then, the RequestProcessor was changed to set the context ID for the Changes object that it creates.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java	2009-11-26 00:36:08 UTC (rev 1358)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java	2009-11-26 20:37:08 UTC (rev 1359)
@@ -25,6 +25,7 @@
 
 import java.security.AccessControlContext;
 import java.security.AccessController;
+import java.util.UUID;
 import javax.security.auth.login.LoginException;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.component.ClassLoaderFactory;
@@ -65,6 +66,8 @@
     private final NamespaceRegistry namespaceRegistry;
     private final MimeTypeDetector mimeTypeDetector;
     private final SecurityContext securityContext;
+    /** The unique ID string, which is always generated so that it can be final and not volatile. */
+    private final String id = UUID.randomUUID().toString();
 
     /**
      * Create an instance of an execution context that uses the {@link AccessController#getContext() current JAAS calling context}
@@ -235,6 +238,15 @@
     }
 
     /**
+     * Get the unique identifier for this context.
+     * 
+     * @return the unique identifier string; never null and never empty
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
      * Create a new execution context that mirrors this context but that uses the supplied namespace registry. The resulting
      * context's {@link #getValueFactories() value factories} and {@link #getPropertyFactory() property factory} all make use of
      * the new namespace registry.
@@ -312,7 +324,11 @@
      */
     @Override
     public String toString() {
-        return "Execution context for " + getSecurityContext() == null ? "null" : getSecurityContext().getUserName();
+        StringBuilder sb = new StringBuilder("Execution context for ");
+        if (getSecurityContext() == null) sb.append("null");
+        else sb.append(getSecurityContext().getUserName());
+        sb.append(" (").append(id).append(')');
+        return sb.toString();
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-26 00:36:08 UTC (rev 1358)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-26 20:37:08 UTC (rev 1359)
@@ -27,6 +27,7 @@
 import java.util.Collections;
 import java.util.List;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.SecurityContext;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.request.ChangeRequest;
@@ -40,19 +41,14 @@
     private static final long serialVersionUID = 1L;
 
     protected final String processId;
+    protected final String contextId;
     protected final String userName;
     protected final String sourceName;
     protected final DateTime timestamp;
     protected final List<ChangeRequest> changeRequests;
 
-    public Changes( String userName,
-                    String sourceName,
-                    DateTime timestamp,
-                    List<ChangeRequest> requests ) {
-        this("", userName, sourceName, timestamp, requests);
-    }
-
     public Changes( String processId,
+                    String contextId,
                     String userName,
                     String sourceName,
                     DateTime timestamp,
@@ -64,6 +60,7 @@
         this.timestamp = timestamp;
         this.changeRequests = Collections.unmodifiableList(requests);
         this.processId = processId != null ? processId : "";
+        this.contextId = contextId != null ? contextId : "";
         assert this.userName != null;
         assert this.sourceName != null;
         assert this.timestamp != null;
@@ -77,11 +74,13 @@
         this.timestamp = changes.timestamp;
         this.changeRequests = changes.changeRequests;
         this.processId = changes.processId;
+        this.contextId = changes.contextId;
         assert this.userName != null;
         assert this.sourceName != null;
         assert this.timestamp != null;
         assert this.changeRequests != null;
         assert this.processId != null;
+        assert this.contextId != null;
     }
 
     /**
@@ -122,6 +121,16 @@
     }
 
     /**
+     * Get the {@link ExecutionContext#getId() identifier} of the {@link ExecutionContext} where these changes originated. This
+     * identifier may be useful in preventing feedbacks.
+     * 
+     * @return the context identifier; never null
+     */
+    public String getContextId() {
+        return processId;
+    }
+
+    /**
      * Get the list of changes.
      * 
      * @return the immutable list of change requests; never null and never empty

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-26 00:36:08 UTC (rev 1358)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-11-26 20:37:08 UTC (rev 1359)
@@ -848,7 +848,9 @@
         if (observer != null && !this.changes.isEmpty()) {
             String userName = context.getSecurityContext() != null ? context.getSecurityContext().getUserName() : null;
             if (userName == null) userName = "";
-            Changes changes = new Changes(userName, getSourceName(), getNowInUtc(), this.changes);
+            String contextId = context.getId();
+            String processId = null;
+            Changes changes = new Changes(processId, contextId, userName, getSourceName(), getNowInUtc(), this.changes);
             observer.notify(changes);
         }
     }

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/ExecutionContextTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/ExecutionContextTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/ExecutionContextTest.java	2009-11-26 20:37:08 UTC (rev 1359)
@@ -0,0 +1,77 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ * 
+ * JBoss DNA 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.graph;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.common.component.ClassLoaderFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ExecutionContextTest {
+
+    private ExecutionContext context;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+    }
+
+    @After
+    public void afterEach() {
+        context = null;
+    }
+
+    @Test
+    public void shouldHaveIdentifierThatIsNotNull() {
+        assertThat(context.getId(), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldHaveIdentifierThatIsNotBlank() {
+        assertThat(context.getId().length(), is(not(0)));
+        assertThat(context.getId().trim().length(), is(not(0)));
+    }
+
+    @Test
+    public void shouldHaveIdentifierThatIsUnique() {
+        // Can't really test this, but we certainly can test that there are no duplicates in many contexts ...
+        Set<String> ids = new HashSet<String>();
+        for (int i = 0; i != 50; ++i) {
+            assertThat(ids.add(new ExecutionContext().getId()), is(true));
+        }
+    }
+
+    @Test
+    public void shouldCreateSubcontextsWithDifferentIdentifiers() {
+        ExecutionContext newContext = context.with(mock(ClassLoaderFactory.class));
+        assertThat(newContext.getId(), is(not(context.getId())));
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/ExecutionContextTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Thu Nov 26 15:40:54 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 15:40:54 -0500
Subject: [dna-commits] DNA SVN: r1360 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <200911262040.nAQKescb007855@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-26 15:40:53 -0500 (Thu, 26 Nov 2009)
New Revision: 1360

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
Log:
DNA-548 Added a helper method in JcrSession to access the ExecutionContext.getId().

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-26 20:37:08 UTC (rev 1359)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-26 20:40:53 UTC (rev 1360)
@@ -170,6 +170,7 @@
         if (obj instanceof Changes) {
             Changes that = (Changes)obj;
             if (!this.getProcessId().equals(that.getProcessId())) return false;
+            if (!this.getContextId().equals(that.getContextId())) return false;
             if (!this.getSourceName().equals(that.getSourceName())) return false;
             if (!this.getTimestamp().equals(that.getTimestamp())) return false;
             if (!this.getUserName().equals(that.getUserName())) return false;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-11-26 20:37:08 UTC (rev 1359)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-11-26 20:40:53 UTC (rev 1360)
@@ -185,6 +185,10 @@
         return this.executionContext;
     }
 
+    String sessionId() {
+        return this.executionContext.getId();
+    }
+
     JcrNodeTypeManager nodeTypeManager() {
         return this.workspace.nodeTypeManager();
     }
@@ -204,7 +208,7 @@
     final Collection<String> lockTokens() {
         return lockTokens;
     }
-    
+
     Graph.Batch createBatch() {
         return graph.batch();
     }
@@ -306,11 +310,11 @@
         if (lockTokens.contains(lt)) {
             return;
         }
-        
+
         if (workspace().lockManager().isHeldBySession(lt)) {
             throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lt));
         }
-        
+
         workspace().lockManager().setHeldBySession(lt, true);
         lockTokens.add(lt);
     }
@@ -849,7 +853,7 @@
             lockTokens.remove(lt);
             return;
         }
-        
+
         if (lock.isSessionScoped()) {
             throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lt));
         }


From dna-commits at lists.jboss.org  Thu Nov 26 20:36:53 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 20:36:53 -0500
Subject: [dna-commits] DNA SVN: r1361 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/connector/inmemory and 1 other
	directory.
Message-ID: <200911270136.nAR1arOr031514@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-26 20:36:53 -0500 (Thu, 26 Nov 2009)
New Revision: 1361

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java
Log:
DNA-546 JCR Workspace and Session Imports Can Fail on JPA Connector

Applied patch that makes some preparatory tweaks to the MapRepository code. MapNode is renamed to DefaultMapNode and now implements a new interface named MapNode. Additionally, some bulk mutators are added.

The first part is a change that had been kicked around during the original development of the MapRepository code and, IIRC, was bypassed as one of those "if we need it later, we'll do it" changes. I'm doing it now to enable the creation of a JpaNode for the new JPA connector model. Instead of implementing the MapNode interface by managing state internally (like DefaultMapNode does), the JpaNode will be an adapter over a JPA entity.

The bulk mutators are added to help limit the number of times that the properties for a node need to be reserialized.


Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/AbstractMapWorkspace.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -115,7 +115,7 @@
      */
     protected MapNode createMapNode( UUID uuid ) {
         assert uuid != null;
-        return new MapNode(uuid);
+        return new DefaultMapNode(uuid);
     }
 
     /**
@@ -233,26 +233,30 @@
     }
 
     /**
-     * Removes the given node and its children, correcting the SNS and child indices for its parent.
+     * Removes the given node and its children, correcting the SNS and child indices for its parent. This method will return false
+     * if the given node does not exist in this workspace.
      * 
-     * @param context the current execution context
-     * @param node the node to be removed
+     * @param context the current execution context; may not be null
+     * @param node the node to be removed; may not be null
+     * @return whether a node was removed as a result of this operation
      */
-    public void removeNode( ExecutionContext context,
-                            MapNode node ) {
+    public boolean removeNode( ExecutionContext context,
+                               MapNode node ) {
         assert context != null;
         assert node != null;
+
         if (getRoot().equals(node)) {
             removeAllNodesFromMap();
             // Recreate the root node ...
             addNodeToMap(createMapNode(repository.getRootNodeUuid()));
-            return;
+            return true;
         }
         MapNode parent = node.getParent();
         assert parent != null;
-        parent.getChildren().remove(node);
+        if (!parent.removeChild(node)) return false;
         correctSameNameSiblingIndexes(context, parent, node.getName().getName());
         removeUuidReference(node);
+        return true;
     }
 
     /**
@@ -274,10 +278,12 @@
      * 
      * @param context the environment; may not be null
      * @param pathToNewNode the path to the new node; may not be null
+     * @param properties the properties for the new node
      * @return the new node (or root if the path specified the root)
      */
     public MapNode createNode( ExecutionContext context,
-                               String pathToNewNode ) {
+                               String pathToNewNode,
+                               Iterable<Property> properties ) {
         assert context != null;
         assert pathToNewNode != null;
         Path path = context.getValueFactories().getPathFactory().create(pathToNewNode);
@@ -285,7 +291,7 @@
         Path parentPath = path.getParent();
         MapNode parentNode = getNode(parentPath);
         Name name = path.getLastSegment().getName();
-        return createNode(context, parentNode, name, null);
+        return createNode(context, parentNode, name, null, properties);
     }
 
     /**
@@ -295,25 +301,27 @@
      * @param parentNode the parent node; may not be null
      * @param name the name; may not be null
      * @param uuid the UUID of the node, or null if the UUID is to be generated
+     * @param properties the properties for the new node
      * @return the new node
      */
     public MapNode createNode( ExecutionContext context,
                                MapNode parentNode,
                                Name name,
-                               UUID uuid ) {
+                               UUID uuid,
+                               Iterable<Property> properties ) {
         assert context != null;
         assert name != null;
         if (parentNode == null) parentNode = getRoot();
         if (uuid == null) uuid = UUID.randomUUID();
 
         MapNode node = createMapNode(uuid);
-        addNodeToMap(node);
 
-        node.setParent(parentNode);
         // Find the last node with this same name ...
         int nextIndex = 1;
-        if (parentNode.existingNames.contains(name)) {
-            ListIterator<MapNode> iter = parentNode.getChildren().listIterator(parentNode.getChildren().size());
+        Set<Name> uniqueNames = parentNode.getUniqueChildNames();
+        if (uniqueNames.contains(name)) {
+            List<MapNode> children = parentNode.getChildren();
+            ListIterator<MapNode> iter = children.listIterator(children.size());
             while (iter.hasPrevious()) {
                 MapNode prev = iter.previous();
                 if (prev.getName().getName().equals(name)) {
@@ -322,10 +330,15 @@
                 }
             }
         }
+
         Path.Segment newName = context.getValueFactories().getPathFactory().createSegment(name, nextIndex);
         node.setName(newName);
-        parentNode.getChildren().add(node);
-        parentNode.existingNames.add(name);
+        node.setProperties(properties);
+        node.setParent(parentNode);
+
+        parentNode.addChild(node);
+        parentNode.getUniqueChildNames().add(name);
+        addNodeToMap(node);
         return node;
     }
 
@@ -366,7 +379,7 @@
         }
 
         if (oldParent != null) {
-            boolean removed = oldParent.getChildren().remove(node);
+            boolean removed = oldParent.removeChild(node);
             assert removed == true;
             node.setParent(null);
             correctSameNameSiblingIndexes(context, oldParent, oldName);
@@ -379,10 +392,10 @@
         }
 
         if (beforeNode == null) {
-            newParent.getChildren().add(node);
+            newParent.addChild(node);
         } else {
             int index = newParent.getChildren().indexOf(beforeNode);
-            newParent.getChildren().add(index, node);
+            newParent.addChild(index, node);
         }
         correctSameNameSiblingIndexes(context, newParent, newName);
 
@@ -464,15 +477,14 @@
         // Get or create the new node ...
         Name childName = desiredName != null ? desiredName : original.getName().getName();
         UUID uuidForCopy = reuseUuids ? original.getUuid() : UUID.randomUUID();
-        MapNode copy = newWorkspace.createNode(context, newParent, childName, uuidForCopy);
+
+        MapNode copy = newWorkspace.createNode(context, newParent, childName, uuidForCopy, original.getProperties().values());
+
         if (!reuseUuids) {
             assert oldToNewUuids != null;
             oldToNewUuids.put(original.getUuid(), copy.getUuid());
         }
 
-        // Copy the properties ...
-        copy.getProperties().clear();
-        copy.getProperties().putAll(original.getProperties());
         if (recursive) {
             // Loop over each child and call this method ...
             for (MapNode child : original.getChildren()) {
@@ -553,11 +565,11 @@
             // a node that has the UUID of the original node (as this will be handled later) ...
             for (UUID uuid : uuidsInFromBranch) {
                 if (null != (existing = newWorkspace.getNode(uuid))) {
-                    newWorkspace.removeNode(context, existing);
                     if (removedExistingNodes != null) {
                         Path path = pathFor(pathFactory, existing);
                         removedExistingNodes.add(Location.create(path, uuid));
                     }
+                    newWorkspace.removeNode(context, existing);
                 }
             }
         } else {
@@ -583,14 +595,12 @@
             assert newRoot != null;
 
             newRoot.getProperties().clear();
-            for (MapNode child : newRoot.getChildren()) {
-                newWorkspace.removeNode(context, child);
-            }
+            newRoot.setProperties(original.getProperties().values());
 
-            for (Property property : original.getProperties().values()) {
-                newRoot.setProperty(property);
-            }
+            newRoot.clearChildren();
 
+            assert newRoot.getChildren().isEmpty();
+
             for (MapNode child : original.getChildren()) {
                 copyNode(context, child, newWorkspace, newRoot, null, true, (Map<UUID, UUID>)null);
             }
@@ -600,11 +610,11 @@
         // Now deal with an existing node that has the same UUID as the original node ...
         existing = newWorkspace.getNode(original.getUuid());
         if (existing != null) {
-            newWorkspace.removeNode(context, existing);
             if (removedExistingNodes != null) {
                 Path path = pathFor(pathFactory, existing);
                 removedExistingNodes.add(Location.create(path, original.getUuid()));
             }
+            newWorkspace.removeNode(context, existing);
         }
         return copyNode(context, original, newWorkspace, newParent, desiredName, true, (Map<UUID, UUID>)null);
     }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -0,0 +1,237 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.map;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+
+public class DefaultMapNode implements MapNode {
+
+    private final UUID uuid;
+    private MapNode parent;
+    private Path.Segment name;
+    private final Map<Name, Property> properties = new HashMap<Name, Property>();
+    private final LinkedList<MapNode> children = new LinkedList<MapNode>();
+    final Set<Name> existingNames = new HashSet<Name>();
+
+    public DefaultMapNode( UUID uuid ) {
+        assert uuid != null;
+        this.uuid = uuid;
+    }
+
+    /* (non-Javadoc)
+     * @see org.jboss.dna.graph.connector.map.MapNode#getUuid()
+     */
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    /* (non-Javadoc)
+     * @see org.jboss.dna.graph.connector.map.MapNode#getName()
+     */
+    public Path.Segment getName() {
+        return name;
+    }
+
+    /**
+     * @param name Sets name to the specified value.
+     */
+    public void setName( Path.Segment name ) {
+        this.name = name;
+    }
+
+    public Set<Name> getUniqueChildNames() {
+        return existingNames;
+    }
+
+    /* (non-Javadoc)
+     * @see org.jboss.dna.graph.connector.map.MapNode#getParent()
+     */
+    public MapNode getParent() {
+        return parent;
+    }
+
+    /**
+     * @param parent Sets parent to the specified value.
+     */
+    public void setParent( MapNode parent ) {
+        this.parent = parent;
+    }
+
+    /**
+     * @return children
+     */
+    public LinkedList<MapNode> getChildren() {
+        return children;
+    }
+
+    /**
+     * @return properties
+     */
+    public Map<Name, Property> getProperties() {
+        return properties;
+    }
+
+    @Override
+    public void addChild( int index,
+                          MapNode child ) {
+        children.add(index, child);
+    }
+
+    @Override
+    public void addChild( MapNode child ) {
+        children.add(child);
+
+    }
+
+    @Override
+    public void clearChildren() {
+        children.clear();
+    }
+
+    @Override
+    public boolean removeChild( MapNode child ) {
+        return children.remove(child);
+    }
+
+    @Override
+    public MapNode removeProperty( Name propertyName ) {
+        properties.remove(propertyName);
+        return this;
+    }
+
+    @Override
+    public MapNode setProperties( Iterable<Property> properties ) {
+        for (Property property : properties) {
+            this.properties.put(property.getName(), property);
+        }
+        return this;
+    }
+
+    /**
+     * Sets the property with the given name, overwriting any previous property for the given name
+     * 
+     * @param property the property to set
+     * @return this map node
+     */
+    public MapNode setProperty( Property property ) {
+        if (property != null) {
+            this.properties.put(property.getName(), property);
+        }
+        return this;
+    }
+
+    /**
+     * Sets the property with the given name, overwriting any previous property for the given name
+     * 
+     * @param context the current execution context, used to get a {@link NameFactory name factory} and {@link PropertyFactory
+     *        property factory}.
+     * @param name the name of the property
+     * @param values the values for the property
+     * @return this map node
+     */
+    public MapNode setProperty( ExecutionContext context,
+                                String name,
+                                Object... values ) {
+        PropertyFactory propertyFactory = context.getPropertyFactory();
+        Name propertyName = context.getValueFactories().getNameFactory().create(name);
+        return setProperty(propertyFactory.create(propertyName, values));
+    }
+
+    /**
+     * Returns the named property
+     * 
+     * @param context the current execution context, used to get a {@link NameFactory name factory}
+     * @param name the name of the property to return
+     * @return the property for the given name
+     */
+    public Property getProperty( ExecutionContext context,
+                                 String name ) {
+        Name propertyName = context.getValueFactories().getNameFactory().create(name);
+        return getProperty(propertyName);
+    }
+
+    /**
+     * Returns the named property
+     * 
+     * @param name the name of the property to return
+     * @return the property for the given name
+     */
+    public Property getProperty( Name name ) {
+        return this.properties.get(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return uuid.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof MapNode) {
+            MapNode that = (MapNode)obj;
+            if (!this.getUuid().equals(that.getUuid())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (this.name == null) {
+            sb.append("");
+        } else {
+            sb.append(this.name);
+        }
+        sb.append(" (").append(uuid).append(")");
+        return sb.toString();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapNode.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapNode.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -23,9 +23,7 @@
  */
 package org.jboss.dna.graph.connector.map;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -36,88 +34,105 @@
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.PropertyFactory;
 
-/**
- * A node within a {@link MapRepository}.
- */
-public class MapNode {
+public interface MapNode {
 
-    private final UUID uuid;
-    private MapNode parent;
-    private Path.Segment name;
-    private final Map<Name, Property> properties = new HashMap<Name, Property>();
-    private final LinkedList<MapNode> children = new LinkedList<MapNode>();
-    final Set<Name> existingNames = new HashSet<Name>();
-
-    public MapNode( UUID uuid ) {
-        assert uuid != null;
-        this.uuid = uuid;
-    }
-
-    /* (non-Javadoc)
-     * @see org.jboss.dna.graph.connector.map.MapNode#getUuid()
+    /**
+     * Returns the UUID for this node
+     * 
+     * @return the UUID for this node
      */
-    public UUID getUuid() {
-        return uuid;
-    }
+    public UUID getUuid();
 
-    /* (non-Javadoc)
-     * @see org.jboss.dna.graph.connector.map.MapNode#getName()
+    /**
+     * Returns the name of this node along with its SNS index within its parent's children
+     * 
+     * @return the name of this node along with its SNS index within its parent's children
      */
-    public Path.Segment getName() {
-        return name;
-    }
+    public Path.Segment getName();
 
     /**
      * @param name Sets name to the specified value.
      */
-    public void setName( Path.Segment name ) {
-        this.name = name;
-    }
+    public void setName( Path.Segment name );
 
-    public Set<Name> getUniqueChildNames() {
-        return existingNames;
-    }
+    /**
+     * Returns the set of child names for this node
+     * 
+     * @return the set of child names for this node
+     */
+    public Set<Name> getUniqueChildNames();
 
-    /* (non-Javadoc)
-     * @see org.jboss.dna.graph.connector.map.MapNode#getParent()
+    /**
+     * Returns the parent of this node or null if the node is the root node for its workspace.
+     * 
+     * @return the parent of this node; may be null if the node is the root node for its workspace
      */
-    public MapNode getParent() {
-        return parent;
-    }
+    public MapNode getParent();
 
     /**
      * @param parent Sets parent to the specified value.
      */
-    public void setParent( MapNode parent ) {
-        this.parent = parent;
-    }
+    public void setParent( MapNode parent );
 
     /**
      * @return children
      */
-    public LinkedList<MapNode> getChildren() {
-        return children;
-    }
+    public List<MapNode> getChildren();
 
     /**
-     * @return properties
+     * Removes all of the children for this node in a single operation.
      */
-    protected Map<Name, Property> getProperties() {
-        return properties;
-    }
+    public void clearChildren();
 
     /**
+     * Adds the given child to the end of the list of children for this node
+     * 
+     * @param child the child to add to this node
+     */
+    public void addChild( MapNode child );
+
+    /**
+     * Inserts the specified child at the specified position in the list of children. Shifts the child currently at that position
+     * (if any) and any subsequent children to the right (adds one to their indices).
+     * 
+     * @param index index at which the specified child is to be inserted
+     * @param child the child to be inserted
+     */
+    public void addChild( int index,
+                          MapNode child );
+
+    /**
+     * Removes the given child from the list of children
+     * 
+     * @param child the child to be removed
+     * @return true if the child was one of this node's children (and was removed); false otherwise
+     */
+    public boolean removeChild( MapNode child );
+
+    /**
+     * Returns a map of property names to the property for the given name
+     * 
+     * @return a map of property names to the property for the given name
+     */
+    public Map<Name, Property> getProperties();
+
+    /**
+     * Sets the given properties in a single operation, overwriting any previous properties for the same name This bulk mutator
+     * should be used when multiple properties are being set in order to allow underlying implementations to optimize their access
+     * to their respective persistent storage mechanism.
+     * 
+     * @param properties the properties to set
+     * @return this map node
+     */
+    public MapNode setProperties( Iterable<Property> properties );
+
+    /**
      * Sets the property with the given name, overwriting any previous property for the given name
      * 
      * @param property the property to set
      * @return this map node
      */
-    public MapNode setProperty( Property property ) {
-        if (property != null) {
-            this.properties.put(property.getName(), property);
-        }
-        return this;
-    }
+    public MapNode setProperty( Property property );
 
     /**
      * Sets the property with the given name, overwriting any previous property for the given name
@@ -130,13 +145,17 @@
      */
     public MapNode setProperty( ExecutionContext context,
                                 String name,
-                                Object... values ) {
-        PropertyFactory propertyFactory = context.getPropertyFactory();
-        Name propertyName = context.getValueFactories().getNameFactory().create(name);
-        return setProperty(propertyFactory.create(propertyName, values));
-    }
+                                Object... values );
 
     /**
+     * Removes the property with the given name
+     * 
+     * @param propertyName the name of the property to remove
+     * @return this map node
+     */
+    public MapNode removeProperty( Name propertyName );
+
+    /**
      * Returns the named property
      * 
      * @param context the current execution context, used to get a {@link NameFactory name factory}
@@ -144,10 +163,7 @@
      * @return the property for the given name
      */
     public Property getProperty( ExecutionContext context,
-                                 String name ) {
-        Name propertyName = context.getValueFactories().getNameFactory().create(name);
-        return getProperty(propertyName);
-    }
+                                 String name );
 
     /**
      * Returns the named property
@@ -155,51 +171,6 @@
      * @param name the name of the property to return
      * @return the property for the given name
      */
-    public Property getProperty( Name name ) {
-        return this.properties.get(name);
-    }
+    public Property getProperty( Name name );
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return uuid.hashCode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals( Object obj ) {
-        if (obj == this) return true;
-        if (obj instanceof MapNode) {
-            MapNode that = (MapNode)obj;
-            if (!this.getUuid().equals(that.getUuid())) return false;
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        if (this.name == null) {
-            sb.append("");
-        } else {
-            sb.append(this.name);
-        }
-        sb.append(" (").append(uuid).append(")");
-        return sb.toString();
-    }
-
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -96,6 +96,10 @@
 
     }
 
+    protected String getDefaultWorkspaceName() {
+        return defaultWorkspaceName;
+    }
+
     /**
      * Returns the UUID used by the root nodes in each workspace.
      * <p>

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.graph.connector.map;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -224,16 +225,21 @@
                                                        GraphI18n.inMemoryNodeDoesNotExist.text(parent)));
             return;
         }
+
         UUID uuid = null;
+        // Make a list of the properties that we will store: all props except dna:uuid and jcr:uuid
+        List<Property> propsToStore = new ArrayList<Property>(request.properties().size());
         for (Property property : request.properties()) {
             if (property.getName().equals(DnaLexicon.UUID) || property.getName().equals(JcrLexicon.UUID)) {
                 uuid = getExecutionContext().getValueFactories().getUuidFactory().create(property.getValues().next());
-                break;
+            } else {
+                propsToStore.add(property);
             }
         }
+
         switch (request.conflictBehavior()) {
             case APPEND:
-                node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+                node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid, propsToStore);
                 break;
             case DO_NOT_REPLACE:
                 for (MapNode child : parentNode.getChildren()) {
@@ -243,7 +249,7 @@
                     }
                 }
                 if (node == null) {
-                    node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+                    node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid, propsToStore);
                 }
                 break;
             case REPLACE:
@@ -252,29 +258,19 @@
                 if (node != null) {
                     workspace.removeNode(getExecutionContext(), node);
                 }
-                node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+                node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid, propsToStore);
                 break;
             case UPDATE:
                 // See if the node already exists (this doesn't record an error on the request) ...
                 node = getTargetNode(workspace, null, Location.create(pathFactory.create(parent, request.named()), uuid));
                 if (node == null) {
-                    node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+                    node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid, propsToStore);
                 } // otherwise, we found it and we're setting any properties below
                 break;
         }
         assert node != null;
         Path path = getExecutionContext().getValueFactories().getPathFactory().create(parent, node.getName());
-        // Now add the properties to the supplied node ...
-        for (Property property : request.properties()) {
-            Name propName = property.getName();
-            if (property.size() == 0) {
-                node.getProperties().remove(propName);
-                continue;
-            }
-            if (!propName.equals(DnaLexicon.UUID)) {
-                node.getProperties().put(propName, property);
-            }
-        }
+
         Location actualLocation = getActualLocation(Location.create(path), node);
         request.setActualLocationOfNode(actualLocation);
         recordChange(request);
@@ -323,7 +319,7 @@
             // Build the path from the before node to the root.
             LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
             MapNode current = beforeNode.getParent();
-            while (current != workspace.getRoot()) {
+            while (!current.equals(workspace.getRoot())) {
                 segments.addFirst(current.getName());
                 current = current.getParent();
             }
@@ -338,7 +334,7 @@
             return;
         }
         workspace.moveNode(getExecutionContext(), node, request.desiredName(), workspace, newParent, beforeNode);
-        assert node.getParent() == newParent;
+        assert node.getParent().equals(newParent);
         Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName());
         Location oldLocation = getActualLocation(request.from(), node);
         Location newLocation = Location.create(newPath, node.getUuid());
@@ -360,15 +356,16 @@
         for (Map.Entry<Name, Property> propertyEntry : request.properties().entrySet()) {
             Property property = propertyEntry.getValue();
             if (property == null) {
-                node.getProperties().remove(propertyEntry.getKey());
+                node.removeProperty(propertyEntry.getKey());
                 continue;
             }
             Name propName = property.getName();
             if (!propName.equals(DnaLexicon.UUID)) {
-                if (node.getProperties().put(propName, property) == null) {
+                if (node.getProperties().get(propName) == null) {
                     // It is a new property ...
                     request.setNewProperty(propName);
                 }
+                node.setProperty(property);
             }
         }
         Location actualLocation = getActualLocation(request.on(), node);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -32,6 +32,7 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
@@ -81,12 +82,13 @@
     MapNode getNode( Path path );
 
     /**
-     * Removes the given node. This method will return silently if the given node does not exist in this workspace.
+     * Removes the given node. This method will return false if the given node does not exist in this workspace.
      * 
      * @param context the current execution context; may not be null
      * @param node the node to be removed; may not be null
+     * @return whether a node was removed as a result of this operation
      */
-    void removeNode( ExecutionContext context,
+    boolean removeNode( ExecutionContext context,
                      MapNode node );
 
     /**
@@ -94,10 +96,12 @@
      * 
      * @param context the environment; may not be null
      * @param pathToNewNode the path to the new node; may not be null
+     * @param properties the properties for the new node
      * @return the new node (or root if the path specified the root)
      */
     MapNode createNode( ExecutionContext context,
-                        String pathToNewNode );
+                        String pathToNewNode,
+                        Iterable<Property> properties );
 
     /**
      * Create a new node with the supplied name, as a child of the supplied parent.
@@ -106,12 +110,14 @@
      * @param parentNode the parent node; may not be null
      * @param name the name; may not be null
      * @param uuid the UUID of the node, or null if the UUID is to be generated
+     * @param properties the properties for the new node
      * @return the new node
      */
     MapNode createNode( ExecutionContext context,
                         MapNode parentNode,
                         Name name,
-                        UUID uuid );
+                        UUID uuid,
+                        Iterable<Property> properties );
 
     /**
      * Move the supplied node to the new parent. This method automatically removes the node from its existing parent, and also

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryTest.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryTest.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -29,6 +29,8 @@
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.hasItems;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.UUID;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.connector.map.MapNode;
@@ -131,12 +133,13 @@
 
         // Populate the workspace with a few nodes ...
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        final Collection<Property> NO_PROPS = Collections.emptySet();
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
         Name propertyName = nameFactory.create("something");
@@ -202,12 +205,13 @@
 
         // Populate the workspace with a few nodes ...
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        final Collection<Property> NO_PROPS = Collections.emptySet();
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
         Name propertyName = nameFactory.create("something");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java	2009-11-26 20:40:53 UTC (rev 1360)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java	2009-11-27 01:36:53 UTC (rev 1361)
@@ -30,6 +30,8 @@
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.hasItems;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -63,6 +65,7 @@
     private PathFactory pathFactory;
     private NameFactory nameFactory;
     private PropertyFactory propertyFactory;
+    private final Collection<Property> NO_PROPS = Collections.emptySet();
 
     @Before
     public void beforeEach() throws Exception {
@@ -103,21 +106,21 @@
     @Test
     public void shouldCreateNodesByPath() {
         Name name_a = nameFactory.create("a");
-        MapNode node_a = workspace.createNode(context, workspace.getRoot(), name_a, null);
+        MapNode node_a = workspace.createNode(context, workspace.getRoot(), name_a, null, NO_PROPS);
         assertThat(node_a, is(notNullValue()));
         assertThat(node_a.getParent(), is(workspace.getRoot()));
         assertThat(node_a.getName().getName(), is(name_a));
         assertThat(node_a.getName().hasIndex(), is(false));
 
         Name name_b = nameFactory.create("b");
-        MapNode node_b = workspace.createNode(context, node_a, name_b, null);
+        MapNode node_b = workspace.createNode(context, node_a, name_b, null, NO_PROPS);
         assertThat(node_b, is(notNullValue()));
         assertThat(node_b.getParent(), is(node_a));
         assertThat(node_b.getName().getName(), is(name_b));
         assertThat(node_b.getName().hasIndex(), is(false));
 
         Name name_c = nameFactory.create("c");
-        MapNode node_c = workspace.createNode(context, node_b, name_c, null);
+        MapNode node_c = workspace.createNode(context, node_b, name_c, null, NO_PROPS);
         assertThat(node_c, is(notNullValue()));
         assertThat(node_c.getParent(), is(node_b));
         assertThat(node_c.getName().getName(), is(name_c));
@@ -132,9 +135,9 @@
 
     @Test
     public void shouldNotFindNodesThatDoNotExist() {
-        MapNode node_a = workspace.createNode(context, workspace.getRoot(), nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        /*Node node_c =*/workspace.createNode(context, node_b, nameFactory.create("c"), null);
+        MapNode node_a = workspace.createNode(context, workspace.getRoot(), nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        /*Node node_c =*/workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
 
         assertThat(workspace.size(), is(4));
         assertThat(workspace.getNode(pathFactory.create("/a")), is(node_a));
@@ -150,14 +153,14 @@
     @Test
     public void shouldCorrectlyManageIndexesOfSiblingsWithSameNames() {
         Name name_a1 = nameFactory.create("a");
-        MapNode node_a1 = workspace.createNode(context, workspace.getRoot(), name_a1, null);
+        MapNode node_a1 = workspace.createNode(context, workspace.getRoot(), name_a1, null, NO_PROPS);
         assertThat(node_a1, is(notNullValue()));
         assertThat(node_a1.getParent(), is(workspace.getRoot()));
         assertThat(node_a1.getName().getName(), is(name_a1));
         assertThat(node_a1.getName().hasIndex(), is(false));
 
         Name name_a2 = nameFactory.create("a");
-        MapNode node_a2 = workspace.createNode(context, workspace.getRoot(), name_a2, null);
+        MapNode node_a2 = workspace.createNode(context, workspace.getRoot(), name_a2, null, NO_PROPS);
         assertThat(node_a2, is(notNullValue()));
         assertThat(node_a2.getParent(), is(workspace.getRoot()));
         assertThat(node_a2.getName().getName(), is(name_a2));
@@ -169,7 +172,7 @@
 
         // Add another node without the same name ..
         Name name_b = nameFactory.create("b");
-        MapNode node_b = workspace.createNode(context, workspace.getRoot(), name_b, null);
+        MapNode node_b = workspace.createNode(context, workspace.getRoot(), name_b, null, NO_PROPS);
         assertThat(node_b, is(notNullValue()));
         assertThat(node_b.getParent(), is(workspace.getRoot()));
         assertThat(node_b.getName().getName(), is(name_b));
@@ -177,7 +180,7 @@
 
         // Add a third node with the same name ..
         Name name_a3 = nameFactory.create("a");
-        MapNode node_a3 = workspace.createNode(context, workspace.getRoot(), name_a3, null);
+        MapNode node_a3 = workspace.createNode(context, workspace.getRoot(), name_a3, null, NO_PROPS);
         assertThat(node_a3, is(notNullValue()));
         assertThat(node_a3.getParent(), is(workspace.getRoot()));
         assertThat(node_a3.getName().getName(), is(name_a3));
@@ -213,12 +216,12 @@
     @Test
     public void shouldMoveNodesWithinSameWorkspace() {
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         assertThat(workspace.size(), is(7));
         assertThat(workspace.getNode(pathFactory.create("/")), is(sameInstance(workspace.getRoot())));
@@ -253,12 +256,12 @@
     @Test
     public void shouldMoveNodeBeforeAnother() {
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
         Name propName = nameFactory.create("prop");
         node_b.setProperty(propertyFactory.create(propName, "node_b"));
         node_b2.setProperty(propertyFactory.create(propName, "node_b2"));
@@ -305,12 +308,12 @@
     public void shouldMoveNodesFromOneWorkspaceToAnother() {
         // Populate the workspace with some content ..
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         assertThat(workspace.size(), is(7));
         assertThat(workspace.getNode(pathFactory.create("/")), is(sameInstance(workspace.getRoot())));
@@ -326,12 +329,12 @@
         assertThat(new_workspace, is(notNullValue()));
 
         MapNode new_root = new_workspace.getRoot();
-        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null);
-        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null);
-        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null);
-        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null);
-        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null);
-        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null);
+        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null, NO_PROPS);
 
         assertThat(new_workspace.size(), is(7));
         assertThat(new_workspace.getNode(pathFactory.create("/")), is(sameInstance(new_root)));
@@ -365,12 +368,12 @@
     @Test
     public void shouldCopyNodesWithinSameWorkspace() {
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = valueFactories.getStringFactory();
         Name propertyName = nameFactory.create("something");
@@ -409,12 +412,12 @@
     public void shouldCopyNodesFromOneWorkspaceToAnotherAndKeepSameUuids() {
         // Populate the workspace with some content ..
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = valueFactories.getStringFactory();
         Name propertyName = nameFactory.create("something");
@@ -437,12 +440,12 @@
         assertThat(new_workspace, is(notNullValue()));
 
         MapNode new_root = new_workspace.getRoot();
-        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null);
-        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null);
-        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null);
-        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null);
-        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null);
-        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null);
+        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null, NO_PROPS);
 
         assertThat(new_workspace.size(), is(7));
         assertThat(new_workspace.getNode(pathFactory.create("/")), is(sameInstance(new_root)));
@@ -492,12 +495,12 @@
     public void shouldCopyNodesFromOneWorkspaceToAnotherAndGenerateNewUuids() {
         // Populate the workspace with some content ..
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = valueFactories.getStringFactory();
         Name propertyName = nameFactory.create("something");
@@ -520,12 +523,12 @@
         assertThat(new_workspace, is(notNullValue()));
 
         MapNode new_root = new_workspace.getRoot();
-        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null);
-        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null);
-        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null);
-        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null);
-        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null);
-        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null);
+        MapNode new_node_a = new_workspace.createNode(context, new_root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode new_node_b = new_workspace.createNode(context, new_node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode new_node_c = new_workspace.createNode(context, new_node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode new_node_d = new_workspace.createNode(context, new_root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode new_node_e = new_workspace.createNode(context, new_node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode new_node_b2 = new_workspace.createNode(context, new_node_d, nameFactory.create("b"), null, NO_PROPS);
 
         assertThat(new_workspace.size(), is(7));
         assertThat(new_workspace.getNode(pathFactory.create("/")), is(sameInstance(new_root)));
@@ -578,12 +581,12 @@
     @Test
     public void shouldCopyNodesWhenDesiredNameIsSpecified() {
         MapNode root = workspace.getRoot();
-        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null);
-        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null);
-        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null);
-        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null);
-        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null);
-        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null);
+        MapNode node_a = workspace.createNode(context, root, nameFactory.create("a"), null, NO_PROPS);
+        MapNode node_b = workspace.createNode(context, node_a, nameFactory.create("b"), null, NO_PROPS);
+        MapNode node_c = workspace.createNode(context, node_b, nameFactory.create("c"), null, NO_PROPS);
+        MapNode node_d = workspace.createNode(context, root, nameFactory.create("d"), null, NO_PROPS);
+        MapNode node_e = workspace.createNode(context, node_d, nameFactory.create("e"), null, NO_PROPS);
+        MapNode node_b2 = workspace.createNode(context, node_d, nameFactory.create("b"), null, NO_PROPS);
 
         ValueFactory<String> stringFactory = valueFactories.getStringFactory();
         Name propertyName = nameFactory.create("something");
@@ -620,12 +623,14 @@
 
     @Test
     public void shouldCreateRepositoryStructure() {
-        workspace.createNode(context, "/a")
+        workspace.createNode(context, "/a", NO_PROPS)
                  .setProperty(context, "name", "value")
-                 .setProperty(context, "desc", "Some description");
-        workspace.createNode(context, "/a/b").setProperty(context, "name", "value2").setProperty(context,
+.setProperty(context,
+                                                                                                        "desc",
+                                                                                                        "Some description");
+        workspace.createNode(context, "/a/b", NO_PROPS).setProperty(context, "name", "value2").setProperty(context,
                                                                                                  "desc",
-                                                                                                 "Some description 2");
+                                                                                                           "Some description 2");
         assertThat(workspace.getNode(context, "/a").getProperty(context, "name").getValuesAsArray(), is(new Object[] {"value"}));
         assertThat(workspace.getNode(context, "/a").getProperty(context, "desc").getValuesAsArray(),
                    is(new Object[] {"Some description"}));


From dna-commits at lists.jboss.org  Thu Nov 26 20:57:01 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 20:57:01 -0500
Subject: [dna-commits] DNA SVN: r1362 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map.
Message-ID: <200911270157.nAR1v1bm001366@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-26 20:57:01 -0500 (Thu, 26 Nov 2009)
New Revision: 1362

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java
Log:
DNA-546 JCR Workspace and Session Imports Can Fail on JPA Connector

Removed gratuitous @Overrides

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java	2009-11-27 01:36:53 UTC (rev 1361)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/DefaultMapNode.java	2009-11-27 01:57:01 UTC (rev 1362)
@@ -103,35 +103,29 @@
         return properties;
     }
 
-    @Override
     public void addChild( int index,
                           MapNode child ) {
         children.add(index, child);
     }
 
-    @Override
     public void addChild( MapNode child ) {
         children.add(child);
 
     }
 
-    @Override
     public void clearChildren() {
         children.clear();
     }
 
-    @Override
     public boolean removeChild( MapNode child ) {
         return children.remove(child);
     }
 
-    @Override
     public MapNode removeProperty( Name propertyName ) {
         properties.remove(propertyName);
         return this;
     }
 
-    @Override
     public MapNode setProperties( Iterable<Property> properties ) {
         for (Property property : properties) {
             this.properties.put(property.getName(), property);


From dna-commits at lists.jboss.org  Thu Nov 26 21:26:23 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 21:26:23 -0500
Subject: [dna-commits] DNA SVN: r1363 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map.
Message-ID: <200911270226.nAR2QNOo007034@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-26 21:26:23 -0500 (Thu, 26 Nov 2009)
New Revision: 1363

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
Log:
DNA-546 JCR Workspace and Session Imports Can Fail on JPA Connector

Fixed bug in behavior of MapRequestProcessor.process(CreateNodeRequest) when given valueless properties.


Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-27 01:57:01 UTC (rev 1362)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-11-27 02:26:23 UTC (rev 1363)
@@ -233,7 +233,7 @@
             if (property.getName().equals(DnaLexicon.UUID) || property.getName().equals(JcrLexicon.UUID)) {
                 uuid = getExecutionContext().getValueFactories().getUuidFactory().create(property.getValues().next());
             } else {
-                propsToStore.add(property);
+                if (property.size() > 0) propsToStore.add(property);
             }
         }
 


From dna-commits at lists.jboss.org  Thu Nov 26 22:08:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 26 Nov 2009 22:08:50 -0500
Subject: [dna-commits] DNA SVN: r1364 - in trunk/utils/dna-jcr-gen:
	src/main/java/org/jboss/dna/jcr/gen and 2 other directories.
Message-ID: <200911270308.nAR38oMR016916@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-26 22:08:49 -0500 (Thu, 26 Nov 2009)
New Revision: 1364

Removed:
   trunk/utils/dna-jcr-gen/benerator-0.5.9-SR1.xsd
   trunk/utils/dna-jcr-gen/pom.xml
   trunk/utils/dna-jcr-gen/readme.txt
   trunk/utils/dna-jcr-gen/src/main/java/org/jboss/dna/jcr/gen/JCRDataMassGen.java
   trunk/utils/dna-jcr-gen/src/main/resources/benerator-0.5.9-SR1.xsd
   trunk/utils/dna-jcr-gen/src/main/resources/log4j.properties
   trunk/utils/dna-jcr-gen/src/test/resources/benerator/bank.properties
   trunk/utils/dna-jcr-gen/src/test/resources/benerator/benerator.xml
Log:
DNA-560 Removed the 'dna-jcr-gen' project because of licensing concerns.

Deleted: trunk/utils/dna-jcr-gen/benerator-0.5.9-SR1.xsd
===================================================================
--- trunk/utils/dna-jcr-gen/benerator-0.5.9-SR1.xsd	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/benerator-0.5.9-SR1.xsd	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,372 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
-		targetNamespace="http://databene.org/benerator/0.5.9-SR1"
-		xmlns="http://databene.org/benerator/0.5.9-SR1"
-		elementFormDefault="qualified">
-		
-	<xs:annotation>
-		<xs:documentation>The benerator schema describes the generation setup for benerator.</xs:documentation>
-		<xs:appinfo/>
-	</xs:annotation>
-	
-	<xs:simpleType name="script-expression-type">
-		<xs:restriction base="xs:string">
-			<xs:pattern value="\{.*\}"/>
-		</xs:restriction>
-	</xs:simpleType>
-
-	<xs:simpleType name="errorhandler-type">
-		<xs:union>
-			<xs:simpleType>
-				<xs:restriction base="xs:string">
-					<xs:enumeration value="fatal"/>
-					<xs:enumeration value="error"/>
-					<xs:enumeration value="warn"/>
-					<xs:enumeration value="info"/>
-					<xs:enumeration value="debug"/>
-					<xs:enumeration value="trace"/>
-					<xs:enumeration value="ignore"/>
-				</xs:restriction>
-			</xs:simpleType>
-			<xs:simpleType>
-				<xs:restriction base="script-expression-type"/>
-			</xs:simpleType>
-		</xs:union>
-	</xs:simpleType>
-
-	
-	<!-- The document root, it can list beans, create-entities and run-tasks in arbitrary order. -->
-	<xs:element name="setup">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"           minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="import"            minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="include"           minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="database"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="bean"              minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="defaultAttributes" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities"   minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities"   minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="run-task"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="execute"           minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="evaluate"          minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="echo"              minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="default-script"       type="xs:string"/> <!-- deprecated -->
-			<xs:attribute name="default-null"         type="xs:string"/> <!-- deprecated -->
-			<xs:attribute name="defaultScript"        type="xs:string"/>
-			<xs:attribute name="defaultNull"          type="xs:string"/>
-            <xs:attribute name="defaultEncoding"      type="xs:string"/>
-            <xs:attribute name="defaultLineSeparator" type="xs:string"/>
-            <xs:attribute name="defaultLocale"        type="xs:string"/>
-            <xs:attribute name="defaultDataset"       type="xs:string"/>
-			<xs:attribute name="defaultPagesize"      type="xs:string"/>
-			<xs:attribute name="defaultSeparator"     type="xs:string"/>
-			<xs:attribute name="defaultErrorHandler"  type="errorhandler-type"/>
-			<xs:attribute name="maxCount"             type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<xs:element name="comment" type="xs:string" />
-
-	<!-- Imports classes. -->
-	<xs:element name="import">
-		<xs:complexType>
-			<xs:attribute name="class"    type="xs:string"/>
-			<xs:attribute name="domain"   type="xs:string"/>
-			<xs:attribute name="defaults" type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Includes a properties file. -->
-	<xs:element name="include">
-		<xs:complexType>
-			<xs:attribute name="uri" type="xs:string" use="required"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Outputs a message to the console. -->
-	<xs:element name="echo">
-		<xs:complexType>
-			<xs:attribute name="message" type="xs:string" use="required"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Instantiates a JavaBean of the given class.
-    	 If the 'id' is set, the bean is put into the context with that name.
-    	 If the JavaBean itself has a property named 'id', this one is set to the value specified here. -->
-	<xs:element name="bean">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="id"    type="xs:string" use="required"/>
-			<xs:attribute name="class" type="xs:string"/>
-			<xs:attribute name="spec"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- connects to a database -->
-	<xs:element name="database">
-		<xs:complexType>
-			<xs:attribute name="id"        type="xs:string" use="required"/>
-			<xs:attribute name="url"       type="xs:string"/>
-			<xs:attribute name="driver"    type="xs:string"/>
-			<xs:attribute name="user"      type="xs:string"/>
-			<xs:attribute name="schema"    type="xs:string"/>
-			<xs:attribute name="password"  type="xs:string"/>
-			<xs:attribute name="batch"     type="xs:string"/>
-            <xs:attribute name="fetchSize" type="xs:string"/>
-            <xs:attribute name="readOnly"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-    <xs:complexType name="execute-type">
-        <xs:simpleContent>
-            <xs:extension base="xs:string">
-                <xs:attribute name="uri"      type="xs:string"/>
-                <xs:attribute name="encoding" type="xs:string"/>
-                <xs:attribute name="target"   type="xs:string"/>
-                <xs:attribute name="type"     type="xs:string"/>
-                <xs:attribute name="onError"  type="errorhandler-type"/>
-                <xs:attribute name="optimize" type="xs:string"/>
-            </xs:extension>
-        </xs:simpleContent>
-    </xs:complexType>
-
-    <!-- executes a script, e.g. SQL -->
-    <xs:element name="execute" />
-    
-    <!-- evaluates a script, e.g. SQL -->
-    <xs:element name="evaluate">
-        <xs:complexType>
-            <xs:simpleContent>
-                <xs:extension base="execute-type">
-                    <xs:attribute name="assert" type="xs:string"/>
-                </xs:extension>
-            </xs:simpleContent>
-        </xs:complexType>
-    </xs:element>
-    
-	<!-- Declares a JavaBean to be used as consumer, either by referencing an object from the context (ref)
-    	 or by instantiating a JavaBean like with the &lt;bean&gt; tag. -->
-	<xs:element name="consumer">
-		<xs:complexType>
-			<xs:sequence minOccurs="1" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="id"    type="xs:string"/>
-			<xs:attribute name="class" type="xs:string"/>
-			<xs:attribute name="ref"   type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Sets a JavaBean's property to the simple property specified as 'value' attribute
-	     or to the object stored in the context with the 'ref'. -->
-	<xs:element name="property">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="bean"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="idref" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="name"  type="xs:string"/>
-			<xs:attribute name="value" type="xs:string"/>
-			<xs:attribute name="ref"   type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Represents a reference to a JavaBean (of the name specified by the 'bean' attribute) from the context. -->
-	<xs:element name="idref">
-		<xs:complexType>
-			<xs:attribute name="bean"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Instantiates and executes a JavaBean that implements the Task interface.
-	     Properties are handled like in the &lt;bean&gt; tag.
-	     The task is executed 'count' times in a number of 'threads' parallel threads.
-	     Invocations are executed in groups of size 'pagesize'. -->
-	<xs:element name="run-task">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="class"    type="xs:string"/>
-			<xs:attribute name="count"    type="xs:string"/>
-			<xs:attribute name="pagesize" type="xs:string"/>
-			<xs:attribute name="threads"  type="xs:string"/>
-			<xs:attribute name="pager"    type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<xs:element name="defaultAttributes">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="id"              minOccurs="0" maxOccurs="1"/>
-				<xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="name"     type="xs:string"/>
-			<xs:attribute name="count"    type="xs:string"/>
-			<xs:attribute name="pagesize" type="xs:string"/>
-			<xs:attribute name="threads"  type="xs:string"/>
-			<xs:attribute name="source"   type="xs:string"/>
-			<xs:attribute name="encoding" type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-    <!-- Creates entities. If a 'count' is provided the corresponding number of entities is created.
-         If no count is provided, entities are created as long as each involved generator is available. -->
-    <xs:element name="create-entities">
-        <xs:complexType>
-            <xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"         minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="id"              minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="variable"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="consumer"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities" minOccurs="0" maxOccurs="unbounded"/>
-            </xs:sequence>
-            <xs:attribute name="name"     type="xs:string"/>
-            <xs:attribute name="count"    type="xs:string"/>
-            <xs:attribute name="minCount" type="xs:string"/>
-            <xs:attribute name="maxCount" type="xs:string"/>
-            <xs:attribute name="pagesize" type="xs:string"/>
-            <xs:attribute name="threads"  type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="selector" type="xs:string"/>
-            <xs:attribute name="encoding" type="xs:string"/>
-            <xs:attribute name="consumer" type="xs:string"/>
-        </xs:complexType>
-    </xs:element>
-
-    <!-- Updates entities. -->
-    <xs:element name="update-entities">
-        <xs:complexType>
-            <xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"         minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="id"              minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="variable"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="consumer"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities" minOccurs="0" maxOccurs="unbounded"/>
-            </xs:sequence>
-            <xs:attribute name="name"     type="xs:string"/>
-            <xs:attribute name="count"    type="xs:string"/>
-            <xs:attribute name="pagesize" type="xs:string"/>
-            <xs:attribute name="threads"  type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="selector" type="xs:string"/>
-            <xs:attribute name="encoding" type="xs:string"/>
-            <xs:attribute name="consumer" type="xs:string"/>
-        </xs:complexType>
-    </xs:element>
-
-	<!-- Defines an ID generator that can be reused in different scopes. -->
-	<xs:element name="id">
-		<xs:complexType>
-			<xs:attribute name="name"     type="xs:string"/>
-			<xs:attribute name="type"     type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="strategy" type="xs:string"/>
-            <xs:attribute name="scope"    type="xs:string"/>
-			<xs:attribute name="param"    type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Defines a reference generator. -->
-	<xs:element name="reference">
-		<xs:complexType>
-			<xs:attribute name="name"         type="xs:string"/>
-			<xs:attribute name="type"         type="xs:string"/>
-			<xs:attribute name="nullable"     type="xs:string"/>
-			<xs:attribute name="nullQuota"    type="xs:string"/>
-			<xs:attribute name="source"       type="xs:string"/>
-			<xs:attribute name="targetType"   type="xs:string"/>
-			<xs:attribute name="selector"     type="xs:string"/>
-			<xs:attribute name="distribution" type="xs:string"/>
-			<xs:attribute name="mode"         type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Defines a variable that is recreated by a generator on each entity creation -->
-	<xs:element name="variable" type="generator-setup"/>
-
-	<xs:complexType name="generator-setup">
-		<xs:attribute name="name"         type="xs:string" use="required"/>
-		<xs:attribute name="type"         type="xs:string"/>
-		<xs:attribute name="generator"    type="xs:string"/>
-		<xs:attribute name="validator"    type="xs:string"/>
-		<xs:attribute name="values"       type="xs:string"/>
-		<xs:attribute name="source"       type="xs:string"/>
-		<xs:attribute name="encoding"     type="xs:string"/>
-		<xs:attribute name="selector"     type="xs:string"/>
-		<xs:attribute name="pattern"      type="xs:string"/>
-		<xs:attribute name="nullQuota"    type="xs:string"/>
-		<xs:attribute name="converter"    type="xs:string"/>
-		<xs:attribute name="dataset"      type="xs:string"/>
-		<xs:attribute name="nesting"      type="xs:string"/>
-		<xs:attribute name="locale"       type="xs:string"/>
-		<xs:attribute name="unique"       type="xs:string"/>
-		<xs:attribute name="distribution" type="xs:string"/>
-		<xs:attribute name="variation1"   type="xs:string"/>
-		<xs:attribute name="variation2"   type="xs:string"/>
-		<xs:attribute name="cyclic"       type="xs:string"/>
-		<xs:attribute name="proxy">
-			<xs:simpleType>
-				<xs:union>
-					<xs:simpleType> <!-- (skip | repeat) -->
-						<xs:restriction base="xs:string">
-							<xs:enumeration value="skip"/>
-							<xs:enumeration value="repeat"/>
-						</xs:restriction>
-					</xs:simpleType>
-					<xs:simpleType>
-						<xs:restriction base="script-expression-type"/>
-					</xs:simpleType>
-				</xs:union>
-			</xs:simpleType>
-		</xs:attribute>
-		<xs:attribute name="proxy-param1" type="xs:string"/>
-		<xs:attribute name="proxy-param2" type="xs:string"/>
-	</xs:complexType>
-	
-	<xs:element name="attribute" type="attribute-generator-setup"/>
-	
-	<xs:complexType name="attribute-generator-setup">
-		<xs:complexContent>
-			<xs:extension base="generator-setup">
-			
-				<xs:attribute name="nullable"     type="xs:string"/>
-				<xs:attribute name="mode"         type="xs:string"/>
-
-				<!-- boolean generator setup -->
-				<xs:attribute name="trueQuota"    type="xs:string"/>
-				
-				<!-- number generator setup -->
-				<xs:attribute name="min"          type="xs:string"/>
-				<xs:attribute name="minExclusive" type="xs:string"/>
-				<xs:attribute name="max"          type="xs:string"/>
-				<xs:attribute name="maxExclusive" type="xs:string"/>
-				<xs:attribute name="precision"    type="xs:string"/>
-				
-				<!-- String generator setup -->
-				<xs:attribute name="script"       type="xs:string"/>
-				<xs:attribute name="minLength"    type="xs:string"/>
-				<xs:attribute name="maxLength"    type="xs:string"/>
-				
-			</xs:extension>
-		</xs:complexContent>
-	</xs:complexType>
-	
-</xs:schema>

Deleted: trunk/utils/dna-jcr-gen/pom.xml
===================================================================
--- trunk/utils/dna-jcr-gen/pom.xml	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/pom.xml	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.jboss.dna</groupId>
-    <artifactId>dna</artifactId>
-    <version>0.6-SNAPSHOT</version>
-    <relativePath>../..</relativePath>
-  </parent>
-  <!-- The groupId and version values are inherited from parent -->
-	<artifactId>dna-jcr-gen</artifactId>
-	<packaging>jar</packaging>
-	<name>JBoss DNA JCR data mass generator</name>
-	<description>
-		A Utilty to generate mass of data for a jcr repository
-		and then write them into the source. For Performance testing,
-		for dummy daten for a start up project.
-	</description>
-	<url>http://labs.jboss.org/dna</url>
-
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.databene</groupId>
-				<artifactId>maven-benerator-plugin</artifactId>
-				<version>0.5.9-SR1</version>
-				<configuration>
-					<descriptor>
-						src/test/resources/benerator/benerator.xml
-					</descriptor>
-					<!-- If you use benerator offline -->
-					<validate>false</validate>
-				</configuration>
-			</plugin>
-		</plugins>
-	</build>
-
-	<!--
-		Define the dependencies.  Note that all version and scopes default to those 
-		defined in the dependencyManagement section of the parent pom.
-	-->
-	<dependencies>
-		<dependency>
-			<groupId>org.databene</groupId>
-			<artifactId>databene-benerator</artifactId>
-			<version>0.5.9-SR1</version>
-		</dependency>
-		<!-- 
-			Java Content Repository API 
-		-->
-		<dependency>
-			<groupId>javax.jcr</groupId>
-			<artifactId>jcr</artifactId>
-			<version>1.0.1</version>
-			<scope>compile</scope>
-		</dependency>
-		<!-- 
-			JBoss DNA JCR  (JCR Implementation) for repository implementation
-		-->
-		<dependency>
-			<groupId>org.jboss.dna</groupId>
-			<artifactId>dna-jcr</artifactId>
-			<version>${project.version}</version>
-			<scope>compile</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.jboss.dna</groupId>
-			<artifactId>dna-repository</artifactId>
-			<version>${project.version}</version>
-			<scope>compile</scope>
-		</dependency>
-
-		<!-- 
-			Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
-		-->
-		<!-- Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) -->
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-api</artifactId>
-			<version>1.4.3</version>
-			<scope>compile</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-log4j12</artifactId>
-			<version>1.4.3</version>
-			<scope>compile</scope>
-		</dependency>
-		<dependency>
-			<groupId>log4j</groupId>
-			<artifactId>log4j</artifactId>
-			<version>1.2.14</version>
-			<scope>compile</scope>
-		</dependency>
-		<dependency>
-			<groupId>com.google.code.google-collections</groupId>
-			<artifactId>google-collect</artifactId>
-			<version>snapshot-20080530</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.jboss.security</groupId>
-			<artifactId>jboss-idtrust</artifactId>
-			<version>2.0.2.CR1</version>
-			<scope>test</scope>
-		</dependency>
-
-		<dependency>
-			<groupId>org.jboss.security</groupId>
-			<artifactId>jboss-security-spi-bare</artifactId>
-			<version>2.0.2.SP6</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.jboss.security</groupId>
-			<artifactId>jbosssx-bare</artifactId>
-			<version>2.0.2.SP6</version>
-			<exclusions>
-				<exclusion>
-					<groupId>apache-xalan</groupId>
-					<artifactId>xalan</artifactId>
-				</exclusion>
-				<exclusion>
-					<groupId>apache-xalan</groupId>
-					<artifactId>serializer</artifactId>
-				</exclusion>
-			</exclusions>
-			<scope>test</scope>
-		</dependency>
-	</dependencies>
-</project>
\ No newline at end of file

Deleted: trunk/utils/dna-jcr-gen/readme.txt
===================================================================
--- trunk/utils/dna-jcr-gen/readme.txt	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/readme.txt	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,23 +0,0 @@
-
-- Prerequire
-
-you need to have JDK6 installed on your OS
-
-check 
-
- sp$ java -version
-java version "1.6.0_13"
-Java(TM) SE Runtime Environment (build 1.6.0_13-b03-211)
-Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02-83, mixed mode)
-
-
-- How to generate mass of data
- 
- -- This two files bank.properties and benerator.xml describe how many entities and the model
- 
-         * /dna-jcr-gen/src/test/resources/benerator/bank.properties
-         * /dna-jcr-gen/src/test/resources/benerator/benerator.xml
- -- Use "mvn benerator:generate" command to generate data into you Source (For now only test on 
-    InMemoryRepositorySource source)
- 
- 
\ No newline at end of file

Deleted: trunk/utils/dna-jcr-gen/src/main/java/org/jboss/dna/jcr/gen/JCRDataMassGen.java
===================================================================
--- trunk/utils/dna-jcr-gen/src/main/java/org/jboss/dna/jcr/gen/JCRDataMassGen.java	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/src/main/java/org/jboss/dna/jcr/gen/JCRDataMassGen.java	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,234 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors. 
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- *
- * JBoss DNA 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.jcr.gen;
-
-import java.io.InputStream;
-import java.util.Calendar;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-import javax.jcr.lock.LockException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.version.VersionException;
-import org.databene.model.consumer.AbstractConsumer;
-import org.databene.model.data.Entity;
-import org.jboss.dna.graph.SecurityContext;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.jcr.JcrConfiguration;
-import org.jboss.dna.jcr.JcrEngine;
-import org.jboss.dna.jcr.SecurityContextCredentials;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A Utility that writes the generated content as nodes to a DNA JCR repository.<br/>
- * 
- * @author Serge Pagop
- */
-
-public class JCRDataMassGen extends AbstractConsumer<Entity> {
-
-    private static final Logger log = LoggerFactory.getLogger(JCRDataMassGen.class);
-    // attributes ------------------------------------------------------------------------------------------------------
-
-    private Session session;
-    private Node currentNode;
-    private boolean printContent;
-
-    private JcrConfiguration configuration;
-    private JcrEngine engine;
-    private Repository repository;
-
-    // Constructor -----------------------------------------------------------------------------------------------------
-
-    /**
-     * Construct a Data Mass Generator, that may be used to generate test data.
-     * @param sourceName - the name of the source.
-     * @param description - the configuration description.
-     * @param beanPropertyName - the name of the bean property.
-     * @param workspaceName - the workspace name.
-     * @param repositoryName - the repository name.
-     * @param repositorySource - 
-     * @param username
-     * @param printContent
-     * @throws Exception
-     */
-    @SuppressWarnings( "unchecked" )
-    public JCRDataMassGen( String sourceName,
-                           String description,
-                           String beanPropertyName,
-                           String workspaceName,
-                           String repositoryName,
-                           String repositorySource,
-                           String username,
-                           boolean printContent ) throws Exception {
-        this.printContent = printContent;
-
-        // setting up the JcrConfiguration
-        configuration = new JcrConfiguration();
-        Class<? extends RepositorySource> clazz = (Class<? extends RepositorySource>)Class.forName(repositorySource);
-        configuration.repositorySource(sourceName).usingClass(clazz).setDescription(description).setProperty(beanPropertyName,
-                                                                                                             workspaceName);
-        // repository
-        configuration.repository(repositoryName).setSource(sourceName);
-        // Start the engine
-        engine = configuration.build();
-        engine.start();
-        // Obtain a JCR Repository instance by name
-        repository = engine.getRepository(repositoryName);
-        SecurityContext securityContext = new MyCustomSecurityContext(username);
-        SecurityContextCredentials credentials = new SecurityContextCredentials(securityContext);
-        this.session = repository.login(credentials, workspaceName);
-        this.currentNode = session.getRootNode();
-    }
-
-    // consumer interface implementation -------------------------------------------------------------------------------
-
-    public void startConsuming( Entity entity ) {
-        Node newNode;
-        try {
-            String n = entity.get("nodeName").toString();
-            newNode = currentNode.addNode(n, "nt:unstructured");
-            for (Map.Entry<String, Object> component : entity.getComponents().entrySet()) {
-                String key = component.getKey();
-                Object value = component.getValue();
-                if (!"nodeName".equals(key)) setNodeProperty(newNode, key, value);
-            }
-            currentNode = newNode;
-        } catch (Exception e) {
-            throw new RuntimeException("Exception whe trying to store " + entity, e);
-        }
-    }
-
-    @Override
-    public void finishConsuming( Entity object ) {
-        try {
-            session.save();
-            currentNode = currentNode.getParent();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public void close() {
-        try {
-            if (printContent) printNode(session.getRootNode(), "");
-        } catch (RepositoryException e) {
-            throw new RuntimeException(e);
-
-        } finally {
-            session.logout();
-
-            // Shutdown the engine
-            if (engine != null) {
-                engine.shutdown();
-                try {
-                    engine.awaitTermination(3, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-        }
-    }
-
-    public void setPrintContent( boolean printContent ) {
-        this.printContent = printContent;
-    }
-
-    // private helpers -------------------------------------------------------------------------------------------------
-
-    private void setNodeProperty( Node node,
-                                  String propertyName,
-                                  Object propertyValue )
-        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        if (propertyValue instanceof Long) node.setProperty(propertyName, (Long)propertyValue);
-        else if (propertyValue instanceof Double) node.setProperty(propertyName, (Double)propertyValue);
-        else if (propertyValue instanceof Boolean) node.setProperty(propertyName, (Boolean)propertyValue);
-        else if (propertyValue instanceof Calendar) node.setProperty(propertyName, (Calendar)propertyValue);
-        else if (propertyValue instanceof String[]) node.setProperty(propertyName, (String[])propertyValue);
-        else if (propertyValue instanceof InputStream) node.setProperty(propertyName, (InputStream)propertyValue);
-        else if (propertyValue instanceof Node) node.setProperty(propertyName, (Node)propertyValue);
-        else if (propertyValue instanceof Value) node.setProperty(propertyName, (Value)propertyValue);
-        else if (propertyValue instanceof Value[]) node.setProperty(propertyName, (Value[])propertyValue);
-        else node.setProperty(propertyName, propertyValue.toString());
-    }
-
-    private void printNode( Node node,
-                            String indent ) throws RepositoryException {
-        log.info(indent + "Node: " + node.getName() + " " + node.getPrimaryNodeType().getName());
-        PropertyIterator propIt = node.getProperties();
-        while (propIt.hasNext()) {
-            Property property = (Property)propIt.next();
-            log.info(indent + '\t' + '\t' + property.getName());
-            Value value = property.getValue();
-            log.info(indent + '\t' + '\t' + '\t' + value.getString());
-
-        }
-        NodeIterator iterator = node.getNodes();
-        while (iterator.hasNext())
-            printNode(iterator.nextNode(), indent + '\t');
-    }
-
-    protected class MyCustomSecurityContext implements SecurityContext {
-        private String username;
-
-        public MyCustomSecurityContext( String username ) {
-            this.username = username;
-        }
-
-        /**
-         * @see org.jboss.dna.graph.SecurityContext#getUserName()
-         */
-        public String getUserName() {
-            return this.username;
-        }
-
-        /**
-         * @see org.jboss.dna.graph.SecurityContext#hasRole(java.lang.String)
-         */
-
-        public boolean hasRole( String roleName ) {
-            return true;
-        }
-
-        /**
-         * @see org.jboss.dna.graph.SecurityContext#logout()
-         */
-        public void logout() {
-            // do something
-        }
-    }
-
-}

Deleted: trunk/utils/dna-jcr-gen/src/main/resources/benerator-0.5.9-SR1.xsd
===================================================================
--- trunk/utils/dna-jcr-gen/src/main/resources/benerator-0.5.9-SR1.xsd	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/src/main/resources/benerator-0.5.9-SR1.xsd	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,372 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
-		targetNamespace="http://databene.org/benerator/0.5.9-SR1"
-		xmlns="http://databene.org/benerator/0.5.9-SR1"
-		elementFormDefault="qualified">
-		
-	<xs:annotation>
-		<xs:documentation>The benerator schema describes the generation setup for benerator.</xs:documentation>
-		<xs:appinfo/>
-	</xs:annotation>
-	
-	<xs:simpleType name="script-expression-type">
-		<xs:restriction base="xs:string">
-			<xs:pattern value="\{.*\}"/>
-		</xs:restriction>
-	</xs:simpleType>
-
-	<xs:simpleType name="errorhandler-type">
-		<xs:union>
-			<xs:simpleType>
-				<xs:restriction base="xs:string">
-					<xs:enumeration value="fatal"/>
-					<xs:enumeration value="error"/>
-					<xs:enumeration value="warn"/>
-					<xs:enumeration value="info"/>
-					<xs:enumeration value="debug"/>
-					<xs:enumeration value="trace"/>
-					<xs:enumeration value="ignore"/>
-				</xs:restriction>
-			</xs:simpleType>
-			<xs:simpleType>
-				<xs:restriction base="script-expression-type"/>
-			</xs:simpleType>
-		</xs:union>
-	</xs:simpleType>
-
-	
-	<!-- The document root, it can list beans, create-entities and run-tasks in arbitrary order. -->
-	<xs:element name="setup">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"           minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="import"            minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="include"           minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="database"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="bean"              minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="defaultAttributes" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities"   minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities"   minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="run-task"          minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="execute"           minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="evaluate"          minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="echo"              minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="default-script"       type="xs:string"/> <!-- deprecated -->
-			<xs:attribute name="default-null"         type="xs:string"/> <!-- deprecated -->
-			<xs:attribute name="defaultScript"        type="xs:string"/>
-			<xs:attribute name="defaultNull"          type="xs:string"/>
-            <xs:attribute name="defaultEncoding"      type="xs:string"/>
-            <xs:attribute name="defaultLineSeparator" type="xs:string"/>
-            <xs:attribute name="defaultLocale"        type="xs:string"/>
-            <xs:attribute name="defaultDataset"       type="xs:string"/>
-			<xs:attribute name="defaultPagesize"      type="xs:string"/>
-			<xs:attribute name="defaultSeparator"     type="xs:string"/>
-			<xs:attribute name="defaultErrorHandler"  type="errorhandler-type"/>
-			<xs:attribute name="maxCount"             type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<xs:element name="comment" type="xs:string" />
-
-	<!-- Imports classes. -->
-	<xs:element name="import">
-		<xs:complexType>
-			<xs:attribute name="class"    type="xs:string"/>
-			<xs:attribute name="domain"   type="xs:string"/>
-			<xs:attribute name="defaults" type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Includes a properties file. -->
-	<xs:element name="include">
-		<xs:complexType>
-			<xs:attribute name="uri" type="xs:string" use="required"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Outputs a message to the console. -->
-	<xs:element name="echo">
-		<xs:complexType>
-			<xs:attribute name="message" type="xs:string" use="required"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Instantiates a JavaBean of the given class.
-    	 If the 'id' is set, the bean is put into the context with that name.
-    	 If the JavaBean itself has a property named 'id', this one is set to the value specified here. -->
-	<xs:element name="bean">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="id"    type="xs:string" use="required"/>
-			<xs:attribute name="class" type="xs:string"/>
-			<xs:attribute name="spec"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- connects to a database -->
-	<xs:element name="database">
-		<xs:complexType>
-			<xs:attribute name="id"        type="xs:string" use="required"/>
-			<xs:attribute name="url"       type="xs:string"/>
-			<xs:attribute name="driver"    type="xs:string"/>
-			<xs:attribute name="user"      type="xs:string"/>
-			<xs:attribute name="schema"    type="xs:string"/>
-			<xs:attribute name="password"  type="xs:string"/>
-			<xs:attribute name="batch"     type="xs:string"/>
-            <xs:attribute name="fetchSize" type="xs:string"/>
-            <xs:attribute name="readOnly"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-    <xs:complexType name="execute-type">
-        <xs:simpleContent>
-            <xs:extension base="xs:string">
-                <xs:attribute name="uri"      type="xs:string"/>
-                <xs:attribute name="encoding" type="xs:string"/>
-                <xs:attribute name="target"   type="xs:string"/>
-                <xs:attribute name="type"     type="xs:string"/>
-                <xs:attribute name="onError"  type="errorhandler-type"/>
-                <xs:attribute name="optimize" type="xs:string"/>
-            </xs:extension>
-        </xs:simpleContent>
-    </xs:complexType>
-
-    <!-- executes a script, e.g. SQL -->
-    <xs:element name="execute" />
-    
-    <!-- evaluates a script, e.g. SQL -->
-    <xs:element name="evaluate">
-        <xs:complexType>
-            <xs:simpleContent>
-                <xs:extension base="execute-type">
-                    <xs:attribute name="assert" type="xs:string"/>
-                </xs:extension>
-            </xs:simpleContent>
-        </xs:complexType>
-    </xs:element>
-    
-	<!-- Declares a JavaBean to be used as consumer, either by referencing an object from the context (ref)
-    	 or by instantiating a JavaBean like with the &lt;bean&gt; tag. -->
-	<xs:element name="consumer">
-		<xs:complexType>
-			<xs:sequence minOccurs="1" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="id"    type="xs:string"/>
-			<xs:attribute name="class" type="xs:string"/>
-			<xs:attribute name="ref"   type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Sets a JavaBean's property to the simple property specified as 'value' attribute
-	     or to the object stored in the context with the 'ref'. -->
-	<xs:element name="property">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="bean"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="idref" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="name"  type="xs:string"/>
-			<xs:attribute name="value" type="xs:string"/>
-			<xs:attribute name="ref"   type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-	
-	<!-- Represents a reference to a JavaBean (of the name specified by the 'bean' attribute) from the context. -->
-	<xs:element name="idref">
-		<xs:complexType>
-			<xs:attribute name="bean"  type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Instantiates and executes a JavaBean that implements the Task interface.
-	     Properties are handled like in the &lt;bean&gt; tag.
-	     The task is executed 'count' times in a number of 'threads' parallel threads.
-	     Invocations are executed in groups of size 'pagesize'. -->
-	<xs:element name="run-task">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="class"    type="xs:string"/>
-			<xs:attribute name="count"    type="xs:string"/>
-			<xs:attribute name="pagesize" type="xs:string"/>
-			<xs:attribute name="threads"  type="xs:string"/>
-			<xs:attribute name="pager"    type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<xs:element name="defaultAttributes">
-		<xs:complexType>
-			<xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"  minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="id"              minOccurs="0" maxOccurs="1"/>
-				<xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-				<xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-			</xs:sequence>
-			<xs:attribute name="name"     type="xs:string"/>
-			<xs:attribute name="count"    type="xs:string"/>
-			<xs:attribute name="pagesize" type="xs:string"/>
-			<xs:attribute name="threads"  type="xs:string"/>
-			<xs:attribute name="source"   type="xs:string"/>
-			<xs:attribute name="encoding" type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-    <!-- Creates entities. If a 'count' is provided the corresponding number of entities is created.
-         If no count is provided, entities are created as long as each involved generator is available. -->
-    <xs:element name="create-entities">
-        <xs:complexType>
-            <xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"         minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="id"              minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="variable"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="consumer"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities" minOccurs="0" maxOccurs="unbounded"/>
-            </xs:sequence>
-            <xs:attribute name="name"     type="xs:string"/>
-            <xs:attribute name="count"    type="xs:string"/>
-            <xs:attribute name="minCount" type="xs:string"/>
-            <xs:attribute name="maxCount" type="xs:string"/>
-            <xs:attribute name="pagesize" type="xs:string"/>
-            <xs:attribute name="threads"  type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="selector" type="xs:string"/>
-            <xs:attribute name="encoding" type="xs:string"/>
-            <xs:attribute name="consumer" type="xs:string"/>
-        </xs:complexType>
-    </xs:element>
-
-    <!-- Updates entities. -->
-    <xs:element name="update-entities">
-        <xs:complexType>
-            <xs:sequence minOccurs="0" maxOccurs="unbounded">
-				<xs:element ref="comment"         minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="id"              minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="attribute"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="reference"       minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="variable"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="consumer"        minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="create-entities" minOccurs="0" maxOccurs="unbounded"/>
-                <xs:element ref="update-entities" minOccurs="0" maxOccurs="unbounded"/>
-            </xs:sequence>
-            <xs:attribute name="name"     type="xs:string"/>
-            <xs:attribute name="count"    type="xs:string"/>
-            <xs:attribute name="pagesize" type="xs:string"/>
-            <xs:attribute name="threads"  type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="selector" type="xs:string"/>
-            <xs:attribute name="encoding" type="xs:string"/>
-            <xs:attribute name="consumer" type="xs:string"/>
-        </xs:complexType>
-    </xs:element>
-
-	<!-- Defines an ID generator that can be reused in different scopes. -->
-	<xs:element name="id">
-		<xs:complexType>
-			<xs:attribute name="name"     type="xs:string"/>
-			<xs:attribute name="type"     type="xs:string"/>
-            <xs:attribute name="source"   type="xs:string"/>
-            <xs:attribute name="strategy" type="xs:string"/>
-            <xs:attribute name="scope"    type="xs:string"/>
-			<xs:attribute name="param"    type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Defines a reference generator. -->
-	<xs:element name="reference">
-		<xs:complexType>
-			<xs:attribute name="name"         type="xs:string"/>
-			<xs:attribute name="type"         type="xs:string"/>
-			<xs:attribute name="nullable"     type="xs:string"/>
-			<xs:attribute name="nullQuota"    type="xs:string"/>
-			<xs:attribute name="source"       type="xs:string"/>
-			<xs:attribute name="targetType"   type="xs:string"/>
-			<xs:attribute name="selector"     type="xs:string"/>
-			<xs:attribute name="distribution" type="xs:string"/>
-			<xs:attribute name="mode"         type="xs:string"/>
-		</xs:complexType>
-	</xs:element>
-
-	<!-- Defines a variable that is recreated by a generator on each entity creation -->
-	<xs:element name="variable" type="generator-setup"/>
-
-	<xs:complexType name="generator-setup">
-		<xs:attribute name="name"         type="xs:string" use="required"/>
-		<xs:attribute name="type"         type="xs:string"/>
-		<xs:attribute name="generator"    type="xs:string"/>
-		<xs:attribute name="validator"    type="xs:string"/>
-		<xs:attribute name="values"       type="xs:string"/>
-		<xs:attribute name="source"       type="xs:string"/>
-		<xs:attribute name="encoding"     type="xs:string"/>
-		<xs:attribute name="selector"     type="xs:string"/>
-		<xs:attribute name="pattern"      type="xs:string"/>
-		<xs:attribute name="nullQuota"    type="xs:string"/>
-		<xs:attribute name="converter"    type="xs:string"/>
-		<xs:attribute name="dataset"      type="xs:string"/>
-		<xs:attribute name="nesting"      type="xs:string"/>
-		<xs:attribute name="locale"       type="xs:string"/>
-		<xs:attribute name="unique"       type="xs:string"/>
-		<xs:attribute name="distribution" type="xs:string"/>
-		<xs:attribute name="variation1"   type="xs:string"/>
-		<xs:attribute name="variation2"   type="xs:string"/>
-		<xs:attribute name="cyclic"       type="xs:string"/>
-		<xs:attribute name="proxy">
-			<xs:simpleType>
-				<xs:union>
-					<xs:simpleType> <!-- (skip | repeat) -->
-						<xs:restriction base="xs:string">
-							<xs:enumeration value="skip"/>
-							<xs:enumeration value="repeat"/>
-						</xs:restriction>
-					</xs:simpleType>
-					<xs:simpleType>
-						<xs:restriction base="script-expression-type"/>
-					</xs:simpleType>
-				</xs:union>
-			</xs:simpleType>
-		</xs:attribute>
-		<xs:attribute name="proxy-param1" type="xs:string"/>
-		<xs:attribute name="proxy-param2" type="xs:string"/>
-	</xs:complexType>
-	
-	<xs:element name="attribute" type="attribute-generator-setup"/>
-	
-	<xs:complexType name="attribute-generator-setup">
-		<xs:complexContent>
-			<xs:extension base="generator-setup">
-			
-				<xs:attribute name="nullable"     type="xs:string"/>
-				<xs:attribute name="mode"         type="xs:string"/>
-
-				<!-- boolean generator setup -->
-				<xs:attribute name="trueQuota"    type="xs:string"/>
-				
-				<!-- number generator setup -->
-				<xs:attribute name="min"          type="xs:string"/>
-				<xs:attribute name="minExclusive" type="xs:string"/>
-				<xs:attribute name="max"          type="xs:string"/>
-				<xs:attribute name="maxExclusive" type="xs:string"/>
-				<xs:attribute name="precision"    type="xs:string"/>
-				
-				<!-- String generator setup -->
-				<xs:attribute name="script"       type="xs:string"/>
-				<xs:attribute name="minLength"    type="xs:string"/>
-				<xs:attribute name="maxLength"    type="xs:string"/>
-				
-			</xs:extension>
-		</xs:complexContent>
-	</xs:complexType>
-	
-</xs:schema>

Deleted: trunk/utils/dna-jcr-gen/src/main/resources/log4j.properties
===================================================================
--- trunk/utils/dna-jcr-gen/src/main/resources/log4j.properties	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/src/main/resources/log4j.properties	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,13 +0,0 @@
-# Direct log messages to stdout
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.Target=System.out
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
-
-# Root logger option
-log4j.rootLogger=INFO, stdout
-
-# Set up the default logging to be INFO level, then override specific units
-log4j.logger.org.jboss.dna=INFO
-log4j.logger.org.jboss.example.dna=INFO
-

Deleted: trunk/utils/dna-jcr-gen/src/test/resources/benerator/bank.properties
===================================================================
--- trunk/utils/dna-jcr-gen/src/test/resources/benerator/bank.properties	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/src/test/resources/benerator/bank.properties	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,5 +0,0 @@
-bankCount=20
-minProd=3
-maxProd=20
-minCont=4
-maxCont=20

Deleted: trunk/utils/dna-jcr-gen/src/test/resources/benerator/benerator.xml
===================================================================
--- trunk/utils/dna-jcr-gen/src/test/resources/benerator/benerator.xml	2009-11-27 02:26:23 UTC (rev 1363)
+++ trunk/utils/dna-jcr-gen/src/test/resources/benerator/benerator.xml	2009-11-27 03:08:49 UTC (rev 1364)
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<setup xmlns="http://databene.org/benerator/0.5.9-SR1"
-		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-		xsi:schemaLocation="http://databene.org/benerator/0.5.9-SR1 benerator-0.5.9-SR1.xsd">
-
-	<!-- Already existed generator -->
-	<import domain="person" />
-	<import domain="address" />
-	
-	<!-- My consumer for my InMmemery repos. source -->
-	<bean id="dnaJcrConsumer" spec="org.jboss.dna.jcr.gen.JCRDataMassGen('firstSource',
-                                'this is only my first jcr repos source with some test data',
-                                'defaultWorkspaceName',
-                                 'WS1',
-                                 'test repository',
-                                 'org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource',
-                                 'username',true)"/>
-    
-    <!-- Some extras data -->
-	<include uri="bank.properties"/>
-		
-    <!-- My Model -->
-	<create-entities name="Bank" consumer="dnaJcrConsumer" count="{${bankCount}}">
-		<attribute name="nodeName" type="string" pattern="[A-Z]{3} Bank" unique="true"/>
-		
-		<create-entities name="Product" consumer="dnaJcrConsumer" minCount="{${minProd}}" maxCount="{${maxProd}}">
-			<attribute name="nodeName" type="string" pattern="[A-Z]{3} Credit" unique="true"/>
-			
-			<create-entities name="Contract" consumer="dnaJcrConsumer" minCount="{${minCont}}" maxCount="{${maxCont}}">
-				<id name="id" strategy="increment(100000)"/>
-				<attribute name="nodeName" script="{Contract #${Contract.id}}"/>
-				<attribute name="amount" type="float" min="1000" max="20000" distribution="cumulated"/>
-				<attribute name="interest" type="big_decimal" min="7" max="12" precision="0.05"/>
-				
-				<create-entities name="Person" consumer="dnaJcrConsumer" count="1">
-					<variable name="person" generator="PersonGenerator" dataset="US" locale="en"/>
-					<attribute name="nodeName" script="{${person.givenName} ${person.familyName}}"/>
-					<attribute name="salutation" script="{${person.salutation}}"/>
-					<attribute name="firstName" script="{${person.givenName}}"/>
-					<attribute name="surname" script="{${person.familyName}}"/>
-					<attribute name="birthDate" type="date" source="person.birthDate"/>
-				</create-entities>
-				
-				<create-entities name="Address" consumer="dnaJcrConsumer" count="1">
-					<variable name="address" generator="AddressGenerator" dataset="US"/>
-					<id name="id" strategy="increment(100000)"/>
-					<attribute name="nodeName" script="{Address #${Address.id}}"/>
-					<attribute name="street" script="{${address.street} ${address.houseNumber}}"/>
-					<attribute name="zipCode" script="{${address.zipCode}}"/>
-					<attribute name="city" script="{${address.city}}"/>
-					<attribute name="country" script="{${address.country}}"/>
-				</create-entities>
-			</create-entities>
-		</create-entities>
-	</create-entities>
-
-</setup>
\ No newline at end of file


From dna-commits at lists.jboss.org  Fri Nov 27 11:52:16 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 27 Nov 2009 11:52:16 -0500
Subject: [dna-commits] DNA SVN: r1365 - in trunk:
	docs/reference/src/main/docbook/en-US/content/connectors and
	16 other directories.
Message-ID: <200911271652.nARGqGG1014026@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-27 11:52:16 -0500 (Fri, 27 Nov 2009)
New Revision: 1365

Added:
   trunk/utils/dna-jpa-ddl-gen/
   trunk/utils/dna-jpa-ddl-gen/.classpath
   trunk/utils/dna-jpa-ddl-gen/.project
   trunk/utils/dna-jpa-ddl-gen/pom.xml
   trunk/utils/dna-jpa-ddl-gen/src/
   trunk/utils/dna-jpa-ddl-gen/src/main/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/org/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/
   trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/SchemaGen.java
   trunk/utils/dna-jpa-ddl-gen/src/main/resources/
   trunk/utils/dna-jpa-ddl-gen/src/main/resources/log4j.properties
   trunk/utils/dna-jpa-ddl-gen/src/test/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/org/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/
   trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/SchemaGenTest.java
Modified:
   trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
   trunk/pom.xml
Log:
DNA-511 The JPA Source should provide a way of producing the DDL for the database dialect

Committed new version of previous patch that adds comments, documentation, and more tolerance for variations on the dialect parameter.

Modified: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml	2009-11-27 03:08:49 UTC (rev 1364)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml	2009-11-27 16:52:16 UTC (rev 1365)
@@ -263,6 +263,25 @@
 		Of course, setting other more advanced properties would entail calling <code>setProperty(...)</code> for each.  Since almost all
 		of the properties have acceptable default values, however, we don't need to set very many of them.
 	</para>
+	<para>
+	DNA users who prefer not to give DDL privileges to the DNA database user for this connector can use the DNA JPA DDL generation 
+	tool to create the proper DDL files for their database dialect.  This tool is packaged as an executable jar in the 
+	utils/dna-jpa-ddl-gen subproject and can be executed with the following syntax:
+	</para>
+<programlisting>
+java -jar &lt;jar_name> -dialect &lt;dialect name&gt; -model &lt;model_name&gt; [-out &lt;path to output directory&gt;]
+</programlisting>
+	<para>
+	The dialect and model parameters should match the value of the <code>dialect</code> and <code>model</code> properties specified for the
+	JPA connector.  
+	</para>
+	<para>
+	Running this executable will create two files in the output directory (or the current directory if no output directory
+	was specified): create.dna-jpa-connector.ddl and drop.dna-jpa-connector.ddl.  The former contains the DDL to create or replace the tables,
+	foreign keys, indices, and sequences needed by the JPA connector and the latter containts the DDL to drop any tables, foreign keys, indices, and 
+	sequences needed by the JPA connector.
+	</para>
+
 	<sect2>
 		<title>Basic Model</title>
 		<para>

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-11-27 03:08:49 UTC (rev 1364)
+++ trunk/pom.xml	2009-11-27 16:52:16 UTC (rev 1365)
@@ -145,6 +145,7 @@
 		<module>web/dna-web-jcr-rest</module> 
 		<module>web/dna-web-jcr-rest-client</module> 
 		<module>web/dna-web-jcr-rest-war</module> 
+		<module>utils/dna-jpa-ddl-gen</module> 
 	</modules>
 	<properties>
 		<!-- These are properties used in the database profiles. Must initialize them to be empty so that Maven applies

Added: trunk/utils/dna-jpa-ddl-gen/.classpath
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/.classpath	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/.classpath	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Added: trunk/utils/dna-jpa-ddl-gen/.project
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/.project	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/.project	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna-jpa-ddl-gen</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

Added: trunk/utils/dna-jpa-ddl-gen/pom.xml
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/pom.xml	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/pom.xml	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,111 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.jboss.dna</groupId>
+		<artifactId>dna</artifactId>
+		<version>0.7-SNAPSHOT</version>
+		<relativePath>../..</relativePath>
+	</parent>
+	<!-- The groupId and version values are inherited from parent -->
+	<artifactId>dna-jpa-ddl-gen</artifactId>
+	<packaging>jar</packaging>
+	<name>JBoss JPA Connector DDL Generator</name>
+	<description>
+		Utility application to generate ddl for JBoss DNA JPA connector
+	</description>
+	<url>http://labs.jboss.org/dna</url>
+	<!--
+		Define the dependencies. Note that all version and scopes default to
+		those defined in the dependencyManagement section of the parent pom.
+	-->
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<descriptorRefs>
+						<descriptorRef>jar-with-dependencies</descriptorRef>
+					</descriptorRefs>
+					<archive>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<mainClass>org.jboss.dna.util.SchemaGen</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-my-jar-with-dependencies</id>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+ 			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<!-- 
+			Common
+		-->
+		<dependency>
+			<groupId>org.jboss.dna</groupId>
+			<artifactId>dna-connector-store-jpa</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.hibernate</groupId>
+			<artifactId>hibernate-tools</artifactId>
+			<version>3.2.4.GA</version>
+		</dependency>
+		<!-- 
+			Testing (note the scope)
+		-->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.hamcrest</groupId>
+			<artifactId>hamcrest-library</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<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)
+		-->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+		    <!--  This defaults to test scope; needs to be compile scope to be in executable JAR -->
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+		    <!--  This defaults to test scope; needs to be compile scope to be in executable JAR -->
+			<scope>compile</scope>
+		</dependency>
+	</dependencies>
+	<reporting>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-report-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</reporting>
+</project>
\ No newline at end of file


Property changes on: trunk/utils/dna-jpa-ddl-gen/pom.xml
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/SchemaGen.java
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/SchemaGen.java	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/SchemaGen.java	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,128 @@
+package org.jboss.dna.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.Model;
+
+/**
+ * Main class to generate DDL that can be used to build the schema for the {@link JpaSource JPA connector}. The class is intended
+ * to be bundled into an executable jar file and invoked with the following syntax:
+ * 
+ * <pre>
+ * java -jar &lt;jar_name&gt; -dialect &lt;dialect name&gt; -model &lt;model_name&gt; [-out &lt;path to output directory&gt;]
+ *     Example: java -jar dna-jpa-ddl-gen-0.7-jar-with-dependencies.jar -dialect HSQL -model Basic -out /tmp
+ * </pre>
+ */
+public class SchemaGen {
+
+    public static final String CREATE_FILE_NAME = "create.dna-jpa-connector.ddl";
+    public static final String DROP_FILE_NAME = "drop.dna-jpa-connector.ddl";
+
+    private final Dialect dialect;
+    private final Model model;
+    private final File outputPath;
+
+    public SchemaGen( String dialect,
+                      String model,
+                      File outputPath ) {
+
+        this.dialect = dialectFor(dialect);
+        this.model = JpaSource.Models.getModel(model);
+        this.outputPath = outputPath;
+    }
+
+    /**
+     * Returns the {@link Dialect Hibernate dialect} that corresponds to the provided name.
+     * <p>
+     * This method will tolerate certain kinds of abbreviations for the dialect. Since all Hibernate dialects have a name equal to
+     * their fully-qualified name, all valid dialect names start with &quot;org.hibernate.dialect.&quot;. If the given {@code
+     * dialectName} does not begin with this string, this string will be prepended.
+     * </p>
+     * <p>
+     * Additionally, all Hibernate dialect names end in &quot;Dialect&quot;. If the given dialect name does not end with
+     * &quot;Dialect&quot;, this will be appended to the dialect name before the Hibernate dialect is looked up.
+     * </p>
+     * <p>
+     * The net effect of these rules is that callers wishing to retrieve a dialect (e.g., org.hibernate.dialect.HSQLDialect) can
+     * specify the dialect as "org.hibernate.dialect.HSQLDialect", "HSQLDialect", "org.hibernate.dialect.HSQL", or just "HSQL".
+     * </p>
+     * 
+     * @param dialectName the name of the {@code Dialect} to return
+     * @return the {@link Dialect Hibernate dialect} that corresponds to the provided name.
+     */
+    private Dialect dialectFor( String dialectName ) {
+        Properties props = new Properties();
+
+        if (!dialectName.endsWith("Dialect")) dialectName += "Dialect";
+        if (!dialectName.startsWith("org.hibernate.dialect.")) dialectName = "org.hibernate.dialect." + dialectName;
+
+        props.put(Environment.DIALECT, dialectName);
+
+        return Dialect.getDialect(props);
+    }
+
+    /**
+     * Writes {@link SchemaExport#create(boolean, boolean) create} and {@link SchemaExport#drop(boolean, boolean) drop} DDL files
+     * to the given {@link #outputPath output directory} or the current directory if no output directory was provided.
+     * 
+     * @throws IOException
+     */
+    void generate() throws IOException {
+        Ejb3Configuration configurator = new Ejb3Configuration();
+        configurator.setProperty(Environment.DIALECT, dialect.toString());
+        model.configure(configurator);
+        // cfg.setProperties(properties);
+        SchemaExport export = new SchemaExport(configurator.getHibernateConfiguration());
+        export.setOutputFile(new File(outputPath, CREATE_FILE_NAME).getCanonicalPath());
+        export.create(false, false);
+
+        export.setOutputFile(new File(outputPath, DROP_FILE_NAME).getCanonicalPath());
+        export.drop(false, false);
+    }
+
+    public static final String USAGE = "java -jar <jar_name> -dialect <dialect name> -model <model_name> [-out <path to output directory>]\n"
+                                       + "\tExample: java -jar dna-jpa-ddl-gen-0.7-jar-with-dependencies.jar -dialect HSQL -model Basic -out /tmp";
+
+    public static void main( String[] args ) throws IOException {
+        String modelName = null;
+        String dialectName = null;
+        File outputFile = new File(".");
+
+        int i = 0;
+        while (i < args.length) {
+            if ("-dialect".equals(args[i])) {
+                if (i == args.length - 1) printUsage();
+                dialectName = args[++i];
+            } else if ("-model".equals(args[i])) {
+                if (i == args.length - 1) printUsage();
+                modelName = args[++i];
+            } else if ("-out".equals(args[i])) {
+                if (i == args.length - 1) printUsage();
+                outputFile = new File(args[++i]);
+            } else if ("-help".equals(args[i]) || "-?".equals(args[i])) {
+                printUsage();
+            }
+            i++;
+        }
+
+        if (modelName == null || dialectName == null) printUsage();
+
+        SchemaGen main = new SchemaGen(dialectName, modelName, outputFile);
+        main.generate();
+    }
+
+    /**
+     * Print the usage message and exit with a non-zero return code.
+     */
+    private static void printUsage() {
+        System.err.println(USAGE);
+        System.exit(1);
+    }
+
+}


Property changes on: trunk/utils/dna-jpa-ddl-gen/src/main/java/org/jboss/dna/util/SchemaGen.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/utils/dna-jpa-ddl-gen/src/main/resources/log4j.properties
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/src/main/resources/log4j.properties	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/src/main/resources/log4j.properties	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,20 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+log4j.logger.org.junit=DEBUG
+# log4j.logger.org.jboss.dna.tests.integration.jackrabbit.JackrabbitMySqlStressTest=DEBUG
+log4j.logger.org.hibernate=WARN, stdout
+
+
+# Jackrabbit logging
+log4j.logger.org.apache.jackrabbit=WARN, stdout
+log4j.logger.org.apache.derby=INFO, stdout
+

Added: trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/SchemaGenTest.java
===================================================================
--- trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/SchemaGenTest.java	                        (rev 0)
+++ trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/SchemaGenTest.java	2009-11-27 16:52:16 UTC (rev 1365)
@@ -0,0 +1,66 @@
+package org.jboss.dna.util;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import java.io.File;
+import java.io.IOException;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SchemaGenTest {
+
+    private static final File outputPath = new File("target");
+
+    @Before
+    public void beforeEach() {
+        cleanFiles();
+    }
+
+    @AfterClass
+    // Run this after the last test to keep the target directory clean
+    public static void cleanFiles() {
+        new File(outputPath, SchemaGen.CREATE_FILE_NAME).delete();
+        new File(outputPath, SchemaGen.DROP_FILE_NAME).delete();
+    }
+
+    private void checkFiles() {
+        File createFile = new File(outputPath, SchemaGen.CREATE_FILE_NAME);
+        assertThat(createFile.exists(), is(true));
+        assertThat(createFile.length(), greaterThan(0L));
+
+        File dropFile = new File(outputPath, SchemaGen.DROP_FILE_NAME);
+        assertThat(dropFile.exists(), is(true));
+        assertThat(dropFile.length(), greaterThan(0L));
+    }
+
+    @Test
+    public void shouldCreateSchemaForHSqlDialect() throws IOException {
+        SchemaGen main = new SchemaGen("org.hibernate.dialect.HSQLDialect", "Basic", outputPath);
+        main.generate();
+        checkFiles();
+    }
+
+    @Test
+    public void shouldCreateSchemaForOracleDialect() throws IOException {
+        SchemaGen main = new SchemaGen("org.hibernate.dialect.Oracle10gDialect", "Basic", outputPath);
+        main.generate();
+        checkFiles();
+    }
+
+    @Test
+    public void shouldCreateSchemaForDialectThatIsNotFullyQualified() throws IOException {
+        SchemaGen main = new SchemaGen("HSQLDialect", "Basic", outputPath);
+        main.generate();
+        checkFiles();
+    }
+
+    @Test
+    public void shouldCreateSchemaForShortFormDialect() throws IOException {
+        SchemaGen main = new SchemaGen("HSQL", "Basic", outputPath);
+        main.generate();
+        checkFiles();
+    }
+
+}


Property changes on: trunk/utils/dna-jpa-ddl-gen/src/test/java/org/jboss/dna/util/SchemaGenTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Fri Nov 27 12:22:55 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 27 Nov 2009 12:22:55 -0500
Subject: [dna-commits] DNA SVN: r1366 - in
	trunk/extensions/dna-connector-store-jpa/src:
	main/java/org/jboss/dna/connector/store/jpa/model/basic and 2
	other directories.
Message-ID: <200911271722.nARHMthx020351@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-27 12:22:54 -0500 (Fri, 27 Nov 2009)
New Revision: 1366

Added:
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java
Modified:
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/EntityManagers.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModel.java
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModelTest.java
Log:
DNA-546

Committed patch (DNA-546_jpa_connector_refactor.patch) that refactors the existing JPA connector framework to more readily support the new model that I'm working on.

The patch removes the relationship between the model and the request processor and adds a relationship between the model and its connection. To work within the MapRepository framework, I had to be able to pass a reference to the MapRepository into the MapRequestProcessor. This ruled out using the existing JpaConnection. The cleanest way to isolate the reusable pieces of the code seemed to be to rename JpaConnection to BasicJpaConnection, move it into model.basic, and then replace Model.createRequestProcessor(...) with Model.createConnection(JpaSource). This required a few changes to class visibility (but not constructor visibility) and the addition of some accessors to JpaSource.


Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/EntityManagers.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/EntityManagers.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/EntityManagers.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -30,16 +30,17 @@
 import javax.persistence.EntityManagerFactory;
 import net.jcip.annotations.ThreadSafe;
 import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.graph.connector.RepositoryConnection;
 
 /**
  * Utility class that owns an {@link EntityManagerFactory} instance and that provides references to {@link EntityManager}
  * instances while providing the ability to properly clean up all resources by closing the EntityManager and EntityManagerFactory
  * objects when no longer needed.
  * <p>
- * This class is instantiated by the {@link JpaSource} and passed to the {@link JpaConnection} objects, which use this class to
- * obtain an EntityManager. When the JpaConnection object is {@link JpaConnection#close() closed}, it returns the EntityManager to
- * this object. Because this class maintains a count of the EntityManager references handed out, the last EntityManager to be
- * returned will cause the EntityManagerFactory to be closed.
+ * This class is instantiated by the {@link JpaSource} and passed to the {@link RepositoryConnection} objects, which use this
+ * class to obtain an EntityManager. When the JPA connection object is {@link RepositoryConnection#close() closed}, it returns the
+ * EntityManager to this object. Because this class maintains a count of the EntityManager references handed out, the last
+ * EntityManager to be returned will cause the EntityManagerFactory to be closed.
  * </p>
  * <p>
  * This class does put the EntityManager implementations inside a HashMap, and therefore does expect that the EntityManager uses
@@ -47,7 +48,7 @@
  * </p>
  */
 @ThreadSafe
-class EntityManagers {
+public class EntityManagers {
 
     private final Ejb3Configuration configuration;
     private final Map<EntityManager, AtomicInteger> referenceCounts = new HashMap<EntityManager, AtomicInteger>();

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -59,7 +59,6 @@
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.observe.Observer;
 
 /**
  * The {@link RepositorySource} for the connector that stores content in a (custom) relational database. This connector uses Java
@@ -75,7 +74,8 @@
      */
     public static class Models {
         public static final Model BASIC = new BasicModel();
-        private static final Model[] ALL_ARRAY = new Model[] {BASIC};
+        // public static final Model SIMPLE = new SimpleModel();
+        private static final Model[] ALL_ARRAY = new Model[] {BASIC /*, SIMPLE */};
         private static final List<Model> MODIFIABLE_MODELS = new ArrayList<Model>(Arrays.asList(ALL_ARRAY));
         public static final Collection<Model> ALL = Collections.unmodifiableCollection(MODIFIABLE_MODELS);
         public static final Model DEFAULT = BASIC;
@@ -367,6 +367,24 @@
     }
 
     /**
+     * Returns the current cache policy
+     * 
+     * @return the current cache policy
+     */
+    public CachePolicy getCachePolicy() {
+        return cachePolicy;
+    }
+
+    /**
+     * Returns the current {@code EntityManagers} reference.
+     * 
+     * @return the current {@code EntityManagers} reference.
+     */
+    public EntityManagers getEntityManagers() {
+        return entityManagers;
+    }
+
+    /**
      * @return rootNodeUuid
      */
     public String getRootNodeUuid() {
@@ -374,6 +392,13 @@
     }
 
     /**
+     * @return rootUuid
+     */
+    public UUID getRootUuid() {
+        return rootUuid;
+    }
+
+    /**
      * @param rootNodeUuid Sets rootNodeUuid to the specified value.
      * @throws IllegalArgumentException if the string value cannot be converted to UUID
      */
@@ -812,6 +837,15 @@
     }
 
     /**
+     * Returns the current repository context for the source, as set with a call to {@link #initialize(RepositoryContext)}.
+     * 
+     * @return the current repository context for the source
+     */
+    public RepositoryContext getRepositoryContext() {
+        return repositoryContext;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public Object getObjectInstance( Object obj,
@@ -1000,10 +1034,8 @@
             // Now, create another entity manager with the classes from the correct model and without changing the schema...
             entityManagers = new EntityManagers(configurator);
         }
-        Observer observer = repositoryContext != null ? repositoryContext.getObserver() : null;
-        return new JpaConnection(getName(), observer, cachePolicy, entityManagers, model, rootUuid, defaultWorkspace,
-                                 getPredefinedWorkspaceNames(), largeValueSizeInBytes, isCreatingWorkspacesAllowed(),
-                                 compressData, referentialIntegrityEnforced);
+
+        return model.createConnection(this);
     }
 
     /**

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -24,14 +24,10 @@
 package org.jboss.dna.connector.store.jpa;
 
 import java.util.Locale;
-import java.util.UUID;
-import javax.persistence.EntityManager;
 import org.hibernate.ejb.Ejb3Configuration;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.observe.Observer;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
+import org.jboss.dna.graph.connector.RepositoryConnection;
 
 /**
  * A descriptor of a schema used by this connector.
@@ -76,17 +72,7 @@
         return description.text(locale);
     }
 
-    public abstract RequestProcessor createRequestProcessor( String sourceName,
-                                                             ExecutionContext context,
-                                                             Observer observer,
-                                                             EntityManager entityManager,
-                                                             UUID rootNodeUuid,
-                                                             String nameOfDefaultWorkspace,
-                                                             String[] predefinedWorkspaceNames,
-                                                             long largeValueMinimumSizeInBytes,
-                                                             boolean creatingWorkspacesAllowed,
-                                                             boolean comparessData,
-                                                             boolean enforceReferentialIntegrity );
+    public abstract RepositoryConnection createConnection( JpaSource source );
 
     /**
      * Configure the entity class that will be used by JPA to store information in the database.

Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java	                        (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -0,0 +1,163 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.connector.store.jpa.model.basic;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import javax.persistence.EntityManager;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.connector.store.jpa.EntityManagers;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.observe.Observer;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * The repository connection to JPA repository sources.
+ */
+class BasicJpaConnection implements RepositoryConnection {
+
+    private final String name;
+    private final CachePolicy cachePolicy;
+    private final EntityManagers entityManagers;
+    private EntityManager entityManager;
+    private final UUID rootNodeUuid;
+    private final String nameOfDefaultWorkspace;
+    private final String[] predefinedWorkspaceNames;
+    private final boolean creatingWorkspacesAllowed;
+    private final long largeValueMinimumSizeInBytes;
+    private final boolean compressData;
+    private final boolean enforceReferentialIntegrity;
+    private final Observer observer;
+
+    public BasicJpaConnection( String sourceName,
+                               Observer observer,
+                               CachePolicy cachePolicy,
+                               EntityManagers entityManagers,
+                               UUID rootNodeUuid,
+                               String nameOfDefaultWorkspace,
+                               String[] predefinedWorkspaceNames,
+                               long largeValueMinimumSizeInBytes,
+                               boolean creatingWorkspacesAllowed,
+                               boolean compressData,
+                               boolean enforceReferentialIntegrity ) {
+        assert sourceName != null;
+        assert entityManagers != null;
+        assert rootNodeUuid != null;
+        this.observer = observer;
+        this.name = sourceName;
+        this.cachePolicy = cachePolicy; // may be null
+        this.entityManagers = entityManagers;
+        this.entityManager = entityManagers.checkout();
+        assert this.entityManagers != null;
+        this.rootNodeUuid = rootNodeUuid;
+        this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+        this.compressData = compressData;
+        this.enforceReferentialIntegrity = enforceReferentialIntegrity;
+        this.nameOfDefaultWorkspace = nameOfDefaultWorkspace;
+        this.predefinedWorkspaceNames = predefinedWorkspaceNames != null ? predefinedWorkspaceNames : new String[] {};
+        this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
+     */
+    public String getSourceName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
+     */
+    public CachePolicy getDefaultCachePolicy() {
+        return cachePolicy;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
+     */
+    public XAResource getXAResource() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
+     */
+    public boolean ping( long time,
+                         TimeUnit unit ) {
+        return entityManager != null ? entityManager.isOpen() : false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+     *      org.jboss.dna.graph.request.Request)
+     */
+    public void execute( ExecutionContext context,
+                         Request request ) throws RepositorySourceException {
+        if (entityManager == null) {
+            throw new RepositorySourceException(JpaConnectorI18n.connectionIsNoLongerOpen.text(name));
+        }
+
+        RequestProcessor proc = new BasicRequestProcessor(name, context, observer, entityManager, rootNodeUuid,
+                                                          nameOfDefaultWorkspace, predefinedWorkspaceNames,
+                                                          largeValueMinimumSizeInBytes, creatingWorkspacesAllowed, compressData,
+                                                          enforceReferentialIntegrity);
+        try {
+            proc.process(request);
+        } finally {
+            proc.close();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
+     */
+    public void close() {
+        if (entityManager != null) {
+            // Do this only once ...
+            try {
+                entityManagers.checkin(entityManager);
+            } finally {
+                entityManager = null;
+            }
+        }
+    }
+
+}


Property changes on: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModel.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModel.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModel.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -23,21 +23,20 @@
  */
 package org.jboss.dna.connector.store.jpa.model.basic;
 
-import java.util.UUID;
-import javax.persistence.EntityManager;
 import org.hibernate.ejb.Ejb3Configuration;
 import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.JpaSource;
 import org.jboss.dna.connector.store.jpa.Model;
 import org.jboss.dna.connector.store.jpa.model.common.ChangeLogEntity;
 import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity;
 import org.jboss.dna.connector.store.jpa.model.common.WorkspaceEntity;
-import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryContext;
 import org.jboss.dna.graph.observe.Observer;
 import org.jboss.dna.graph.request.CopyBranchRequest;
 import org.jboss.dna.graph.request.DeleteBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadBranchRequest;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
 
 /**
  * Database model that stores node properties as opaque records and children as transparent records. Large property values are
@@ -98,29 +97,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.store.jpa.Model#createRequestProcessor(String, ExecutionContext, Observer, EntityManager,
-     *      UUID, String, String[], long, boolean, boolean, boolean)
-     */
-    @Override
-    public RequestProcessor createRequestProcessor( String sourceName,
-                                                    ExecutionContext context,
-                                                    Observer observer,
-                                                    EntityManager entityManager,
-                                                    UUID rootNodeUuid,
-                                                    String nameOfDefaultWorkspace,
-                                                    String[] predefinedWorkspaceNames,
-                                                    long largeValueMinimumSizeInBytes,
-                                                    boolean creatingWorkspacesAllowed,
-                                                    boolean compressData,
-                                                    boolean enforceReferentialIntegrity ) {
-        return new BasicRequestProcessor(sourceName, context, observer, entityManager, rootNodeUuid, nameOfDefaultWorkspace,
-                                         predefinedWorkspaceNames, largeValueMinimumSizeInBytes, creatingWorkspacesAllowed,
-                                         compressData, enforceReferentialIntegrity);
-    }
-
-    /**
      * Configure the entity class that will be used by JPA to store information in the database.
      * 
      * @param configurator the Hibernate {@link Ejb3Configuration} component; never null
@@ -148,4 +124,15 @@
         // "read-write, RegionName");
     }
 
+    @Override
+    public RepositoryConnection createConnection( JpaSource source ) {
+        RepositoryContext repositoryContext = source.getRepositoryContext();
+        Observer observer = repositoryContext != null ? repositoryContext.getObserver() : null;
+        return new BasicJpaConnection(getName(), observer, source.getCachePolicy(), source.getEntityManagers(),
+                                 source.getRootUuid(),
+                                 source.getDefaultWorkspaceName(), source.getPredefinedWorkspaceNames(),
+                                 source.getLargeValueSizeInBytes(), source.isCreatingWorkspacesAllowed(),
+                                 source.isCompressData(), source.isReferentialIntegrityEnforced());
+    }
+
 }

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -26,13 +26,9 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertThat;
-import java.util.UUID;
-import javax.persistence.EntityManager;
 import org.hibernate.ejb.Ejb3Configuration;
 import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.observe.Observer;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
+import org.jboss.dna.graph.connector.RepositoryConnection;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -50,7 +46,7 @@
     private Model model2;
     private Model model3;
     @Mock
-    private RequestProcessor requestProcessor;
+    private RepositoryConnection connection;
 
     @Before
     public void beforeEach() throws Exception {
@@ -104,20 +100,10 @@
         public void configure( Ejb3Configuration configurator ) {
         }
 
-        @SuppressWarnings( "synthetic-access" )
         @Override
-        public RequestProcessor createRequestProcessor( String sourceName,
-                                                        ExecutionContext context,
-                                                        Observer observer,
-                                                        EntityManager entityManager,
-                                                        UUID rootNodeUuid,
-                                                        String nameOfDefaultWorkspace,
-                                                        String[] predefinedWorkspaceNames,
-                                                        long largeValueMinimumSizeInBytes,
-                                                        boolean createWorkspacesAllowed,
-                                                        boolean compressData,
-                                                        boolean enforceReferentialIntegrity ) {
-            return requestProcessor;
+        public RepositoryConnection createConnection( JpaSource source ) {
+            return connection;
         }
+
     }
 }

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModelTest.java	2009-11-27 16:52:16 UTC (rev 1365)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicModelTest.java	2009-11-27 17:22:54 UTC (rev 1366)
@@ -39,11 +39,12 @@
 import org.hibernate.ejb.Ejb3Configuration;
 import org.jboss.dna.common.util.SecureHash;
 import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.EntityManagers;
 import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.JpaSource;
 import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity;
-import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.RepositoryConnection;
 import org.jboss.dna.graph.property.PropertyType;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -62,7 +63,6 @@
     private EntityManagerFactory factory;
     private EntityManager manager;
     private BasicModel model;
-    private ExecutionContext context;
 
     @BeforeClass
     public static void beforeAll() throws Exception {
@@ -71,7 +71,6 @@
     @Before
     public void beforeEach() throws Exception {
         model = new BasicModel();
-        context = new ExecutionContext();
     }
 
     @After
@@ -122,22 +121,22 @@
     }
 
     @Test
-    public void shouldCreateRequestProcessor() {
+    public void shouldCreateConnection() {
         EntityManager manager = mock(EntityManager.class);
         EntityTransaction txn = mock(EntityTransaction.class);
+        EntityManagers managers = mock(EntityManagers.class);
+        JpaSource source = mock(JpaSource.class);
+        
         stub(manager.getTransaction()).toReturn(txn);
-        RequestProcessor proc = model.createRequestProcessor("test source",
-                                                             context,
-                                                             null,
-                                                             manager,
-                                                             UUID.randomUUID(),
-                                                             "default workspace",
-                                                             new String[] {"default workspace", "workspace1"},
-                                                             100,
-                                                             true,
-                                                             false,
-                                                             false);
-        assertThat(proc, is(notNullValue()));
+        
+        stub(managers.checkout()).toReturn(manager);
+
+        stub(source.getRootUuid()).toReturn(UUID.randomUUID());
+        stub(source.getEntityManagers()).toReturn(managers);
+
+        RepositoryConnection conn = model.createConnection(source);
+
+        assertThat(conn, is(notNullValue()));
     }
 
     @Test


From dna-commits at lists.jboss.org  Sat Nov 28 18:26:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sat, 28 Nov 2009 18:26:28 -0500
Subject: [dna-commits] DNA SVN: r1367 -
	trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa.
Message-ID: <200911282326.nASNQS2w017215@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-28 18:26:28 -0500 (Sat, 28 Nov 2009)
New Revision: 1367

Removed:
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
Log:
DNA-546  JCR Workspace and Session Imports Can Fail on JPA Connector  

Deleted file that had been moved.


Deleted: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java	2009-11-27 17:22:54 UTC (rev 1366)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java	2009-11-28 23:26:28 UTC (rev 1367)
@@ -1,172 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors. 
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- *
- * JBoss DNA 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.connector.store.jpa;
-
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import javax.persistence.EntityManager;
-import javax.transaction.xa.XAResource;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.cache.CachePolicy;
-import org.jboss.dna.graph.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.observe.Observer;
-import org.jboss.dna.graph.request.Request;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
-
-/**
- * The repository connection to JPA repository sources.
- */
-public class JpaConnection implements RepositoryConnection {
-
-    private final String name;
-    private final CachePolicy cachePolicy;
-    private final EntityManagers entityManagers;
-    private EntityManager entityManager;
-    private final Model model;
-    private final UUID rootNodeUuid;
-    private final String nameOfDefaultWorkspace;
-    private final String[] predefinedWorkspaceNames;
-    private final boolean creatingWorkspacesAllowed;
-    private final long largeValueMinimumSizeInBytes;
-    private final boolean compressData;
-    private final boolean enforceReferentialIntegrity;
-    private final Observer observer;
-
-    /*package*/JpaConnection( String sourceName,
-                               Observer observer,
-                               CachePolicy cachePolicy,
-                               EntityManagers entityManagers,
-                               Model model,
-                               UUID rootNodeUuid,
-                               String nameOfDefaultWorkspace,
-                               String[] predefinedWorkspaceNames,
-                               long largeValueMinimumSizeInBytes,
-                               boolean creatingWorkspacesAllowed,
-                               boolean compressData,
-                               boolean enforceReferentialIntegrity ) {
-        assert sourceName != null;
-        assert entityManagers != null;
-        assert model != null;
-        assert rootNodeUuid != null;
-        this.observer = observer;
-        this.name = sourceName;
-        this.cachePolicy = cachePolicy; // may be null
-        this.entityManagers = entityManagers;
-        this.entityManager = entityManagers.checkout();
-        assert this.entityManagers != null;
-        this.model = model;
-        this.rootNodeUuid = rootNodeUuid;
-        this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
-        this.compressData = compressData;
-        this.enforceReferentialIntegrity = enforceReferentialIntegrity;
-        this.nameOfDefaultWorkspace = nameOfDefaultWorkspace;
-        this.predefinedWorkspaceNames = predefinedWorkspaceNames != null ? predefinedWorkspaceNames : new String[] {};
-        this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
-     */
-    public String getSourceName() {
-        return name;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
-     */
-    public CachePolicy getDefaultCachePolicy() {
-        return cachePolicy;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
-     */
-    public XAResource getXAResource() {
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
-     */
-    public boolean ping( long time,
-                         TimeUnit unit ) {
-        return entityManager != null ? entityManager.isOpen() : false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.request.Request)
-     */
-    public void execute( ExecutionContext context,
-                         Request request ) throws RepositorySourceException {
-        long size = largeValueMinimumSizeInBytes;
-        if (entityManager == null) {
-            throw new RepositorySourceException(JpaConnectorI18n.connectionIsNoLongerOpen.text(name));
-        }
-        RequestProcessor proc = model.createRequestProcessor(name,
-                                                             context,
-                                                             observer,
-                                                             entityManager,
-                                                             rootNodeUuid,
-                                                             nameOfDefaultWorkspace,
-                                                             predefinedWorkspaceNames,
-                                                             size,
-                                                             creatingWorkspacesAllowed,
-                                                             compressData,
-                                                             enforceReferentialIntegrity);
-        try {
-            proc.process(request);
-        } finally {
-            proc.close();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
-     */
-    public void close() {
-        if (entityManager != null) {
-            // Do this only once ...
-            try {
-                entityManagers.checkin(entityManager);
-            } finally {
-                entityManager = null;
-            }
-        }
-    }
-
-}


From dna-commits at lists.jboss.org  Sun Nov 29 07:25:56 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sun, 29 Nov 2009 07:25:56 -0500
Subject: [dna-commits] DNA SVN: r1368 -
	trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa.
Message-ID: <200911291225.nATCPubm000687@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-11-29 07:25:56 -0500 (Sun, 29 Nov 2009)
New Revision: 1368

Modified:
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
Log:
DNA-546  JCR Workspace and Session Imports Can Fail on JPA Connector 

Committed test with dependency on some previous changes.  This should fix the nightly integration build.

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java	2009-11-28 23:26:28 UTC (rev 1367)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java	2009-11-29 12:25:56 UTC (rev 1368)
@@ -41,7 +41,7 @@
 public class JpaSourceTest {
 
     private JpaSource source;
-    private JpaConnection connection;
+    private RepositoryConnection connection;
 
     @Before
     public void beforeEach() throws Exception {
@@ -73,7 +73,7 @@
 
     @Test
     public void shouldCreateConnection() throws Exception {
-        connection = (JpaConnection)source.getConnection();
+        connection = source.getConnection();
         assertThat(connection, is(notNullValue()));
     }
 


From dna-commits at lists.jboss.org  Mon Nov 30 11:03:37 2009
From: dna-commits at lists.jboss.org (VIAGRA ® Pfizer Inc.)
Date: Mon, 30 Nov 2009 11:03:37 -0500
Subject: [dna-commits] Dear dna-commits@lists.jboss.org 78% OFF on Pfizer.
Message-ID: <200911301603.nAUG3b1s019744@lists01.dmz-a.mwc.hst.phx2.redhat.com>

An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/dna-commits/attachments/20091130/9f9c14f7/attachment.html 

From dna-commits at lists.jboss.org  Mon Nov 30 12:06:05 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 30 Nov 2009 12:06:05 -0500
Subject: [dna-commits] DNA SVN: r1369 - in trunk: dna-graph and 5 other
	directories.
Message-ID: <200911301706.nAUH65ai008451@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-30 12:06:05 -0500 (Mon, 30 Nov 2009)
New Revision: 1369

Modified:
   trunk/.gitignore
   trunk/dna-graph/pom.xml
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java
   trunk/dna-jcr/pom.xml
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
Log:
DNA-561 Google Collections API compatibility issues w/ Weld/AS 6

Applied the patch and successfully ran all of the tests.  Nicely done, John!

Also uploaded the Google Collections 1.0RC3 (jar,sources,javadoc) to the JBoss Maven Repository.

Modified: trunk/.gitignore
===================================================================
--- trunk/.gitignore	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/.gitignore	2009-11-30 17:06:05 UTC (rev 1369)
@@ -41,6 +41,8 @@
 /web/dna-web-jcr-rest-war/target/
 /web/dna-web-jcr-rest-client/target/
 
+/utils/dna-jpa-ddl-gen/target/
+
 /extensions/dna-classloader-maven/target
 /extensions/dna-common-jdbc/target
 /extensions/dna-connector-federation/target

Modified: trunk/dna-graph/pom.xml
===================================================================
--- trunk/dna-graph/pom.xml	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-graph/pom.xml	2009-11-30 17:06:05 UTC (rev 1369)
@@ -32,11 +32,11 @@
       <groupId>joda-time</groupId>
       <artifactId>joda-time</artifactId>
     </dependency>
-    <dependency>
-        <groupId>com.google.code.google-collections</groupId>
-        <artifactId>google-collect</artifactId>
-        <version>snapshot-20080530</version>
-    </dependency>
+	  <dependency>
+         <groupId>com.google.collections</groupId>
+         <artifactId>google-collections</artifactId>
+		 <version>1.0-rc3</version>
+      </dependency>
     <!-- 
     Testing (note the scope)
     -->

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	2009-11-30 17:06:05 UTC (rev 1369)
@@ -46,6 +46,7 @@
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 
@@ -80,7 +81,7 @@
         for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
             // Look for select nodes below an ACCESS node that have a single Comparison constraint,
             // and accumulate them keyed by the dynamic operand ...
-            Multimap<DynamicOperand, PlanNode> selectNodeByOperand = Multimaps.newArrayListMultimap();
+            Multimap<DynamicOperand, PlanNode> selectNodeByOperand = ArrayListMultimap.create();
             for (PlanNode select : access.findAllAtOrBelow(Type.SELECT)) {
                 Constraint constraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
                 // Look for Comparison constraints that use a range operator

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-30 17:06:05 UTC (rev 1369)
@@ -66,6 +66,7 @@
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.session.GraphSession.Authorizer.Action;
 import com.google.common.collect.ListMultimap;
+import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Multimaps;
 
 /**
@@ -86,7 +87,7 @@
 @NotThreadSafe
 public class GraphSession<Payload, PropertyPayload> {
 
-    protected final ListMultimap<Name, Node<Payload, PropertyPayload>> NO_CHILDREN = Multimaps.immutableMultimap();
+    protected final ListMultimap<Name, Node<Payload, PropertyPayload>> NO_CHILDREN = LinkedListMultimap.create();
     protected final Map<Name, PropertyInfo<PropertyPayload>> NO_PROPERTIES = Collections.emptyMap();
 
     protected final Authorizer authorizer;
@@ -1621,7 +1622,7 @@
             if (children.isEmpty()) {
                 childrenByName = cache.NO_CHILDREN;
             } else {
-                childrenByName = Multimaps.newLinkedListMultimap();
+                childrenByName = LinkedListMultimap.create();
                 for (Location location : children) {
                     NodeId id = cache.idFactory.create();
                     Name childName = location.getPath().getLastSegment().getName();
@@ -1699,7 +1700,7 @@
             Name childName = childPath.getLastSegment().getName();
             if (this.childrenByName.isEmpty()) {
                 // Just have to add the child ...
-                this.childrenByName = Multimaps.newLinkedListMultimap();
+                this.childrenByName = LinkedListMultimap.create();
                 if (childPath.getLastSegment().hasIndex()) {
                     // The child has a SNS index, but this is an only child ...
                     newChild = newChild.with(cache.pathFactory.create(childPath.getParent(), childName));
@@ -1710,7 +1711,7 @@
             }
 
             // Unfortunately, there is no efficient way to insert into the multi-map, so we need to recreate it ...
-            ListMultimap<Name, Node<Payload, PropertyPayload>> children = Multimaps.newLinkedListMultimap();
+            ListMultimap<Name, Node<Payload, PropertyPayload>> children = LinkedListMultimap.create();
             boolean added = false;
             for (Node<Payload, PropertyPayload> child : this.childrenByName.values()) {
                 if (!added && child.isNew()) {
@@ -1988,7 +1989,7 @@
             child.remove();
             // Now add the child ...
             if (parent.childrenByName == cache.NO_CHILDREN) {
-                parent.childrenByName = Multimaps.newLinkedListMultimap();
+                parent.childrenByName = LinkedListMultimap.create();
             }
             parent.childrenByName.put(newNodeName, child);
             child.parent = parent;
@@ -2035,7 +2036,7 @@
 
             parent.load();
             if (parent.childrenByName == cache.NO_CHILDREN) {
-                parent.childrenByName = Multimaps.newLinkedListMultimap();
+                parent.childrenByName = LinkedListMultimap.create();
             }
 
             cache.nodeOperations.preCopy(this, parent);
@@ -2101,7 +2102,7 @@
             }
 
             // Unfortunately, there is no efficient way to insert into the multi-map, so we need to recreate it ...
-            ListMultimap<Name, Node<Payload, PropertyPayload>> children = Multimaps.newLinkedListMultimap();
+            ListMultimap<Name, Node<Payload, PropertyPayload>> children = LinkedListMultimap.create();
             for (Node<Payload, PropertyPayload> child : childrenByName.values()) {
                 if (child == nodeToBeMoved) continue;
                 if (before != null && child.getSegment().equals(before)) {
@@ -2388,7 +2389,7 @@
             Node<Payload, PropertyPayload> child = cache.createNode(this, cache.idFactory.create(), newChild);
             child.markAsNew(); // marks parent as changed
             if (childrenByName == cache.NO_CHILDREN) {
-                childrenByName = Multimaps.newLinkedListMultimap();
+                childrenByName = LinkedListMultimap.create();
             }
             childrenByName.put(name, child);
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java	2009-11-30 17:06:05 UTC (rev 1369)
@@ -513,7 +513,7 @@
             this.pathToParent = pathToParent;
             this.name = name;
             this.state = ElementEntryState.TBD;
-            properties = new LinkedHashMultimap<Name, Object>();
+            properties = LinkedHashMultimap.create();
         }
 
         protected void setName( Name name ) {

Modified: trunk/dna-jcr/pom.xml
===================================================================
--- trunk/dna-jcr/pom.xml	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-jcr/pom.xml	2009-11-30 17:06:05 UTC (rev 1369)
@@ -102,11 +102,11 @@
       <groupId>org.apache.jackrabbit</groupId>
       <artifactId>jackrabbit-jcr-tests</artifactId>
     </dependency>
-    <dependency>
-      <groupId>com.google.code.google-collections</groupId>
-      <artifactId>google-collect</artifactId>
-      <version>snapshot-20080530</version>
-    </dependency>
+	  <dependency>
+         <groupId>com.google.collections</groupId>
+         <artifactId>google-collections</artifactId>
+		 <version>1.0-rc3</version>
+      </dependency>
 
 	<dependency>
 		<groupId>org.jboss.security</groupId>

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	2009-11-29 12:25:56 UTC (rev 1368)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	2009-11-30 17:06:05 UTC (rev 1369)
@@ -53,7 +53,7 @@
      * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
      * </p>
      */
-    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowSns = new LinkedListMultimap<Name, JcrNodeDefinition>();
+    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowSns = LinkedListMultimap.create();
     /**
      * A local cache of all defined and inherited child node definitions, keyed by their name, that do <i>not</i> allow
      * same-name-sibilings. This includes residual child node definitions, which are keyed by the
@@ -67,7 +67,7 @@
      * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
      * </p>
      */
-    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowNoSns = new LinkedListMultimap<Name, JcrNodeDefinition>();
+    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowNoSns = LinkedListMultimap.create();
 
     /**
      * A local cache of all defined and inherited property definitions, keyed by their name, that allow multiple values. This
@@ -80,7 +80,7 @@
      * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
      * </p>
      */
-    private final Multimap<Name, JcrPropertyDefinition> multiValuedPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+    private final Multimap<Name, JcrPropertyDefinition> multiValuedPropertyDefinitions = LinkedListMultimap.create();
     /**
      * A local cache of all defined and inherited property definitions, keyed by their name, that allow single values. This
      * includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
@@ -92,10 +92,10 @@
      * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
      * </p>
      */
-    private final Multimap<Name, JcrPropertyDefinition> singleValuedPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+    private final Multimap<Name, JcrPropertyDefinition> singleValuedPropertyDefinitions = LinkedListMultimap.create();
 
-    private final Multimap<Name, JcrNodeDefinition> allChildNodeDefinitions = new LinkedListMultimap<Name, JcrNodeDefinition>();
-    private final Multimap<Name, JcrPropertyDefinition> allPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+    private final Multimap<Name, JcrNodeDefinition> allChildNodeDefinitions = LinkedListMultimap.create();
+    private final Multimap<Name, JcrPropertyDefinition> allPropertyDefinitions = LinkedListMultimap.create();
 
     DefinitionCache( JcrNodeType nodeType ) {
         addDefinitionsForTypeAndAllSupertypes(nodeType);


From dna-commits at lists.jboss.org  Mon Nov 30 14:40:58 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 30 Nov 2009 14:40:58 -0500
Subject: [dna-commits] DNA SVN: r1370 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <200911301940.nAUJewJ1004412@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-30 14:40:58 -0500 (Mon, 30 Nov 2009)
New Revision: 1370

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
Log:
DNA-548 Changed JcrWorkspace, JcrSession, and WorkspaceLockManager so that they use ExecutionContext more consistently. JcrWorkspace and JcrSession both share the exact same ExecutionContext instance, while WorkspaceLockManager was changed to expect the JcrSession in most of its methods so the lock manager can use the JcrSession's ExecutionContext in its operations.  (The latter also has the advantage of recording which sessions were performing the lock operations, since now the session's context is used for all lock operations.)

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -127,7 +127,7 @@
      * @return the context identifier; never null
      */
     public String getContextId() {
-        return processId;
+        return contextId;
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -1336,7 +1336,7 @@
      * @see javax.jcr.Node#holdsLock()
      */
     public final boolean holdsLock() /*throws RepositoryException*/{
-        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(this.location);
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(session(), this.location);
 
         return lock != null && cache.session().lockTokens().contains(lock.getLockToken());
     }
@@ -1368,9 +1368,9 @@
 
             while (!nodesToVisit.isEmpty()) {
                 Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.remove(nodesToVisit.size() - 1);
-                if (session().workspace().lockManager().lockFor(node.getLocation()) != null) throw new LockException(
-                                                                                                                     JcrI18n.parentAlreadyLocked.text(this.location,
-                                                                                                                                                      node.getLocation()));
+                if (session().workspace().lockManager().lockFor(session(), node.getLocation()) != null) throw new LockException(
+                                                                                                                                JcrI18n.parentAlreadyLocked.text(this.location,
+                                                                                                                                                                 node.getLocation()));
 
                 for (Node<JcrNodePayload, JcrPropertyPayload> child : node.getChildren()) {
                     nodesToVisit.add(child);
@@ -1378,9 +1378,8 @@
             }
         }
 
-        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lock(cache,
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lock(session(),
                                                                                      this.location,
-                                                                                     session().getUserID(),
                                                                                      isDeep,
                                                                                      isSessionScoped);
 
@@ -1394,7 +1393,7 @@
      * @see javax.jcr.Node#unlock()
      */
     public final void unlock() throws LockException, RepositoryException {
-        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(this.location);
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(session(), this.location);
 
         if (lock == null) {
             throw new LockException(JcrI18n.notLocked.text(this.location));
@@ -1404,7 +1403,7 @@
             throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
         }
 
-        session().workspace().lockManager().unlock(lock);
+        session().workspace().lockManager().unlock(session(), lock);
         session().removeLockToken(lock.getLockToken());
     }
 
@@ -1413,14 +1412,14 @@
         if (session() == null || session().workspace() == null) return null;
 
         WorkspaceLockManager lockManager = session().workspace().lockManager();
-        WorkspaceLockManager.DnaLock lock = lockManager.lockFor(this.location);
+        WorkspaceLockManager.DnaLock lock = lockManager.lockFor(session(), this.location);
         if (lock != null) return lock;
 
         AbstractJcrNode parent = this;
         while (!parent.isRoot()) {
             parent = parent.getParent();
 
-            WorkspaceLockManager.DnaLock parentLock = lockManager.lockFor(parent.location);
+            WorkspaceLockManager.DnaLock parentLock = lockManager.lockFor(session(), parent.location);
             if (parentLock != null && parentLock.isLive()) {
                 return parentLock.isDeep() ? parentLock : null;
             }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -410,7 +410,7 @@
         this.federatedSource.initialize(new FederatedRepositoryContext(this.connectionFactory));
 
         this.lockManagers = new ConcurrentHashMap<String, WorkspaceLockManager>();
-        this.locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);    
+        this.locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
     }
 
     protected void initializeSystemContent( Graph systemGraph ) {
@@ -430,12 +430,12 @@
         return graph;
     }
 
-    Graph createSystemGraph() {
+    Graph createSystemGraph( ExecutionContext sessionContext ) {
         assert this.systemSourceName != null;
         assert this.connectionFactory != null;
-        assert this.executionContext != null;
+        assert sessionContext != null;
         // The default workspace should be the system workspace ...
-        Graph result = Graph.create(this.systemSourceName, this.connectionFactory, this.executionContext);
+        Graph result = Graph.create(this.systemSourceName, this.connectionFactory, sessionContext);
         if (this.systemWorkspaceName != null) {
             result.useWorkspace(systemWorkspaceName);
         }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -65,7 +65,6 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.graph.session.GraphSession;
 import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
 import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
@@ -143,21 +142,21 @@
 
     JcrSession( JcrRepository repository,
                 JcrWorkspace workspace,
-                ExecutionContext workspaceContext,
+                ExecutionContext sessionContext,
+                NamespaceRegistry globalNamespaceRegistry,
                 Map<String, Object> sessionAttributes ) {
         assert repository != null;
         assert workspace != null;
         assert sessionAttributes != null;
-        assert workspaceContext != null;
+        assert sessionContext != null;
         this.repository = repository;
         this.sessionAttributes = sessionAttributes;
         this.workspace = workspace;
 
         // Create an execution context for this session, which should use the local namespace registry ...
-        NamespaceRegistry workspaceRegistry = workspaceContext.getNamespaceRegistry();
-        NamespaceRegistry local = new LocalNamespaceRegistry(workspaceRegistry);
-        this.executionContext = workspaceContext.with(local);
-        this.sessionRegistry = new JcrNamespaceRegistry(Behavior.JSR170_SESSION, local, workspaceRegistry, this);
+        this.executionContext = sessionContext;
+        NamespaceRegistry local = sessionContext.getNamespaceRegistry();
+        this.sessionRegistry = new JcrNamespaceRegistry(Behavior.JSR170_SESSION, local, globalNamespaceRegistry, this);
         this.rootPath = this.executionContext.getValueFactories().getPathFactory().createRootPath();
 
         // Set up the graph to use for this session (which uses the session's namespace registry and context) ...
@@ -311,11 +310,11 @@
             return;
         }
 
-        if (workspace().lockManager().isHeldBySession(lt)) {
+        if (workspace().lockManager().isHeldBySession(this, lt)) {
             throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lt));
         }
 
-        workspace().lockManager().setHeldBySession(lt, true);
+        workspace().lockManager().setHeldBySession(this, lt, true);
         lockTokens.add(lt);
     }
 
@@ -776,7 +775,7 @@
             return;
         }
 
-        this.workspace().lockManager().cleanLocks(lockTokens);
+        this.workspace().lockManager().cleanLocks(this);
         this.executionContext.getSecurityContext().logout();
         isLive = false;
     }
@@ -858,7 +857,7 @@
             throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lt));
         }
 
-        workspace().lockManager().setHeldBySession(lt, false);
+        workspace().lockManager().setHeldBySession(this, lt, false);
         lockTokens.remove(lt);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -34,7 +34,6 @@
 import javax.jcr.InvalidSerializedDataException;
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
-import javax.jcr.NamespaceRegistry;
 import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
@@ -61,10 +60,12 @@
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.UuidAlreadyExistsException;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
 import org.jboss.dna.graph.request.ReadBranchRequest;
 import org.jboss.dna.graph.session.GraphSession;
@@ -132,7 +133,7 @@
     private final JcrQueryManager queryManager;
 
     private final WorkspaceLockManager lockManager;
-    
+
     /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
@@ -151,34 +152,16 @@
         this.repository = repository;
         this.lockManager = repository.getLockManager(workspaceName);
 
-        // // Set up the execution context for this workspace, which should use the namespace registry that persists
-        // // the namespaces in the graph ...
-        // Graph namespaceGraph = Graph.create(this.repository.getRepositorySourceName(),
-        // this.repository.getConnectionFactory(),
-        // context);
-        // namespaceGraph.useWorkspace(workspaceName);
-        //
-        // // Make sure the "/jcr:system" node exists ...
-        // PathFactory pathFactory = context.getValueFactories().getPathFactory();
-        // Path root = pathFactory.createRootPath();
-        // Path systemPath = pathFactory.create(root, JcrLexicon.SYSTEM);
-        // Property systemPrimaryType = context.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.SYSTEM);
-        // namespaceGraph.create(systemPath, systemPrimaryType).ifAbsent().and();
-        //
-        // Name uriProperty = DnaLexicon.NAMESPACE_URI;
-        // Path namespacesPath = pathFactory.create(systemPath, DnaLexicon.NAMESPACES);
-        // PropertyFactory propertyFactory = context.getPropertyFactory();
-        // Property namespaceType = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.NAMESPACE);
-        // org.jboss.dna.graph.property.NamespaceRegistry persistentRegistry = new GraphNamespaceRegistry(namespaceGraph,
-        // namespacesPath,
-        // uriProperty, namespaceType);
-        this.context = context;
+        // Create an execution context for this session, which should use the local namespace registry ...
+        NamespaceRegistry globalRegistry = context.getNamespaceRegistry();
+        NamespaceRegistry local = new LocalNamespaceRegistry(globalRegistry);
+        this.context = context.with(local);
 
         // Now create a graph for the session ...
         this.graph = this.repository.createWorkspaceGraph(workspaceName);
 
         // Set up the session for this workspace ...
-        this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
+        this.session = new JcrSession(this.repository, this, this.context, globalRegistry, sessionAttributes);
 
         // This must be initialized after the session
         this.nodeTypeManager = new JcrNodeTypeManager(session, this.repository.getRepositoryTypeManager());
@@ -213,7 +196,7 @@
     final WorkspaceLockManager lockManager() {
         return this.lockManager;
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -233,7 +216,7 @@
      * 
      * @see javax.jcr.Workspace#getNamespaceRegistry()
      */
-    public final NamespaceRegistry getNamespaceRegistry() {
+    public final javax.jcr.NamespaceRegistry getNamespaceRegistry() {
         return workspaceRegistry;
     }
 
@@ -325,7 +308,6 @@
             // This also performs the check permission for reading the parent ...
             Name newNodeName = destPath.getLastSegment().getName();
             SessionCache cache = this.session.cache();
-            
 
             /*
              * Find the UUID for the source node.  Have to go directly against the graph.
@@ -336,7 +318,7 @@
             if (uuidProp != null) {
                 UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
 
-                DnaLock sourceLock = lockManager().lockFor(Location.create(sourceUuid));
+                DnaLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid));
                 if (sourceLock != null && sourceLock.getLockToken() == null) {
                     throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath));
                 }
@@ -350,7 +332,7 @@
                     throw new LockException(destAbsPath);
                 }
             }
-            
+
             Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
             cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
 
@@ -481,7 +463,6 @@
             // This also performs the check permission for reading the parent ...
             Name newNodeName = destPath.getLastSegment().getName();
             SessionCache cache = this.session.cache();
-            
 
             /*
              * Find the UUID for the source node.  Have to go directly against the graph.
@@ -492,7 +473,7 @@
             if (uuidProp != null) {
                 UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
 
-                DnaLock sourceLock = lockManager().lockFor(Location.create(sourceUuid));
+                DnaLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid));
                 if (sourceLock != null && sourceLock.getLockToken() == null) {
                     throw new LockException(srcAbsPath);
                 }
@@ -506,7 +487,7 @@
                     throw new LockException(destAbsPath);
                 }
             }
-            
+
             Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
             cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
 
@@ -633,7 +614,7 @@
                 if (newParentLock != null && newParentLock.getLockToken() == null) {
                     throw new LockException(destAbsPath);
                 }
-            }            
+            }
 
             // Now perform the clone, using the direct (non-session) method ...
             cache.graphSession().immediateMove(srcPath, destPath);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -31,7 +31,6 @@
 @ThreadSafe
 class WorkspaceLockManager {
 
-    private final ExecutionContext context;
     private final Path locksPath;
     private final JcrRepository repository;
     private final String workspaceName;
@@ -41,7 +40,6 @@
                           JcrRepository repository,
                           String workspaceName,
                           Path locksPath ) {
-        this.context = context;
         this.repository = repository;
         this.workspaceName = workspaceName;
         this.locksPath = locksPath;
@@ -49,7 +47,7 @@
         this.workspaceLocksByNodeUuid = new ConcurrentHashMap<UUID, DnaLock>();
 
         Property locksPrimaryType = context.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.LOCKS);
-        repository.createSystemGraph().create(locksPath, locksPrimaryType).ifAbsent().and();
+        repository.createSystemGraph(context).create(locksPath, locksPrimaryType).ifAbsent().and();
     }
 
     /**
@@ -60,34 +58,34 @@
      * The location given in {@code nodeLocation} must have a UUID.
      * </p>
      * 
-     * @param cache the session cache from which the node was loaded
+     * @param session the session in which the node is being locked and that loaded the node
      * @param nodeLocation the location for the node; may not be null and must have a UUID
-     * @param lockOwner the owner of the new lock
      * @param isDeep whether the node's descendants in the content graph should also be locked
      * @param isSessionScoped whether the lock should outlive the session in which it was created
      * @return an object representing the newly created lock
      * @throws RepositoryException if an error occurs updating the graph state
      */
-    DnaLock lock( SessionCache cache,
+    DnaLock lock( JcrSession session,
                   Location nodeLocation,
-                  String lockOwner,
                   boolean isDeep,
                   boolean isSessionScoped ) throws RepositoryException {
         assert nodeLocation != null;
 
         UUID lockUuid = UUID.randomUUID();
-        UUID nodeUuid = uuidFor(nodeLocation);
+        UUID nodeUuid = uuidFor(session, nodeLocation);
 
         if (nodeUuid == null) {
             throw new RepositoryException(JcrI18n.uuidRequiredForLock.text(nodeLocation));
         }
 
+        ExecutionContext sessionContext = session.getExecutionContext();
+        String lockOwner = sessionContext.getSecurityContext().getUserName();
         DnaLock lock = createLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
 
-        Graph.Batch batch = repository.createSystemGraph().batch();
+        Graph.Batch batch = repository.createSystemGraph(sessionContext).batch();
 
-        PropertyFactory propFactory = context.getPropertyFactory();
-        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+        PropertyFactory propFactory = sessionContext.getPropertyFactory();
+        PathFactory pathFactory = sessionContext.getValueFactories().getPathFactory();
         Property lockOwnerProp = propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner);
         Property lockIsDeepProp = propFactory.create(JcrLexicon.LOCK_IS_DEEP, isDeep);
 
@@ -102,6 +100,7 @@
                      lockIsDeepProp).ifAbsent().and();
         batch.execute();
 
+        SessionCache cache = session.cache();
         AbstractJcrNode lockedNode = cache.findJcrNode(Location.create(nodeUuid));
         NodeEditor editor = cache.getEditorFor(lockedNode.nodeInfo());
 
@@ -111,7 +110,7 @@
                            false);
         editor.setProperty(JcrLexicon.LOCK_IS_DEEP, (JcrValue)cache.session().getValueFactory().createValue(isDeep), false);
 
-        lockNodeInRepository(nodeUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
+        lockNodeInRepository(session, nodeUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
         workspaceLocksByNodeUuid.put(nodeUuid, lock);
 
         return lock;
@@ -133,13 +132,14 @@
      * <p>
      * This method will also attempt to {@link Graph#lock(Location) lock the node in the underlying repository}. If the underlying
      * repository supports locks and {@link LockFailedException the lock attempt fails}, this method will cancel the lock attempt
-     * by calling {@link #unlock(DnaLock)} and will throw a {@code RepositoryException}.
+     * by calling {@link #unlock(JcrSession,DnaLock)} and will throw a {@code RepositoryException}.
      * </p>
      * <p>
      * This method does not modify the system graph. In other words, it will not create the record for the lock in the {@code
      * /jcr:system/dna:locks} subgraph.
      * </p>
      * 
+     * @param session the session in which the node is being locked and that loaded the node
      * @param nodeUuid the UUID of the node to lock
      * @param lockOwnerProp an existing property with name {@link JcrLexicon#LOCK_OWNER} and the value being the name of the lock
      *        owner
@@ -153,7 +153,8 @@
      * @throws RepositoryException if the repository in which the node represented by {@code nodeUuid} supports locking but
      *         signals that the lock for the node cannot be acquired
      */
-    void lockNodeInRepository( UUID nodeUuid,
+    void lockNodeInRepository( JcrSession session,
+                               UUID nodeUuid,
                                Property lockOwnerProp,
                                Property lockIsDeepProp,
                                DnaLock lock,
@@ -170,7 +171,7 @@
             workspaceBatch.execute();
         } catch (LockFailedException lfe) {
             // Attempt to lock node at the repo level failed - cancel lock
-            unlock(lock);
+            unlock(session, lock);
             throw new RepositoryException(lfe);
         }
 
@@ -179,13 +180,16 @@
     /**
      * Removes the provided lock, effectively unlocking the node to which the lock is associated.
      * 
+     * @param session the session in which the node is being unlocked
      * @param lock the lock to be removed
      */
-    void unlock( DnaLock lock ) {
+    void unlock( JcrSession session,
+                 DnaLock lock ) {
         try {
+            ExecutionContext context = session.getExecutionContext();
             PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-            Graph.Batch batch = repository.createSystemGraph().batch();
+            Graph.Batch batch = repository.createSystemGraph(context).batch();
 
             batch.delete(pathFactory.create(locksPath, pathFactory.createSegment(lock.getUuid().toString())));
             batch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
@@ -232,17 +236,21 @@
      * Checks whether the given lock token is currently held by any session by querying the lock record in the underlying
      * repository.
      * 
+     * @param session the session on behalf of which the lock query is being performed
      * @param lockToken the lock token to check; may not be null
      * @return true if a session currently holds the lock token, false otherwise
      */
-    boolean isHeldBySession( String lockToken ) {
+    boolean isHeldBySession( JcrSession session,
+                             String lockToken ) {
         assert lockToken != null;
 
+        ExecutionContext context = session.getExecutionContext();
         ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
-                                                                                                        pathFactory.createSegment(lockToken)));
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph(context)
+                                                      .getNodeAt(pathFactory.create(locksPath,
+                                                                                    pathFactory.createSegment(lockToken)));
 
         return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
 
@@ -253,18 +261,22 @@
      * lock as being held (or not held) by some {@link Session}. Note that this method does not identify <i>which</i> (if any)
      * session holds the token for the lock, just that <i>some</i> session holds the token for the lock.
      * 
+     * @param session the session on behalf of which the lock operation is being performed
      * @param lockToken the lock token for which the "held" status should be modified; may not be null
      * @param value the new value
      */
-    void setHeldBySession( String lockToken,
+    void setHeldBySession( JcrSession session,
+                           String lockToken,
                            boolean value ) {
         assert lockToken != null;
 
+        ExecutionContext context = session.getExecutionContext();
         PropertyFactory propFactory = context.getPropertyFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        repository.createSystemGraph().set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath,
-                                                                                                                           pathFactory.createSegment(lockToken)));
+        repository.createSystemGraph(context)
+                  .set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value))
+                  .on(pathFactory.create(locksPath, pathFactory.createSegment(lockToken)));
     }
 
     /**
@@ -288,11 +300,13 @@
     /**
      * Returns the lock that corresponds to the given UUID
      * 
+     * @param session the session on behalf of which the lock operation is being performed
      * @param nodeLocation the node UUID
      * @return the corresponding lock, possibly null if there is no such lock
      */
-    DnaLock lockFor( Location nodeLocation ) {
-        UUID nodeUuid = uuidFor(nodeLocation);
+    DnaLock lockFor( JcrSession session,
+                     Location nodeLocation ) {
+        UUID nodeUuid = uuidFor(session, nodeLocation);
         if (nodeUuid == null) return null;
         return workspaceLocksByNodeUuid.get(nodeUuid);
     }
@@ -302,10 +316,12 @@
      * returns the {@link Location#getUuid() default UUID} if it exists. If it does not, the method returns the value of the
      * {@link JcrLexicon#UUID} property as a UUID. If the location does not contain that property, the method returns null.
      * 
+     * @param session the session on behalf of which the lock operation is being performed
      * @param location the location for which the UUID should be returned
      * @return the UUID that identifies the given location or {@code null} if the location does not have a UUID.
      */
-    UUID uuidFor( Location location ) {
+    UUID uuidFor( JcrSession session,
+                  Location location ) {
         assert location != null;
 
         if (location.getUuid() != null) return location.getUuid();
@@ -313,26 +329,28 @@
         org.jboss.dna.graph.property.Property uuidProp = location.getIdProperty(JcrLexicon.UUID);
         if (uuidProp == null) return null;
 
+        ExecutionContext context = session.getExecutionContext();
         return context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
     }
 
     /**
      * Unlocks all locks corresponding to the tokens in the {@code lockTokens} collection that are session scoped.
      * 
-     * @param lockTokens the collection of lock tokens
+     * @param session the session on behalf of which the lock operation is being performed
      */
-    void cleanLocks( Collection<String> lockTokens ) {
+    void cleanLocks( JcrSession session ) {
+        Collection<String> lockTokens = session.lockTokens();
         for (String lockToken : lockTokens) {
             DnaLock lock = lockFor(lockToken);
             if (lock != null && lock.isSessionScoped()) {
-                unlock(lock);
+                unlock(session, lock);
             }
         }
     }
 
     /**
      * Internal representation of a locked node. This class should only be created through calls to
-     * {@link WorkspaceLockManager#lock(SessionCache, Location, String, boolean, boolean)}.
+     * {@link WorkspaceLockManager#lock(JcrSession, Location, boolean, boolean)}.
      */
     @ThreadSafe
     public class DnaLock {

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -142,7 +142,7 @@
                 return graph;
             }
         });
-        stub(repository.createSystemGraph()).toAnswer(new Answer<Graph>() {
+        stub(repository.createSystemGraph(context)).toAnswer(new Answer<Graph>() {
             public Graph answer( InvocationOnMock invocation ) throws Throwable {
                 return graph;
             }
@@ -164,7 +164,7 @@
         // Set up the session attributes ...
         sessionAttributes = new HashMap<String, Object>();
         sessionAttributes.put("attribute1", "value1");
-        
+
         // Now create the workspace ...
         SecurityContext mockSecurityContext = new MockSecurityContext(null,
                                                                       Collections.singleton(JcrSession.DNA_WRITE_PERMISSION));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -119,7 +119,7 @@
         sourceGraph = Graph.create(source, context);
 
         // Set up the graph that goes directly to the system source ...
-        systemGraph = repository.createSystemGraph();
+        systemGraph = repository.createSystemGraph(context);
     }
 
     @After

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -56,7 +56,9 @@
 import javax.jcr.nodetype.NodeType;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JaasSecurityContext;
+import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.junit.After;
 import org.junit.Before;
@@ -203,8 +205,9 @@
         Subject subject = new Subject(false, Collections.singleton(principal), Collections.EMPTY_SET, Collections.EMPTY_SET);
         LoginContext loginContext = mock(LoginContext.class);
         stub(loginContext.getSubject()).toReturn(subject);
-        Session session = new JcrSession(repository, workspace, context.with(new JaasSecurityContext(loginContext)),
-                                         sessionAttributes);
+        NamespaceRegistry globalRegistry = context.getNamespaceRegistry();
+        ExecutionContext sessionContext = context.with(new JaasSecurityContext(loginContext));
+        Session session = new JcrSession(repository, workspace, sessionContext, globalRegistry, sessionAttributes);
         try {
             assertThat(session.getUserID(), is("name"));
         } finally {

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -117,6 +117,11 @@
     }
 
     @Test
+    public void shouldHaveSameContextIdAsSession() {
+        assertThat(workspace.context().getId(), is(session.getExecutionContext().getId()));
+    }
+
+    @Test
     public void shouldProvideNamespaceRegistry() throws Exception {
         NamespaceRegistry registry = workspace.getNamespaceRegistry();
         assertThat(registry, is(notNullValue()));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-30 17:06:05 UTC (rev 1369)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-30 19:40:58 UTC (rev 1370)
@@ -4,6 +4,7 @@
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.util.LinkedList;
 import java.util.UUID;
@@ -75,7 +76,7 @@
                 return graph;
             }
         });
-        stub(repository.createSystemGraph()).toAnswer(new Answer<Graph>() {
+        stub(repository.createSystemGraph(context)).toAnswer(new Answer<Graph>() {
             public Graph answer( InvocationOnMock invocation ) throws Throwable {
                 return graph;
             }
@@ -139,7 +140,9 @@
         Property lockOwnerProp = propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner);
         Property lockIsDeepProp = propFactory.create(JcrLexicon.LOCK_IS_DEEP, isDeep);
 
-        workspaceLockManager.lockNodeInRepository(validUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
+        JcrSession session = mock(JcrSession.class);
+        stub(session.getExecutionContext()).toReturn(context);
+        workspaceLockManager.lockNodeInRepository(session, validUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
 
         assertNextRequestIsLock(validLocation, LockScope.SELF_ONLY, 0);
     }


From dna-commits at lists.jboss.org  Mon Nov 30 15:08:49 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 30 Nov 2009 15:08:49 -0500
Subject: [dna-commits] DNA SVN: r1371 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <200911302008.nAUK8nie010528@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-30 15:08:48 -0500 (Mon, 30 Nov 2009)
New Revision: 1371

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
Log:
Merge branch 'dna-548'

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-11-30 19:40:58 UTC (rev 1370)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-11-30 20:08:48 UTC (rev 1371)
@@ -424,8 +424,9 @@
         // Right now, the other nodes will be created as needed
     }
 
-    Graph createWorkspaceGraph( String workspaceName ) {
-        Graph graph = Graph.create(this.federatedSource, this.executionContext);
+    Graph createWorkspaceGraph( String workspaceName,
+                                ExecutionContext workspaceContext ) {
+        Graph graph = Graph.create(this.federatedSource, workspaceContext);
         graph.useWorkspace(workspaceName);
         return graph;
     }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-11-30 19:40:58 UTC (rev 1370)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-11-30 20:08:48 UTC (rev 1371)
@@ -102,7 +102,7 @@
 
     /**
      * The reference to the {@link JcrRepository} instance that owns this {@link Workspace} instance. Very few methods on this
-     * repository object are used; mainly {@link JcrRepository#createWorkspaceGraph(String)},
+     * repository object are used; mainly {@link JcrRepository#createWorkspaceGraph(String,ExecutionContext)},
      * {@link JcrRepository#getPersistentRegistry()} and {@link JcrRepository#getRepositorySourceName()}.
      */
     private final JcrRepository repository;
@@ -158,7 +158,7 @@
         this.context = context.with(local);
 
         // Now create a graph for the session ...
-        this.graph = this.repository.createWorkspaceGraph(workspaceName);
+        this.graph = this.repository.createWorkspaceGraph(this.name, this.context);
 
         // Set up the session for this workspace ...
         this.session = new JcrSession(this.repository, this, this.context, globalRegistry, sessionAttributes);
@@ -312,7 +312,7 @@
             /*
              * Find the UUID for the source node.  Have to go directly against the graph.
              */
-            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace).getNodeAt(srcPath);
+            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace, context).getNodeAt(srcPath);
             Property uuidProp = sourceNode.getProperty(DnaLexicon.UUID);
 
             if (uuidProp != null) {
@@ -467,7 +467,7 @@
             /*
              * Find the UUID for the source node.  Have to go directly against the graph.
              */
-            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace).getNodeAt(srcPath);
+            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace, context).getNodeAt(srcPath);
             Property uuidProp = sourceNode.getProperty(DnaLexicon.UUID);
 
             if (uuidProp != null) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-30 19:40:58 UTC (rev 1370)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-11-30 20:08:48 UTC (rev 1371)
@@ -160,7 +160,7 @@
                                DnaLock lock,
                                boolean isDeep ) throws RepositoryException {
         // Write them directly to the underlying graph
-        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(workspaceName).batch();
+        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(workspaceName, session.getExecutionContext()).batch();
         workspaceBatch.set(lockOwnerProp, lockIsDeepProp).on(nodeUuid);
         if (isDeep) {
             workspaceBatch.lock(nodeUuid).andItsDescendants().withDefaultTimeout();
@@ -195,7 +195,7 @@
             batch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
             batch.execute();
 
-            unlockNodeInRepository(lock);
+            unlockNodeInRepository(session, lock);
 
             workspaceLocksByNodeUuid.remove(lock.nodeUuid);
         } catch (PathNotFoundException pnfe) {
@@ -221,10 +221,12 @@
      * /jcr:system/dna:locks} subgraph.
      * </p>
      * 
+     * @param session the session in which the node is being unlocked
      * @param lock
      */
-    void unlockNodeInRepository( DnaLock lock ) {
-        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(this.workspaceName).batch();
+    void unlockNodeInRepository( JcrSession session,
+                                 DnaLock lock ) {
+        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(this.workspaceName, session.getExecutionContext()).batch();
 
         workspaceBatch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
         workspaceBatch.unlock(lock.nodeUuid);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-11-30 19:40:58 UTC (rev 1370)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-11-30 20:08:48 UTC (rev 1371)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.stub;
 import java.io.IOException;
@@ -137,7 +138,7 @@
         stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
         stub(repository.getPersistentRegistry()).toReturn(context.getNamespaceRegistry());
-        stub(repository.createWorkspaceGraph(anyString())).toAnswer(new Answer<Graph>() {
+        stub(repository.createWorkspaceGraph(anyString(), (ExecutionContext)anyObject())).toAnswer(new Answer<Graph>() {
             public Graph answer( InvocationOnMock invocation ) throws Throwable {
                 return graph;
             }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-30 19:40:58 UTC (rev 1370)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-11-30 20:08:48 UTC (rev 1371)
@@ -3,6 +3,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
@@ -71,7 +72,7 @@
         stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
         stub(repository.getRepositorySourceName()).toReturn(sourceName);
         stub(repository.getPersistentRegistry()).toReturn(context.getNamespaceRegistry());
-        stub(repository.createWorkspaceGraph(anyString())).toAnswer(new Answer<Graph>() {
+        stub(repository.createWorkspaceGraph(anyString(), (ExecutionContext)anyObject())).toAnswer(new Answer<Graph>() {
             public Graph answer( InvocationOnMock invocation ) throws Throwable {
                 return graph;
             }
@@ -150,7 +151,9 @@
     @Test
     public void shouldCreateLockRequestWhenUnlockingNode() {
         DnaLock lock = workspaceLockManager.createLock("testOwner", UUID.randomUUID(), validUuid, false, false);
-        workspaceLockManager.unlockNodeInRepository(lock);
+        JcrSession session = mock(JcrSession.class);
+        stub(session.getExecutionContext()).toReturn(context);
+        workspaceLockManager.unlockNodeInRepository(session, lock);
 
         assertNextRequestIsUnlock(validLocation);
     }


From dna-commits at lists.jboss.org  Mon Nov 30 16:53:48 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 30 Nov 2009 16:53:48 -0500
Subject: [dna-commits] DNA SVN: r1372 - in
	trunk/dna-graph/src/main/java/org/jboss/dna/graph: request
	and 1 other directories.
Message-ID: <200911302153.nAULrm9Q031457@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-11-30 16:53:47 -0500 (Mon, 30 Nov 2009)
New Revision: 1372

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
Log:
DNA-551 Fixed a couple of calls to 'Location.isSame()' that should have been fixed earlier.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteAsRangeCriteria.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -48,7 +48,6 @@
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
 
 /**
  * An {@link OptimizerRule optimizer rule} that rewrites two {@link And AND-ed} {@link Constraint}s that constraint a dynamic

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -167,10 +167,9 @@
      * 
      * @param fromLocation the actual location of the node being cloned
      * @param intoLocation the actual location of the new clone of the node
-     * @throws IllegalArgumentException if the either location is null; if the old location does not represent the
-     *         {@link Location#isSame(Location) same location} as the {@link #from() from location}; if the new location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}; if the either
-     *         location does not have a path
+     * @throws IllegalArgumentException if the either location is null; if the old location is not {@link Location#equals(Object)
+     *         equal to} the {@link #from() from location}; if the new location is not {@link Location#equals(Object) equal to}
+     *         the {@link #into() into location}; if the either location does not have a path
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocations( Location fromLocation,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -203,10 +203,9 @@
      * 
      * @param fromLocation the actual location of the node being copied
      * @param intoLocation the actual location of the new copy of the node
-     * @throws IllegalArgumentException if the either location is null; if the old location does not represent the
-     *         {@link Location#isSame(Location) same location} as the {@link #from() from location}; if the new location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}; if the either
-     *         location does not have a path
+     * @throws IllegalArgumentException if the either location is null; if the old location is not {@link Location#equals(Object)
+     *         equal to} the {@link #from() from location}; if the new location is not {@link Location#equals(Object) equal to}
+     *         the {@link #into() into location}; if the either location does not have a path
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocations( Location fromLocation,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -275,8 +275,8 @@
      * 
      * @param actual the actual location of the node being created, or null if the {@link #under() current location} should be
      *        used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #under() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the
+     *         {@link #under() current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -88,8 +88,8 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being deleted, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -92,8 +92,8 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being deleted, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -122,8 +122,8 @@
      * the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actualLocation the actual location of the node before being locked
-     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #at() current location}
+     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location is not
+     *         {@link Location#equals(Object) equal to} the {@link #at() current location}
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocation( Location actualLocation ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -225,10 +225,9 @@
      * 
      * @param oldLocation the actual location of the node before being moved
      * @param newLocation the actual new location of the node
-     * @throws IllegalArgumentException if the either location is null, if the old location does not represent the
-     *         {@link Location#isSame(Location) same location} as the {@link #from() from location}, if the new location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}, or if the
-     *         either location does not have a path
+     * @throws IllegalArgumentException if the either location is null, if the old location is not {@link Location#equals(Object)
+     *         equal to} the {@link #from() from location}, if the new location is not {@link Location#equals(Object) equal to}
+     *         the {@link #into() into location}, or if the either location does not have a path
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocations( Location oldLocation,

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -187,9 +187,8 @@
      * 
      * @param actualLocation the actual location of the node being read, or null if the {@link #of() current location} should be
      *        used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #of() current location}; if the actual location does not have a path; or if the actual
-     *         workspace name is null
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #of()
+     *         current location}; if the actual location does not have a path; or if the actual workspace name is null
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actualLocation ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -189,8 +189,8 @@
      * processing the request, and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -234,8 +234,8 @@
      * the request, and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #of() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #of() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #of()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -363,8 +363,8 @@
      * the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      */
     public void setActualLocationOfNode( Location actual ) {
         if (!at.equals(actual)) { // not same if actual is null

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -203,13 +203,13 @@
      * 
      * @param actual the actual location of the node being read, or null if the {@link #startingAfter() starting after location}
      *        should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #startingAfter() starting after location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the
+     *         {@link #startingAfter() starting after location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfStartingAfterNode( Location actual ) {
         checkNotFrozen();
-        if (!startingAfter.isSame(actual)) { // not same if actual is null
+        if (!startingAfter.equals(actual)) { // not same if actual is null
             throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, startingAfter));
         }
         assert actual != null;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -253,8 +253,8 @@
      * when processing the request, and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -128,8 +128,8 @@
      * the request, and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #on() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #on() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #on()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -104,13 +104,13 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being updated, or null if the {@link #from() current location} should be used
-     * @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
-     *         as the {@link #from() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #from()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {
         checkNotFrozen();
-        if (!from.isSame(actual)) { // not same if actual is null
+        if (!from.equals(actual)) { // not same if actual is null
             throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, from));
         }
         assert actual != null;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -106,17 +106,16 @@
      * 
      * @param oldLocation the actual location of the node before being renamed
      * @param newLocation the actual location of the node after being renamed
-     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #at() current location}, if the new
-     *         location does not have the same parent as the old location, or if the new location does not have the same
-     *         {@link Path.Segment#getName() name} on {@link Path#getLastSegment() last segment} as that {@link #toName()
-     *         specified on the request}
+     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location is not
+     *         {@link Location#equals(Object) equal to} the {@link #at() current location}, if the new location does not have the
+     *         same parent as the old location, or if the new location does not have the same {@link Path.Segment#getName() name}
+     *         on {@link Path#getLastSegment() last segment} as that {@link #toName() specified on the request}
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocations( Location oldLocation,
                                     Location newLocation ) {
         checkNotFrozen();
-        if (!at.isSame(oldLocation)) { // not same if actual is null
+        if (!at.equals(oldLocation)) { // not same if actual is null
             throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(oldLocation, at));
         }
         assert oldLocation != null;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -106,8 +106,8 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being updated, or null if the {@link #on() current location} should be used
-     * @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
-     *         as the {@link #on() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #on()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -88,13 +88,13 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actualLocation the actual location of the node before being unlocked
-     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location does not
-     *         represent the {@link Location#isSame(Location) same location} as the {@link #at() current location}
+     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location is not
+     *         {@link Location#equals(Object) equal to} the {@link #at() current location}
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocation( Location actualLocation ) {
         checkNotFrozen();
-        if (!at.isSame(actualLocation)) { // not same if actual is null
+        if (!at.equals(actualLocation)) { // not same if actual is null
             throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actualLocation, at));
         }
         assert actualLocation != null;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -125,8 +125,8 @@
      * and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being updated, or null if the {@link #on() current location} should be used
-     * @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
-     *         as the {@link #on() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #on()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      * @see #setNewProperties(Iterable)
      * @see #setNewProperties(Name...)

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -87,8 +87,8 @@
      * processing the request, and the actual location must have a {@link Location#getPath() path}.
      * 
      * @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #at() current location}, or if the actual location does not have a path.
+     * @throws IllegalArgumentException if the actual location is not {@link Location#equals(Object) equal to} the {@link #at()
+     *         current location}, or if the actual location does not have a path.
      * @throws IllegalStateException if the request is frozen
      */
     public void setActualLocationOfNode( Location actual ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-30 20:08:48 UTC (rev 1371)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/session/GraphSession.java	2009-11-30 21:53:47 UTC (rev 1372)
@@ -65,9 +65,8 @@
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.session.GraphSession.Authorizer.Action;
-import com.google.common.collect.ListMultimap;
 import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimaps;
+import com.google.common.collect.ListMultimap;
 
 /**
  * This class represents an interactive session for working with the content within a graph. This session maintains a cache of


