From dna-commits at lists.jboss.org  Wed Apr  1 11:17:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 11:17:06 -0400
Subject: [dna-commits] DNA SVN: r796 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property/basic
	and 1 other directories.
Message-ID: <E1Lp2Be-0006Or-5u@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 11:17:06 -0400 (Wed, 01 Apr 2009)
New Revision: 796

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/GraphNamespaceRegistry.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
Log:
DNA-337 Namespace Registries Create Two Nodes at /jcr:system/dna:namespaces

Applied the patch, which corrects this behavior by not storing the trivial mapping in the system view and adding code to explicitly create the dna:namespaces node if it is not already there.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java	2009-03-24 22:33:26 UTC (rev 795)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java	2009-04-01 15:17:06 UTC (rev 796)
@@ -40,6 +40,7 @@
     public static final Name MERGE_PLAN = new BasicName(Namespace.URI, "mergePlan");
     public static final Name CLASSNAME = new BasicName(Namespace.URI, "classname");
     public static final Name CLASSPATH = new BasicName(Namespace.URI, "classpath");
+    public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
     public static final Name PROJECTION_RULES = new BasicName(Namespace.URI, "projectionRules");
     public static final Name TIME_TO_EXPIRE = new BasicName(Namespace.URI, "timeToExpire");
     public static final Name NAMESPACE_URI = new BasicName(Namespace.URI, "uri");

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/GraphNamespaceRegistry.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/GraphNamespaceRegistry.java	2009-03-24 22:33:26 UTC (rev 795)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/GraphNamespaceRegistry.java	2009-04-01 15:17:06 UTC (rev 796)
@@ -32,6 +32,7 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.Subgraph;
@@ -78,6 +79,7 @@
             }
         }
         this.namespaceProperties = Collections.unmodifiableList(properties);
+        createNamespaceParentIfNeeded();
         initializeCacheFromStore(cache);
 
         // Load in the namespaces from the execution context used by the store ...
@@ -86,6 +88,16 @@
         }
     }
 
+    private void createNamespaceParentIfNeeded() {
+        try {
+            this.store.getNodeAt(this.parentOfNamespaceNodes);
+        } catch (PathNotFoundException pnfe) {
+            // The node did not already exist - create it!
+            this.store.create(parentOfNamespaceNodes);
+            this.store.set(JcrLexicon.PRIMARY_TYPE).on(parentOfNamespaceNodes).to(DnaLexicon.NAMESPACES);
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -221,6 +233,9 @@
                     cache.register(prefix, uri);
                 }
             }
+
+            // Empty prefix to namespace mapping is built-in
+            cache.register("", "");
         } catch (PathNotFoundException e) {
             // Nothing to read
         }
@@ -297,6 +312,11 @@
         prefix = prefix.trim();
         uri = uri.trim();
 
+        // Empty prefix to namespace mapping is built in
+        if (prefix.length() == 0) {
+            return null;
+        }
+
         // Read the store ...
         String previousUri = null;
         ValueFactory<String> stringFactory = store.getContext().getValueFactories().getStringFactory();

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-24 22:33:26 UTC (rev 795)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-01 15:17:06 UTC (rev 796)
@@ -31,12 +31,9 @@
  */
 public class DnaLexicon extends org.jboss.dna.graph.DnaLexicon {
 
-    public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
     public static final Name ROOT = new BasicName(Namespace.URI, "root");
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
     public static final Name URI = new BasicName(Namespace.URI, "uri");
-
-
 }



From dna-commits at lists.jboss.org  Wed Apr  1 11:46:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 11:46:00 -0400
Subject: [dna-commits] DNA SVN: r797 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lp2dc-0000GY-Iq@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 11:46:00 -0400 (Wed, 01 Apr 2009)
New Revision: 797

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
Log:
DNA-338 Definitions That Allow SNS Are Not Used For Nodes Without SNS

Did not apply the patch, since it copied the same logic of DefinitionCache.allChildNodeDefinitions(Name childName, boolean requireSns) into JcrNodeType.allChildNodeDefinitions (rather than just calling the same DefinitionCache method).  The patch would have not have changed the behavior.  However, a few argument names were corrected (from "propertyName" to "childName"); likely due to a copy/paste error.

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-04-01 15:17:06 UTC (rev 796)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	2009-04-01 15:46:00 UTC (rev 797)
@@ -139,8 +139,8 @@
             } else {
                 childNodeDefinitionsThatAllowNoSns.put(name, definition);
             }
+            allChildNodeDefinitions.put(name, definition);
             namesFromThisType.add(name);
-            allChildNodeDefinitions.put(name, definition);
         }
 
         namesFromThisType.clear();
@@ -193,9 +193,10 @@
         return this.allChildNodeDefinitions.get(childName);
     }
 
-    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
-                                                           boolean requireSns ) {
+    public Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
+                                                                  boolean requireSns ) {
         if (requireSns) {
+            // Only return definitions that allow SNS since we require SNS support
             return childNodeDefinitionsThatAllowSns.get(childName);
         }
         return allChildNodeDefinitions.get(childName);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-01 15:17:06 UTC (rev 796)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-01 15:46:00 UTC (rev 797)
@@ -186,13 +186,13 @@
         return allDefinitions.allPropertyDefinitions(propertyName);
     }
 
-    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name propertyName,
+    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
                                                            boolean requireSns ) {
-        return allDefinitions.allChildNodeDefinitions(propertyName, requireSns);
+        return allDefinitions.allChildNodeDefinitions(childName, requireSns);
     }
 
-    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name propertyName ) {
-        return allDefinitions.allChildNodeDefinitions(propertyName);
+    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName ) {
+        return allDefinitions.allChildNodeDefinitions(childName);
     }
 
     /**



From dna-commits at lists.jboss.org  Wed Apr  1 12:10:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 12:10:43 -0400
Subject: [dna-commits] DNA SVN: r798 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lp31X-0008GT-UE@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 12:10:43 -0400 (Wed, 01 Apr 2009)
New Revision: 798

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
Log:
DNA-338 Definitions That Allow SNS Are Not Used For Nodes Without SNS

I believe the reason the patch did not include an effective change was that Brian had multiple changes brewing in his workspace (see DNA-339).  The actual correction for this behavior was buried in RepositoryNodeTypeManager.findChildNodeDefinition(...), where the boolean determining whether SNS were required was computed incorrectly.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-01 15:46:00 UTC (rev 797)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-01 16:10:43 UTC (rev 798)
@@ -508,7 +508,7 @@
                                                int numberOfExistingChildrenWithSameName,
                                                boolean skipProtected ) {
         JcrNodeType childType = childPrimaryNodeType != null ? getNodeType(childPrimaryNodeType) : null;
-        boolean requireSns = numberOfExistingChildrenWithSameName != 0;
+        boolean requireSns = numberOfExistingChildrenWithSameName > 1;
 
         // First look in the primary type ...
         JcrNodeType primaryType = getNodeType(primaryTypeNameOfParent);



From dna-commits at lists.jboss.org  Wed Apr  1 18:27:52 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 18:27:52 -0400
Subject: [dna-commits] DNA SVN: r799 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/request and 4
	other directories.
Message-ID: <E1Lp8uW-0001fL-SR@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 18:27:52 -0400 (Wed, 01 Apr 2009)
New Revision: 799

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.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/MoveBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.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/UpdatePropertiesRequest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-194 Implement update JCR capability

Implemented Node.addNode(...) methods and Node.save().  Some enhancements to the Request classes were required to allow the SessionCache to easily figure out whether a request affected a particular branch.  Also enabled one TCK unit test.


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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -1925,10 +1925,26 @@
      * @see Results
      */
     public Batch batch() {
-        return new Batch();
+        return new Batch(new LinkedList<Request>());
     }
 
     /**
+     * Begin a batch of requests to perform various operations, but specify the queue where all accumulated requests should be
+     * placed. Use this approach when multiple operations are to be built and then executed with one submission to the underlying
+     * {@link #getSourceName() repository source}. The {@link Results results} are not available until the {@link Batch#execute()}
+     * method is invoked.
+     * 
+     * @param queue the queue where all batched requests should be placed
+     * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
+     * @see Batch#execute()
+     * @see Results
+     */
+    public Batch batch( LinkedList<Request> queue ) {
+        CheckArg.isNotNull(queue, "queue");
+        return new Batch(queue);
+    }
+
+    /**
      * 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()}.
@@ -1937,12 +1953,13 @@
      */
     @Immutable
     public final class Batch implements Executable<Node> {
-        protected final CompositingRequestQueue requestQueue = new CompositingRequestQueue();
+        protected final CompositingRequestQueue requestQueue;
         protected final BatchConjunction nextRequests;
         protected final String workspaceName;
         protected boolean executed = false;
 
-        /*package*/Batch() {
+        /*package*/Batch( LinkedList<Request> queue ) {
+            this.requestQueue = new CompositingRequestQueue(queue);
             this.workspaceName = Graph.this.getCurrentWorkspaceName();
             this.nextRequests = new BatchConjunction() {
                 public Batch and() {
@@ -4737,8 +4754,13 @@
      */
     @NotThreadSafe
     /*package*/class CompositingRequestQueue implements RequestQueue {
-        private final LinkedList<Request> requests = new LinkedList<Request>();
+        private final LinkedList<Request> requests;
 
+        CompositingRequestQueue( LinkedList<Request> queue ) {
+            assert queue != null;
+            this.requests = queue;
+        }
+
         public Graph getGraph() {
             return Graph.this;
         }

Added: 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	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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.request;
+
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * 
+ */
+public interface ChangeRequest {
+
+    /**
+     * Determine if this request changes the branch at the given path.
+     * 
+     * @param workspace the name of the workspace; may not be null
+     * @param path the path; may not be null
+     * @return true if this request changes a node under the given path
+     */
+    boolean changes( String workspace,
+                     Path path );
+
+    /**
+     * Get the location of the top-most node that is to be changed by this request.
+     * 
+     * @return the location changed by this request
+     */
+    Location changedLocation();
+}


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

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -28,6 +28,7 @@
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.NodeConflictBehavior;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 
 /**
  * Instruction that a branch be copied from one location into another. This request can copy a branch in one workspace into
@@ -35,7 +36,7 @@
  * 
  * @author Randall Hauch
  */
-public class CopyBranchRequest extends Request {
+public class CopyBranchRequest extends Request implements ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -246,6 +247,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.intoWorkspace.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return into;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -34,6 +34,7 @@
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.NodeConflictBehavior;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
 
 /**
@@ -41,7 +42,7 @@
  * 
  * @author Randall Hauch
  */
-public class CreateNodeRequest extends Request implements Iterable<Property> {
+public class CreateNodeRequest extends Request implements Iterable<Property>, ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -317,6 +318,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return under;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -26,13 +26,14 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
 
 /**
  * Instruction that a branch be deleted.
  * 
  * @author Randall Hauch
  */
-public class DeleteBranchRequest extends Request {
+public class DeleteBranchRequest extends Request implements ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -114,6 +115,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -34,7 +34,7 @@
  * 
  * @author Randall Hauch
  */
-public class MoveBranchRequest extends Request {
+public class MoveBranchRequest extends Request implements ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -207,6 +207,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return into;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -32,13 +32,14 @@
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 
 /**
  * Instruction to remove properties from the node at the specified location.
  * 
  * @author Randall Hauch
  */
-public class RemovePropertiesRequest extends Request implements Iterable<Name> {
+public class RemovePropertiesRequest extends Request implements Iterable<Name>, ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -196,6 +197,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && from.hasPath() && from.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return from;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -35,7 +35,7 @@
  * 
  * @author Randall Hauch
  */
-public class RenameNodeRequest extends Request {
+public class RenameNodeRequest extends Request implements ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -161,6 +161,25 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().getParent().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -35,6 +35,7 @@
 import org.jboss.dna.graph.GraphI18n;
 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.Property;
 
 /**
@@ -42,7 +43,7 @@
  * 
  * @author Randall Hauch
  */
-public class UpdatePropertiesRequest extends Request implements Iterable<Property> {
+public class UpdatePropertiesRequest extends Request implements Iterable<Property>, ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
@@ -216,6 +217,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && on.hasPath() && on.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -234,6 +245,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return on;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#toString()
      */
     @Override

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-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -31,7 +31,9 @@
 import java.util.List;
 import java.util.UUID;
 import java.util.regex.Pattern;
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
@@ -44,10 +46,13 @@
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.i18n.I18n;
@@ -56,6 +61,7 @@
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.NodeInfo;
@@ -88,6 +94,30 @@
         return cache.findNodeInfo(nodeUuid);
     }
 
+    final NodeEditor editorForParent() throws RepositoryException {
+        try {
+            return cache.getEditorFor(nodeInfo().getParent());
+        } catch (ItemNotFoundException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        } catch (InvalidItemStateException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        }
+    }
+
+    final NodeEditor editor() throws RepositoryException {
+        try {
+            return cache.getEditorFor(nodeUuid);
+        } catch (ItemNotFoundException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        } catch (InvalidItemStateException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        }
+    }
+
     @Override
     Path path() throws RepositoryException {
         return cache.getPathFor(nodeInfo());
@@ -380,7 +410,7 @@
         int indexOfFirstSlash = relativePath.indexOf('/');
         if (indexOfFirstSlash == 0) {
             // Not a relative path ...
-            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
         }
         if (indexOfFirstSlash != -1) {
             // We know it's a relative path with more than one segment ...
@@ -411,7 +441,7 @@
         int indexOfFirstSlash = relativePath.indexOf('/');
         if (indexOfFirstSlash == 0) {
             // Not a relative path ...
-            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
         }
         if (indexOfFirstSlash != -1) {
             Path path = pathFrom(relativePath).getNormalizedPath();
@@ -450,7 +480,7 @@
         int indexOfFirstSlash = relativePath.indexOf('/');
         if (indexOfFirstSlash == 0) {
             // Not a relative path ...
-            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
         }
         if (indexOfFirstSlash != -1) {
             // We know it's a relative path with more than one segment ...
@@ -547,22 +577,86 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#addNode(java.lang.String)
      */
-    public final Node addNode( String relPath ) {
-        throw new UnsupportedOperationException();
+    public final Node addNode( String relPath )
+        throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
+        RepositoryException {
+        return addNode(relPath, null);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#addNode(java.lang.String, java.lang.String)
      */
     public final Node addNode( String relPath,
-                               String primaryNodeTypeName ) {
-        throw new UnsupportedOperationException();
+                               String primaryNodeTypeName )
+        throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
+        RepositoryException {
+        // Determine the path ...
+        NodeEditor editor = null;
+        Path path = null;
+        try {
+            path = cache.pathFactory().create(relPath);
+        } catch (ValueFormatException e) {
+            throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
+        }
+        if (path.size() == 0) {
+            throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
+        }
+        if (path.getLastSegment().getIndex() > 1 || relPath.endsWith("]")) {
+            throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
+        }
+        if (path.size() != 1) {
+            // The only segment in the path is the child name ...
+            NodeInfo parentInfo = null;
+            Path parentPath = path.getParent();
+            try {
+                parentInfo = cache.findNodeInfo(nodeUuid, parentPath); // throws PathNotFoundException
+                editor = cache.getEditorFor(parentInfo.getUuid());
+            } catch (PathNotFoundException e) {
+                // We're going to throw an exception ... the question is which one ...
+                if (parentPath.size() > 1) {
+                    // Per the TCK, if relPath references a property, then we have to throw a ConstraintViolationException
+                    // So, if we can't find the parent, try for the parent's parent and see if the last segment of the parent's
+                    // path contains a property ...
+                    Path grandparentPath = parentPath.getParent();
+                    assert grandparentPath != null;
+                    try {
+                        NodeInfo grandparentInfo = cache.findNodeInfo(nodeUuid, grandparentPath); // throws PathNotFoundException
+                        if (grandparentInfo.getProperty(parentPath.getLastSegment().getName()) != null) {
+                            // Need to throw a ConstraintViolationException since the request was to add a child to
+                            // a property ...
+                            throw new ConstraintViolationException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
+                        }
+                    } catch (PathNotFoundException e2) {
+                        // eat, since the original exception is what we want ...
+                    }
+                }
+                // Otherwise, just throw the PathNotFoundException ...
+                throw e;
+            }
+        } else {
+            assert path.size() == 1;
+            editor = editor();
+        }
+        Name childName = path.getLastSegment().getName();
+
+        // Determine the name for the primary node type
+        Name childPrimaryTypeName = null;
+        if (primaryNodeTypeName != null) {
+            try {
+                childPrimaryTypeName = cache.nameFactory().create(primaryNodeTypeName);
+            } catch (ValueFormatException e) {
+                throw new RepositoryException(JcrI18n.invalidNodeTypeNameParameter.text(primaryNodeTypeName,
+                                                                                        "primaryNodeTypeName"));
+            }
+        }
+
+        // Create the child ...
+        ChildNode child = editor.createChild(childName, null, childPrimaryTypeName);
+        return cache.findJcrNode(child.getUuid());
     }
 
     /**
@@ -1001,10 +1095,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Item#save()
      */
-    public void save() {
-        throw new UnsupportedOperationException();
+    public void save() throws RepositoryException {
+        cache.save(nodeUuid);
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -62,6 +62,7 @@
     public static I18n invalidRelativePath;
     public static I18n invalidPathParameter;
     public static I18n invalidNamePattern;
+    public static I18n invalidNodeTypeNameParameter;
     public static I18n noPrimaryItemNameDefinedOnPrimaryType;
     public static I18n primaryItemNameForPrimaryTypeIsNotValid;
     public static I18n primaryItemDoesNotExist;
@@ -75,6 +76,9 @@
     public static I18n errorWhileFindingNodeWithPath;
     public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
     public static I18n missingNodeTypeForExistingNode;
+    public static I18n unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
+    public static I18n unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
+    public static I18n unableToSaveNodeThatWasCreatedSincePreviousSave;
 
     public static I18n unableToRemoveRootNode;
     public static I18n unableToMoveNodeToBeChildOfDecendent;
@@ -100,7 +104,7 @@
     // Query-related messages
     public static I18n notStoredQuery;
     public static I18n invalidQueryLanguage;
-    
+
     static {
         try {
             I18n.initialize(JcrI18n.class);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -24,7 +24,6 @@
 package org.jboss.dna.jcr;
 
 import java.util.UUID;
-import javax.jcr.InvalidItemStateException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -93,16 +92,7 @@
      * @see javax.jcr.Item#remove()
      */
     public void remove() throws RepositoryException {
-        try {
-            SessionCache.NodeEditor editor = cache.getEditorFor(nodeInfo().getParent());
-            editor.destroyChild(nodeUuid);
-        } catch (ItemNotFoundException err) {
-            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
-            throw new RepositoryException(msg);
-        } catch (InvalidItemStateException err) {
-            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
-            throw new RepositoryException(msg);
-        }
+        editorForParent().destroyChild(nodeUuid);
     }
 
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -27,6 +27,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -44,10 +45,12 @@
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
@@ -63,6 +66,8 @@
 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.request.ChangeRequest;
+import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.jcr.cache.ChangedNodeInfo;
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
@@ -141,7 +146,8 @@
     protected final HashMap<UUID, ChangedNodeInfo> changedNodes;
     protected final HashMap<UUID, NodeInfo> deletedNodes;
 
-    private Graph.Batch operations;
+    private LinkedList<Request> requests;
+    protected Graph.Batch operations;
 
     public SessionCache( JcrSession session,
                          String workspaceName,
@@ -174,6 +180,7 @@
         this.deletedNodes = new HashMap<UUID, NodeInfo>();
 
         // Create the batch operations ...
+        this.requests = new LinkedList<Request>();
         this.operations = this.store.batch();
     }
 
@@ -181,22 +188,26 @@
         return session;
     }
 
+    String workspaceName() {
+        return workspaceName;
+    }
+
     ExecutionContext context() {
         return context;
     }
 
-    String workspaceName() {
-        return workspaceName;
+    PathFactory pathFactory() {
+        return pathFactory;
     }
 
+    NameFactory nameFactory() {
+        return nameFactory;
+    }
+
     JcrNodeTypeManager nodeTypes() {
         return session.nodeTypeManager();
     }
 
-    final Graph.Batch operations() {
-        return operations;
-    }
-
     /**
      * Save any changes that have been accumulated by this session.
      * 
@@ -207,12 +218,17 @@
             // Execute the batched operations ...
             try {
                 operations.execute();
+            } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+                throw new PathNotFoundException(e.getLocalizedMessage(), e);
             } catch (RuntimeException e) {
-                throw new RepositoryException(e);
+                throw new RepositoryException(e.getLocalizedMessage(), e);
             }
 
             // Create a new batch for future operations ...
-            operations = store.batch();
+            // LinkedList<Request> oldRequests = this.requests;
+            this.requests = new LinkedList<Request>();
+            operations = store.batch(this.requests);
+
             // Remove all the cached items that have been changed or deleted ...
             for (UUID changedUuid : changedNodes.keySet()) {
                 cachedNodes.remove(changedUuid);
@@ -226,6 +242,77 @@
         }
     }
 
+    /**
+     * Save any changes to the identified node or its decendants. The supplied node may not have been deleted or created in this
+     * session since the last save operation.
+     * 
+     * @param nodeUuid the UUID of the node that is to be saved; may not be null
+     * @throws RepositoryException if any error resulting while saving the changes to the repository
+     */
+    public void save( UUID nodeUuid ) throws RepositoryException {
+        assert nodeUuid != null;
+        if (deletedNodes.containsKey(nodeUuid)) {
+            // This node was deleted in this session ...
+            throw new InvalidItemStateException(JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, workspaceName()));
+        }
+
+        // The node must not have been created since the last save ...
+        if (!cachedNodes.containsKey(nodeUuid)) {
+            // It is not cached, which means it WAS created ...
+            Path path = getPathFor(nodeUuid);
+            throw new RepositoryException(JcrI18n.unableToSaveNodeThatWasCreatedSincePreviousSave.text(path, workspaceName()));
+        }
+
+        if (operations.isExecuteRequired()) {
+            // Find the path of the given node ...
+            Path path = getPathFor(nodeUuid);
+
+            // Remove all of the enqueued requests for this branch ...
+            LinkedList<Request> branchRequests = new LinkedList<Request>();
+            LinkedList<Request> nonBranchRequests = new LinkedList<Request>();
+            Set<UUID> branchUuids = new HashSet<UUID>();
+            for (Request request : this.requests) {
+                assert request instanceof ChangeRequest;
+                ChangeRequest change = (ChangeRequest)request;
+                if (change.changes(workspaceName, path)) {
+                    branchRequests.add(request);
+                    // Record the UUID of the node being saved now ...
+                    UUID changedUuid = change.changedLocation().getUuid();
+                    assert changedUuid != null;
+                    branchUuids.add(changedUuid);
+                } else {
+                    nonBranchRequests.add(request);
+                }
+            }
+
+            if (branchRequests.isEmpty()) {
+                // None of the changes affected the branch given by the node ...
+                return;
+            }
+
+            // Now execute the branch ...
+            Graph.Batch branchBatch = store.batch(branchRequests);
+            try {
+                branchBatch.execute();
+
+                // Still have non-branch related requests that we haven't executed ...
+                this.requests = nonBranchRequests;
+                this.operations = store.batch(nonBranchRequests);
+
+                // Remove all the cached, changed or deleted items that were just saved ...
+                for (UUID changedUuid : branchUuids) {
+                    cachedNodes.remove(changedUuid);
+                    changedNodes.remove(changedUuid);
+                    deletedNodes.remove(changedUuid);
+                }
+            } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+                throw new PathNotFoundException(e.getLocalizedMessage(), e);
+            } catch (RuntimeException e) {
+                throw new RepositoryException(e.getLocalizedMessage(), e);
+            }
+        }
+    }
+
     public JcrRootNode findJcrRootNode() throws RepositoryException {
         return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
     }
@@ -498,7 +585,7 @@
 
             // Finally update the cached information and record the change ...
             node.setProperty(newProperty, context().getValueFactories());
-            operations().set(dnaProp).on(currentLocation);
+            operations.set(dnaProp).on(currentLocation);
         }
 
         /**
@@ -587,7 +674,7 @@
 
             // Finally update the cached information and record the change ...
             node.setProperty(newProperty, context().getValueFactories());
-            operations().set(dnaProp).on(currentLocation);
+            operations.set(dnaProp).on(currentLocation);
         }
 
         /**
@@ -598,7 +685,7 @@
          */
         public boolean removeProperty( Name name ) {
             if (node.removeProperty(name) != null) {
-                operations().remove(name).on(currentLocation);
+                operations.remove(name).on(currentLocation);
                 return true;
             }
             return false;
@@ -675,7 +762,7 @@
             existingNodeInfo.setParent(node.getUuid());
 
             // Now, record the operation to do this ...
-            operations().move(existingNodeEditor.currentLocation).into(currentLocation);
+            operations.move(existingNodeEditor.currentLocation).into(currentLocation);
 
             return newChild;
         }
@@ -686,17 +773,16 @@
          * @param name the name for the new child; may not be null
          * @param desiredUuid the desired UUID, or null if the UUID for the child should be generated automatically
          * @param primaryTypeName the name of the primary type for the new node
-         * @param nodeDefinitionId
          * @return the representation of the newly-created child, which includes the {@link ChildNode#getSnsIndex()
          *         same-name-sibling index}
          * @throws InvalidItemStateException if the specified child has been marked for deletion within this session
          * @throws ConstraintViolationException if moving the node into this node violates this node's definition
+         * @throws NoSuchNodeTypeException if the node type for the primary type could not be found
          * @throws RepositoryException if any other error occurs while reading information from the repository
          */
         public ChildNode createChild( Name name,
                                       UUID desiredUuid,
-                                      Name primaryTypeName,
-                                      NodeDefinitionId nodeDefinitionId )
+                                      Name primaryTypeName )
             throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
             if (desiredUuid == null) desiredUuid = UUID.randomUUID();
 
@@ -708,10 +794,34 @@
                                                                                primaryTypeName,
                                                                                numSns,
                                                                                true);
+            // Make sure there was a valid child node definition ...
             if (definition == null) {
-                throw new ConstraintViolationException();
+                Path pathForChild = pathFactory.create(getPathFor(node), name, numSns + 1);
+                String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(pathForChild, workspaceName());
+                throw new ConstraintViolationException(msg);
             }
 
+            // Find the primary type ...
+            JcrNodeType primaryType = null;
+            if (primaryTypeName != null) {
+                primaryType = nodeTypes().getNodeType(primaryTypeName);
+                if (primaryType == null) {
+                    Path pathForChild = pathFactory.create(getPathFor(node), name, numSns + 1);
+                    I18n msg = JcrI18n.unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
+                    throw new NoSuchNodeTypeException(msg.text(primaryTypeName, pathForChild, workspaceName()));
+                }
+            } else {
+                primaryType = (JcrNodeType)definition.getDefaultPrimaryType();
+                if (primaryType == null) {
+                    // There is no default primary type ...
+                    Path pathForChild = pathFactory.create(getPathFor(node), name, numSns + 1);
+                    I18n msg = JcrI18n.unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
+                    String nodeTypeName = definition.getDeclaringNodeType().getName();
+                    throw new NoSuchNodeTypeException(msg.text(definition.getName(), nodeTypeName, pathForChild, workspaceName()));
+                }
+            }
+            primaryTypeName = primaryType.getInternalName();
+
             ChildNode result = node.addChild(name, desiredUuid, pathFactory);
 
             // ---------------------------------------------------------
@@ -723,7 +833,7 @@
             // Create the properties ...
             Map<Name, PropertyInfo> properties = new HashMap<Name, PropertyInfo>();
             Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
-            Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, nodeDefinitionId.getString());
+            Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
 
             // Create the property info for the "jcr:primaryType" child property ...
             JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(node.getPrimaryTypeName(),
@@ -759,10 +869,10 @@
             // ---------------------------------------
             // Now record the changes to the store ...
             // ---------------------------------------
-            Graph.Create<Graph.Batch> create = operations().createUnder(currentLocation)
-                                                           .nodeNamed(name)
-                                                           .with(desiredUuid)
-                                                           .with(primaryTypeProp);
+            Graph.Create<Graph.Batch> create = operations.createUnder(currentLocation)
+                                                         .nodeNamed(name)
+                                                         .with(desiredUuid)
+                                                         .with(primaryTypeProp);
             if (nodeDefnDefn != null) {
                 create = create.with(nodeDefinitionProp);
             }
@@ -787,7 +897,7 @@
                 // Now make the request to the source ...
                 Path childPath = pathFactory.create(currentLocation.getPath(), deleted.getSegment());
                 Location locationOfChild = Location.create(childPath, nodeUuid);
-                operations().delete(locationOfChild);
+                operations.delete(locationOfChild);
                 return true;
             }
             return false;
@@ -917,6 +1027,7 @@
      * @see #findNodeInfoForRoot()
      */
     NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+        assert uuid != null;
         // See if we already have something in the cache ...
         NodeInfo info = findNodeInfoInCache(uuid);
         if (info == null) {
@@ -1326,16 +1437,16 @@
             definition = nodeTypes().getNodeDefinition(id);
         }
         // Figure out the node definition for this node ...
-        if (definition == null) {
-            if (isRoot) {
-                definition = nodeTypes().getRootNodeDefinition();
-            } else {
-                // We need the parent ...
-                Path path = location.getPath();
-                if (parentInfo == null) {
-                    Path parentPath = path.getParent();
-                    parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
-                }
+        if (isRoot) {
+            if (definition == null) definition = nodeTypes().getRootNodeDefinition();
+        } else {
+            // We need the parent ...
+            Path path = location.getPath();
+            if (parentInfo == null) {
+                Path parentPath = path.getParent();
+                parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
+            }
+            if (definition == null) {
                 Name childName = path.getLastSegment().getName();
                 int numExistingChildrenWithSameName = parentInfo.getChildren().getCountOfSameNameSiblingsWithName(childName);
                 definition = nodeTypes().findChildNodeDefinition(parentInfo.getPrimaryTypeName(),
@@ -1344,11 +1455,11 @@
                                                                  primaryTypeName,
                                                                  numExistingChildrenWithSameName,
                                                                  false);
-                if (definition == null) {
-                    String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
-                    throw new RepositorySourceException(msg);
-                }
             }
+            if (definition == null) {
+                String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
+                throw new RepositorySourceException(msg);
+            }
         }
 
         // ------------------------------------------------------

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-01 22:27:52 UTC (rev 799)
@@ -52,6 +52,7 @@
 invalidRelativePath = "{0}" is not a valid relative path
 invalidPathParameter = The "{1}" parameter value "{0}" was not a valid path
 invalidNamePattern = The "{1}" name pattern contained the '{0}' character, which is not allowed in a name pattern
+invalidNodeTypeNameParameter = The "{1}" parameter value "{0}" was not a valid node type name
 noPrimaryItemNameDefinedOnPrimaryType = The primary type "{0}" for node "{1}" in workspace "{2}" does not define a primary item name
 primaryItemNameForPrimaryTypeIsNotValid = The primary type "{0}" for node "{2}" in workspace "{3}" defines an invalid primary item name ("{1}")
 primaryItemDoesNotExist = The node "{2}" in workspace "{3}" does not have an item named "{1}" as defined by its primary type "{0}"
@@ -65,6 +66,9 @@
 errorWhileFindingNodeWithPath = Error while finding the node "{0}" in workspace "{1}"
 nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
 missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
+unableToCreateNodeWithPrimaryTypeThatDoesNotExist = Unable to create child "{1}" in workspace "{2}" because the node type "{0}" does not exist
+unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition = Unable to create child "{2}" in workspace "{3}" because the node definition "{0}" on the "{1}" node type has no default primary type 
+unableToSaveNodeThatWasCreatedSincePreviousSave = Unable to save node "{0}" in workspace "{1}" because it was created since the last save
 
 unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
 unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}" cannot be moved under a decendant node ("{1}") 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-01 22:27:52 UTC (rev 799)
@@ -35,6 +35,7 @@
 import junit.framework.TestSuite;
 import org.apache.jackrabbit.test.JCRTestSuite;
 import org.apache.jackrabbit.test.RepositoryStub;
+import org.apache.jackrabbit.test.api.AddNodeTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
@@ -142,7 +143,7 @@
             // See https://jira.jboss.org/jira/browse/DNA-285
 
             // level 2 tests
-            // addTestSuite(AddNodeTest.class);
+            addTestSuite(AddNodeTest.class);
             // addTestSuite(NamespaceRegistryTest.class);
             // addTestSuite(ReferencesTest.class);
             // addTestSuite(SessionTest.class);

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-01 16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-01 22:27:52 UTC (rev 799)
@@ -7,3 +7,5 @@
 javax.jcr.tck.propertyname1=prop1
 javax.jcr.tck.propertyname2=prop2
 javax.jcr.tck.workspacename=
+javax.jcr.tck.nodetype=nt:unstructured
+javax.jcr.tck.nodetypenochildren=dna:namespace



From dna-commits at lists.jboss.org  Wed Apr  1 18:37:07 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 18:37:07 -0400
Subject: [dna-commits] DNA SVN: r800 -
	trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
Message-ID: <E1Lp93T-0004rD-6N@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 18:37:07 -0400 (Wed, 01 Apr 2009)
New Revision: 800

Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-194 Implement update JCR capability

Enabled one additional TCK unit test.


Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-01 22:27:52 UTC (rev 799)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-01 22:37:07 UTC (rev 800)
@@ -36,6 +36,7 @@
 import org.apache.jackrabbit.test.JCRTestSuite;
 import org.apache.jackrabbit.test.RepositoryStub;
 import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
@@ -144,7 +145,7 @@
 
             // level 2 tests
             addTestSuite(AddNodeTest.class);
-            // addTestSuite(NamespaceRegistryTest.class);
+            addTestSuite(NamespaceRegistryTest.class);
             // addTestSuite(ReferencesTest.class);
             // addTestSuite(SessionTest.class);
             // addTestSuite(SessionUUIDTest.class);



From dna-commits at lists.jboss.org  Wed Apr  1 19:16:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 19:16:08 -0400
Subject: [dna-commits] DNA SVN: r801 -
	trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
Message-ID: <E1Lp9fE-0000mn-MC@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 19:16:08 -0400 (Wed, 01 Apr 2009)
New Revision: 801

Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
Log:
DNA-194 Implement update JCR capability

Removed unit tests that were requiring UnsupportedOperationException on methods that were just recently implemented.


Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-01 22:37:07 UTC (rev 800)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-01 23:16:08 UTC (rev 801)
@@ -236,16 +236,6 @@
         node.addMixin(null);
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shoudNotAllowAddNode() throws Exception {
-        node.addNode(null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shoudNotAllowAddNodeWithType() throws Exception {
-        node.addNode(null, null);
-    }
-
     @Test
     public void shoudNotAllowCanAddMixin() throws Exception {
         assertThat(node.canAddMixin(null), is(false));



From dna-commits at lists.jboss.org  Wed Apr  1 20:03:04 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 01 Apr 2009 20:03:04 -0400
Subject: [dna-commits] DNA SVN: r802 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory.
Message-ID: <E1LpAOe-00071W-B0@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-01 20:03:04 -0400 (Wed, 01 Apr 2009)
New Revision: 802

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
Log:
Change to the InMemory connector to make adding nodes more efficient.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryNode.java	2009-04-01 23:16:08 UTC (rev 801)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryNode.java	2009-04-02 00:03:04 UTC (rev 802)
@@ -24,9 +24,10 @@
 package org.jboss.dna.graph.connector.inmemory;
 
 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;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.ExecutionContext;
@@ -45,7 +46,8 @@
     private InMemoryNode parent;
     private Path.Segment name;
     private final Map<Name, Property> properties = new HashMap<Name, Property>();
-    private final List<InMemoryNode> children = new LinkedList<InMemoryNode>();
+    private final LinkedList<InMemoryNode> children = new LinkedList<InMemoryNode>();
+    final Set<Name> existingNames = new HashSet<Name>();
 
     public InMemoryNode( UUID uuid ) {
         assert uuid != null;
@@ -90,7 +92,7 @@
     /**
      * @return children
      */
-    protected List<InMemoryNode> getChildren() {
+    LinkedList<InMemoryNode> getChildren() {
         return children;
     }
 
@@ -109,8 +111,8 @@
     }
 
     public InMemoryNode setProperty( ExecutionContext context,
-                             String name,
-                             Object... values ) {
+                                     String name,
+                                     Object... values ) {
         PropertyFactory propertyFactory = context.getPropertyFactory();
         Name propertyName = context.getValueFactories().getNameFactory().create(name);
         return setProperty(propertyFactory.create(propertyName, values));

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-01 23:16:08 UTC (rev 801)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-02 00:03:04 UTC (rev 802)
@@ -28,6 +28,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -316,10 +317,22 @@
             InMemoryNode node = new InMemoryNode(uuid);
             nodesByUuid.put(node.getUuid(), node);
             node.setParent(parentNode);
-            Path.Segment newName = context.getValueFactories().getPathFactory().createSegment(name);
+            // Find the last node with this same name ...
+            int nextIndex = 1;
+            if (parentNode.existingNames.contains(name)) {
+                ListIterator<InMemoryNode> iter = parentNode.getChildren().listIterator(parentNode.getChildren().size());
+                while (iter.hasPrevious()) {
+                    InMemoryNode prev = iter.previous();
+                    if (prev.getName().getName().equals(name)) {
+                        nextIndex = prev.getName().getIndex() + 1;
+                        break;
+                    }
+                }
+            }
+            Path.Segment newName = context.getValueFactories().getPathFactory().createSegment(name, nextIndex);
             node.setName(newName);
             parentNode.getChildren().add(node);
-            correctSameNameSiblingIndexes(context, parentNode, name);
+            parentNode.existingNames.add(name);
             return node;
         }
 



From dna-commits at lists.jboss.org  Thu Apr  2 14:52:26 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 02 Apr 2009 14:52:26 -0400
Subject: [dna-commits] DNA SVN: r803 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property/basic
	and 2 other directories.
Message-ID: <E1LpS1a-0000s8-Jp@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-02 14:52:26 -0400 (Thu, 02 Apr 2009)
New Revision: 803

Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/IoUtil.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/InMemoryBinary.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-194 Implement update JCR capability

Added more support for Node.setProperty(...), and enabled several TCK unit tests.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/IoUtil.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/IoUtil.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/IoUtil.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -34,6 +34,7 @@
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;
+import java.util.Arrays;
 
 /**
  * @author Randall Hauch
@@ -352,6 +353,58 @@
         }
     }
 
+    /**
+     * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when
+     * finished.
+     * 
+     * @param input1 the first stream
+     * @param input2 the second stream
+     * @return true if the streams contain the same content, or false otherwise
+     * @throws IOException
+     * @throws IllegalArgumentException if the stream is null
+     */
+    public static boolean isSame( InputStream input1,
+                                  InputStream input2 ) throws IOException {
+        CheckArg.isNotNull(input1, "input1");
+        CheckArg.isNotNull(input2, "input2");
+        boolean error = false;
+        try {
+            byte[] buffer1 = new byte[1024];
+            byte[] buffer2 = new byte[1024];
+            try {
+                int numRead1 = 0;
+                int numRead2 = 0;
+                while (true) {
+                    numRead1 = input1.read(buffer1);
+                    numRead2 = input2.read(buffer2);
+                    if (numRead1 > -1) {
+                        if (numRead2 != numRead1) return false;
+                        // Otherwise same number of bytes read
+                        if (!Arrays.equals(buffer1, buffer2)) return false;
+                        // Otherwise same bytes read, so continue ...
+                    } else {
+                        // Nothing more in stream 1 ...
+                        return numRead2 < 0;
+                    }
+                }
+            } finally {
+                input1.close();
+            }
+        } catch (IOException e) {
+            error = true; // this error should be thrown, even if there is an error closing stream 2
+            throw e;
+        } catch (RuntimeException e) {
+            error = true; // this error should be thrown, even if there is an error closing stream 2
+            throw e;
+        } finally {
+            try {
+                input2.close();
+            } catch (IOException e) {
+                if (!error) throw e;
+            }
+        }
+    }
+
     private IoUtil() {
         // Prevent construction
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/InMemoryBinary.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/InMemoryBinary.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/InMemoryBinary.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -44,6 +44,7 @@
 
     private final byte[] bytes;
     private byte[] sha1hash;
+    private int hc;
 
     public InMemoryBinary( byte[] bytes ) {
         CheckArg.isNotNull(bytes, "bytes");
@@ -52,7 +53,22 @@
 
     /**
      * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
      */
+    @Override
+    public int hashCode() {
+        if (sha1hash == null) {
+            // Idempotent, so doesn't matter if we recompute in concurrent threads ...
+            sha1hash = computeHash(bytes);
+            hc = sha1hash.hashCode();
+        }
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public long getSize() {
         return this.bytes.length;
     }
@@ -66,6 +82,7 @@
         if (sha1hash == null) {
             // Idempotent, so doesn't matter if we recompute in concurrent threads ...
             sha1hash = computeHash(bytes);
+            hc = sha1hash.hashCode();
         }
         return sha1hash;
     }

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-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -24,6 +24,7 @@
 package org.jboss.dna.jcr;
 
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Iterator;
@@ -57,9 +58,12 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
+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.ValueFactories;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.ChildNode;
@@ -118,6 +122,40 @@
         }
     }
 
+    final JcrValue valueFrom( int propertyType,
+                              Object value ) {
+        return new JcrValue(cache.factories(), cache, propertyType, value);
+    }
+
+    final JcrValue valueFrom( Calendar value ) {
+        ValueFactories factories = cache.factories();
+        DateTime dateTime = factories.getDateFactory().create(value);
+        return new JcrValue(factories, cache, PropertyType.DATE, dateTime);
+    }
+
+    final JcrValue valueFrom( InputStream value ) {
+        ValueFactories factories = cache.factories();
+        Binary binary = factories.getBinaryFactory().create(value);
+        return new JcrValue(factories, cache, PropertyType.DATE, binary);
+    }
+
+    final JcrValue valueFrom( Node value ) throws UnsupportedRepositoryOperationException, RepositoryException {
+        ValueFactories factories = cache.factories();
+        String uuid = factories.getStringFactory().create(value.getUUID());
+        return new JcrValue(factories, cache, PropertyType.REFERENCE, uuid);
+    }
+
+    final JcrValue[] valuesFrom( int propertyType,
+                                 Object[] values ) {
+        int len = values.length;
+        ValueFactories factories = cache.factories();
+        JcrValue[] results = new JcrValue[values.length];
+        for (int i = 0; i != len; ++i) {
+            results[i] = new JcrValue(factories, cache, propertyType, values[i]);
+        }
+        return results;
+    }
+
     @Override
     Path path() throws RepositoryException {
         return cache.getPathFor(nodeInfo());
@@ -672,159 +710,184 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, boolean)
      */
     public final Property setProperty( String name,
-                                       boolean value ) {
-        throw new UnsupportedOperationException();
+                                       boolean value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.BOOLEAN, value)));
+
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.util.Calendar)
      */
     public final Property setProperty( String name,
-                                       Calendar value ) {
-        throw new UnsupportedOperationException();
+                                       Calendar value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
+
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, double)
      */
     public final Property setProperty( String name,
-                                       double value ) {
-        throw new UnsupportedOperationException();
+                                       double value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.DOUBLE, value)));
+
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.io.InputStream)
      */
     public final Property setProperty( String name,
-                                       InputStream value ) {
-        throw new UnsupportedOperationException();
+                                       InputStream value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, long)
      */
     public final Property setProperty( String name,
-                                       long value ) {
-        throw new UnsupportedOperationException();
+                                       long value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.LONG, value)));
+
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Node)
      */
     public final Property setProperty( String name,
-                                       Node value ) {
-        throw new UnsupportedOperationException();
+                                       Node value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String)
      */
     public final Property setProperty( String name,
-                                       String value ) {
-        throw new UnsupportedOperationException();
+                                       String value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String, int)
      */
     public final Property setProperty( String name,
                                        String value,
-                                       int type ) {
-        throw new UnsupportedOperationException();
+                                       int type )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(type, value)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String[])
      */
     public final Property setProperty( String name,
-                                       String[] values ) {
-        throw new UnsupportedOperationException();
+                                       String[] values )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String[], int)
      */
     public final Property setProperty( String name,
                                        String[] values,
-                                       int type ) {
-        throw new UnsupportedOperationException();
+                                       int type )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(type, values)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value)
      */
     public final Property setProperty( String name,
-                                       Value value ) {
-        throw new UnsupportedOperationException();
+                                       Value value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), (JcrValue)value));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value, int)
      */
     public final Property setProperty( String name,
                                        Value value,
-                                       int type ) {
-        throw new UnsupportedOperationException();
+                                       int type )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type)));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value[])
      */
     public final Property setProperty( String name,
-                                       Value[] values ) {
-        throw new UnsupportedOperationException();
+                                       Value[] values )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), values));
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value[], int)
      */
     public final Property setProperty( String name,
                                        Value[] values,
-                                       int type ) {
-        throw new UnsupportedOperationException();
+                                       int type )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        int len = values.length;
+        Value[] newValues = null;
+        if (len == 0) {
+            newValues = JcrMultiValueProperty.EMPTY_VALUES;
+        } else {
+            List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
+            for (int i = 0; i != len; ++i) {
+                Value value = values[i];
+                if (value == null) continue;
+                if (value.getType() != type) {
+                    value = ((JcrValue)value).asType(type);
+                }
+                valuesWithDesiredType.add(value);
+            }
+            if (valuesWithDesiredType.isEmpty()) {
+                newValues = JcrMultiValueProperty.EMPTY_VALUES;
+            } else {
+                newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
+            }
+        }
+        // Set the value, perhaps to an empty array ...
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), newValues));
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -23,17 +23,23 @@
  */
 package org.jboss.dna.jcr;
 
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.VersionException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.PropertyInfo;
 
 /**
@@ -51,6 +57,12 @@
         this.propertyId = propertyId;
     }
 
+    final NodeEditor editor() throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+        return cache.getEditorFor(propertyId.getNodeId());
+    }
+
+    abstract boolean isMultiple();
+
     /**
      * {@inheritDoc}
      * 
@@ -179,11 +191,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Item#remove()
      */
-    public void remove() {
-        throw new UnsupportedOperationException();
+    public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
+        editor().removeProperty(propertyId.getPropertyName());
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -24,8 +24,10 @@
 package org.jboss.dna.jcr;
 
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Iterator;
+import java.util.List;
 import javax.jcr.Node;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -44,6 +46,8 @@
 @NotThreadSafe
 final class JcrMultiValueProperty extends AbstractJcrProperty {
 
+    static final JcrValue[] EMPTY_VALUES = new JcrValue[] {};
+
     JcrMultiValueProperty( SessionCache cache,
                            PropertyId propertyId ) {
         super(cache, propertyId);
@@ -52,6 +56,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.AbstractJcrProperty#isMultiple()
+     */
+    @Override
+    boolean isMultiple() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws ValueFormatException always
      * @see javax.jcr.Property#getBoolean()
      */
@@ -170,16 +184,19 @@
      */
     public final void setValue( Value[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        JcrValue[] jcrValues = null;
+        Value[] newValues = null;
         if (values != null && values.length != 0) {
             int numValues = values.length;
-            jcrValues = new JcrValue[numValues];
+            List<Value> valuesList = new ArrayList<Value>(numValues);
             ValueFactory<?> factory = null;
             for (int i = 0; i != numValues; ++i) {
                 Value value = values[i];
-                if (value instanceof JcrValue) {
+                if (value == null) {
+                    // skip null values ...
+                    continue;
+                } else if (value instanceof JcrValue) {
                     // just use the value ...
-                    jcrValues[i] = (JcrValue)value;
+                    valuesList.add(value);
                 } else {
                     // This isn't our implementation, so create one for use
                     if (factory == null) {
@@ -219,11 +236,18 @@
                         default:
                             throw new RepositoryException();
                     }
-                    jcrValues[i] = createValue(factory.create(data), type);
+                    valuesList.add(createValue(factory.create(data), type));
                 }
             }
+            if (valuesList.isEmpty()) {
+                newValues = EMPTY_VALUES;
+            } else {
+                newValues = valuesList.toArray(new Value[valuesList.size()]);
+            }
+        } else {
+            newValues = EMPTY_VALUES;
         }
-        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues);
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), newValues);
     }
 
     /**
@@ -233,13 +257,23 @@
      */
     public final void setValue( String[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        JcrValue[] jcrValues = null;
+        Value[] jcrValues = null;
         if (values != null && values.length != 0) {
             int numValues = values.length;
+            List<Value> valuesList = new ArrayList<Value>(numValues);
             jcrValues = new JcrValue[numValues];
             for (int i = 0; i != numValues; ++i) {
-                jcrValues[i] = createValue(values[i], PropertyType.STRING);
+                String value = values[i];
+                if (value == null) continue; // skip null values
+                valuesList.add(createValue(values[i], PropertyType.STRING));
             }
+            if (valuesList.isEmpty()) {
+                jcrValues = EMPTY_VALUES;
+            } else {
+                jcrValues = valuesList.toArray(new Value[valuesList.size()]);
+            }
+        } else {
+            jcrValues = EMPTY_VALUES;
         }
         cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues);
     }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -531,7 +531,8 @@
 
             Node node = null;
             try {
-                node = cache.findJcrNode((UUID)jcrValue.value());
+                UUID uuid = cache.factories().getUuidFactory().create(jcrValue.value());
+                node = cache.findJcrNode(uuid);
             } catch (RepositoryException re) {
                 return false;
             }
@@ -643,9 +644,9 @@
 
         public boolean matches( Value valueToMatch ) {
             assert valueToMatch instanceof JcrValue;
-            
-            JcrValue jcrValue = (JcrValue) valueToMatch;
 
+            JcrValue jcrValue = (JcrValue)valueToMatch;
+
             /*
              * Need two path factories here.  One uses the permanent namespace mappings to parse the constraints.
              * The other also looks at the transient mappings to parse the checked value
@@ -658,7 +659,7 @@
             for (int i = 0; i < constraints.length; i++) {
                 boolean matchesDescendants = constraints[i].endsWith("*");
                 Path constraintPath = repoPathFactory.create(matchesDescendants ? constraints[i].substring(0,
-                                                                                                       constraints[i].length() - 2) : constraints[i]);
+                                                                                                           constraints[i].length() - 2) : constraints[i]);
 
                 if (matchesDescendants && value.isDecendantOf(constraintPath)) {
                     return true;

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-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -52,6 +52,8 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+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;
@@ -454,15 +456,18 @@
             }
 
             public Value createValue( Node value ) throws RepositoryException {
-                return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
+                String uuid = valueFactories.getStringFactory().create(value.getUUID());
+                return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, uuid);
             }
 
             public Value createValue( InputStream value ) {
-                return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, value);
+                Binary binary = valueFactories.getBinaryFactory().create(value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, binary);
             }
 
             public Value createValue( Calendar value ) {
-                return new JcrValue(valueFactories, sessionCache, PropertyType.DATE, value);
+                DateTime dateTime = valueFactories.getDateFactory().create(value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.DATE, dateTime);
             }
 
             public Value createValue( boolean value ) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -51,6 +51,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.AbstractJcrProperty#isMultiple()
+     */
+    @Override
+    boolean isMultiple() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see javax.jcr.Property#getBoolean()
      */
     public boolean getBoolean() throws RepositoryException {
@@ -184,6 +194,11 @@
             cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValue);
             return;
         }
+        if (value == null) {
+            // Then we're to delete the property ...
+            cache.getEditorFor(propertyId.getNodeId()).removeProperty(propertyId.getPropertyName());
+            return;
+        }
         // We have to convert from one Value implementation to ours ...
         switch (value.getType()) {
             case PropertyType.STRING:
@@ -220,6 +235,7 @@
 
     protected void setValue( JcrValue jcrValue )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        assert jcrValue != null;
         cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValue);
     }
 
@@ -230,6 +246,10 @@
      */
     public void setValue( String value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            this.remove();
+            return;
+        }
         setValue(createValue(value, PropertyType.STRING));
     }
 
@@ -240,6 +260,10 @@
      */
     public void setValue( InputStream value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            this.remove();
+            return;
+        }
         setValue(createValue(context().getValueFactories().getBinaryFactory().create(value), PropertyType.DATE));
     }
 
@@ -270,6 +294,10 @@
      */
     public void setValue( Calendar value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            this.remove();
+            return;
+        }
         setValue(createValue(context().getValueFactories().getDateFactory().create(value), PropertyType.DATE));
     }
 
@@ -290,6 +318,10 @@
      */
     public void setValue( Node value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            this.remove();
+            return;
+        }
         String uuid = value.getUUID();
         setValue(createValue(uuid, PropertyType.REFERENCE));
     }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Calendar;
 import javax.jcr.Node;
@@ -31,9 +32,16 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.util.IoUtil;
 import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.BinaryFactory;
+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.NameFactory;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactories;
 
 /**
@@ -211,6 +219,104 @@
         return type;
     }
 
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        // Use the value's hash code
+        return value.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof JcrValue) {
+            JcrValue that = (JcrValue)obj;
+            if (this.type != that.type) return false;
+            try {
+                switch (this.type) {
+                    case PropertyType.STRING:
+                        return this.getString().equals(that.getString());
+                    case PropertyType.BINARY:
+                        BinaryFactory binaryFactory = valueFactories.getBinaryFactory();
+                        Binary thisValue = binaryFactory.create(this.value);
+                        Binary thatValue = binaryFactory.create(that.value);
+                        return thisValue.equals(thatValue);
+                    case PropertyType.BOOLEAN:
+                        return this.getBoolean() == that.getBoolean();
+                    case PropertyType.DOUBLE:
+                        return this.getDouble() == that.getDouble();
+                    case PropertyType.LONG:
+                        return this.getLong() == that.getLong();
+                    case PropertyType.DATE:
+                        DateTimeFactory dateFactory = valueFactories.getDateFactory();
+                        DateTime thisDateValue = dateFactory.create(this.value);
+                        DateTime thatDateValue = dateFactory.create(that.value);
+                        return thisDateValue.equals(thatDateValue);
+                    case PropertyType.PATH:
+                        PathFactory pathFactory = valueFactories.getPathFactory();
+                        Path thisPathValue = pathFactory.create(this.value);
+                        Path thatPathValue = pathFactory.create(that.value);
+                        return thisPathValue.equals(thatPathValue);
+                    case PropertyType.NAME:
+                        NameFactory nameFactory = valueFactories.getNameFactory();
+                        Name thisNameValue = nameFactory.create(this.value);
+                        Name thatNameValue = nameFactory.create(that.value);
+                        return thisNameValue.equals(thatNameValue);
+                    case PropertyType.REFERENCE:
+                        return this.getString().equals(that.getString());
+                    default:
+                        throw new SystemFailureException();
+                }
+            } catch (RepositoryException e) {
+                return false;
+            }
+            // will not get here
+        }
+        if (obj instanceof Value) {
+            Value that = (Value)obj;
+            if (this.type != that.getType()) return false;
+            try {
+                switch (this.type) {
+                    case PropertyType.STRING:
+                        return this.getString().equals(that.getString());
+                    case PropertyType.BINARY:
+                        return IoUtil.isSame(this.getStream(), that.getStream());
+                    case PropertyType.BOOLEAN:
+                        return this.getBoolean() == that.getBoolean();
+                    case PropertyType.DOUBLE:
+                        return this.getDouble() == that.getDouble();
+                    case PropertyType.LONG:
+                        return this.getLong() == that.getLong();
+                    case PropertyType.DATE:
+                        return this.getDate().equals(that.getDate());
+                    case PropertyType.PATH:
+                        return this.getString().equals(that.getString());
+                    case PropertyType.NAME:
+                        return this.getString().equals(that.getString());
+                    case PropertyType.REFERENCE:
+                        return this.getString().equals(that.getString());
+                    default:
+                        throw new SystemFailureException();
+                }
+            } catch (IOException e) {
+                return false;
+            } catch (RepositoryException e) {
+                return false;
+            }
+            // will not get here
+        }
+        return false;
+    }
+
     private JcrValue withTypeAndValue( int type,
                                        Object value ) {
         return new JcrValue(this.valueFactories, this.sessionCache, type, value);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -127,6 +127,7 @@
     private final JcrSession session;
     private final String workspaceName;
     protected final ExecutionContext context;
+    protected final ValueFactories factories;
     protected final PathFactory pathFactory;
     protected final NameFactory nameFactory;
     protected final ValueFactory<String> stringFactory;
@@ -162,8 +163,9 @@
         this.workspaceName = workspaceName;
         this.store = store;
         this.context = context;
-        this.pathFactory = context.getValueFactories().getPathFactory();
-        this.nameFactory = context.getValueFactories().getNameFactory();
+        this.factories = context.getValueFactories();
+        this.pathFactory = this.factories.getPathFactory();
+        this.nameFactory = this.factories.getNameFactory();
         this.stringFactory = context.getValueFactories().getStringFactory();
         this.namespaces = context.getNamespaceRegistry();
         this.propertyFactory = context.getPropertyFactory();
@@ -196,6 +198,10 @@
         return context;
     }
 
+    ValueFactories factories() {
+        return factories;
+    }
+
     PathFactory pathFactory() {
         return pathFactory;
     }
@@ -519,11 +525,12 @@
          * 
          * @param name the property name; may not be null
          * @param value the new property values; may not be null
+         * @return the identifier for the property; never null
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
          */
-        public void setProperty( Name name,
-                                 JcrValue value ) throws ConstraintViolationException {
+        public PropertyId setProperty( Name name,
+                                       JcrValue value ) throws ConstraintViolationException {
             assert name != null;
             assert value != null;
             JcrPropertyDefinition definition = null;
@@ -575,7 +582,7 @@
             } else {
                 // A cast is required ...
                 org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
-                ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+                ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
                 objValue = factory.create(value);
             }
             Property dnaProp = propertyFactory.create(name, objValue);
@@ -584,8 +591,9 @@
             PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
 
             // Finally update the cached information and record the change ...
-            node.setProperty(newProperty, context().getValueFactories());
+            node.setProperty(newProperty, factories());
             operations.set(dnaProp).on(currentLocation);
+            return newProperty.getPropertyId();
         }
 
         /**
@@ -594,16 +602,16 @@
          * 
          * @param name the property name; may not be null
          * @param values new property values, all of which must have the same {@link Value#getType() property type}; may not be
-         *        null or empty
+         *        null but may be empty
+         * @return the identifier for the property; never null
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
          */
-        public void setProperty( Name name,
-                                 JcrValue[] values ) throws ConstraintViolationException {
+        public PropertyId setProperty( Name name,
+                                       Value[] values ) throws ConstraintViolationException {
             assert name != null;
             assert values != null;
             int numValues = values.length;
-            assert numValues != 0;
             JcrPropertyDefinition definition = null;
             PropertyId id = null;
 
@@ -622,6 +630,7 @@
                     if (numValues == 0) {
                         // Just use the definition as is ...
                     } else {
+                        // Use the property type for the first non-null value ...
                         int type = values[0].getType();
                         if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != type) {
                             // The property type is not right, so we have to check if we can cast.
@@ -656,15 +665,15 @@
                 // Can use the values as is ...
                 propertyType = type;
                 for (int i = 0; i != numValues; ++i) {
-                    objValues[i] = values[i].value();
+                    objValues[i] = ((JcrValue)values[i]).value();
                 }
             } else {
                 // A cast is required ...
                 assert propertyType != type;
                 org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
-                ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+                ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
                 for (int i = 0; i != numValues; ++i) {
-                    objValues[i] = factory.create(values[i].value());
+                    objValues[i] = factory.create(((JcrValue)values[i]).value());
                 }
             }
             Property dnaProp = propertyFactory.create(name, objValues);
@@ -673,8 +682,9 @@
             PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
 
             // Finally update the cached information and record the change ...
-            node.setProperty(newProperty, context().getValueFactories());
+            node.setProperty(newProperty, factories());
             operations.set(dnaProp).on(currentLocation);
+            return newProperty.getPropertyId();
         }
 
         /**
@@ -739,8 +749,8 @@
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    JcrValue value = new JcrValue(context().getValueFactories(), SessionCache.this, PropertyType.STRING,
-                                                  definition.getId().getString());
+                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
+                                                                                                                 .getString());
                     setProperty(DnaLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
@@ -932,12 +942,15 @@
                                                                int propertyType,
                                                                boolean skipProtected ) {
         JcrPropertyDefinition definition = null;
+        if (propertyType == PropertyType.UNDEFINED) {
+            propertyType = PropertyTypeUtil.jcrPropertyTypeFor(dnaProperty);
+        }
 
         // If single-valued ...
         if (dnaProperty.isSingle()) {
             // Create a value for the DNA property value ...
             Object value = dnaProperty.getFirstValue();
-            Value jcrValue = new JcrValue(context().getValueFactories(), SessionCache.this, propertyType, value);
+            Value jcrValue = new JcrValue(factories(), SessionCache.this, propertyType, value);
             definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
                                                             mixinTypeNamesOfParent,
                                                             dnaProperty.getName(),
@@ -949,7 +962,7 @@
             Value[] jcrValues = new Value[dnaProperty.size()];
             int index = 0;
             for (Object value : dnaProperty) {
-                jcrValues[index++] = new JcrValue(context().getValueFactories(), SessionCache.this, propertyType, value);
+                jcrValues[index++] = new JcrValue(factories(), SessionCache.this, propertyType, value);
             }
             definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
                                                             mixinTypeNamesOfParent,
@@ -1355,7 +1368,6 @@
                                                   NodeInfo parentInfo ) throws RepositoryException {
         // Now get the DNA node's UUID and find the DNA property containing the UUID ...
         Location location = graphNode.getLocation();
-        ValueFactories factories = context.getValueFactories();
         UUID uuid = location.getUuid();
         org.jboss.dna.graph.property.Property uuidProperty = null;
         if (uuid != null) {
@@ -1537,7 +1549,7 @@
         // Now add the "jcr:uuid" property if and only if referenceable ...
         if (referenceable) {
             // We know that this property is single-valued
-            JcrValue value = new JcrValue(context.getValueFactories(), this, PropertyType.STRING, uuid);
+            JcrValue value = new JcrValue(factories(), this, PropertyType.STRING, uuid);
             PropertyDefinition propertyDefinition = nodeTypes().findPropertyDefinition(primaryTypeName,
                                                                                        mixinTypeNames,
                                                                                        JcrLexicon.UUID,

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -29,9 +29,7 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
-import java.io.InputStream;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.List;
 import java.util.UUID;
 import javax.jcr.Item;
@@ -729,76 +727,6 @@
         node.restoreByLabel(null, false);
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetBooleanProperty() throws Exception {
-        node.setProperty(null, false);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetCalendarProperty() throws Exception {
-        node.setProperty(null, (Calendar)null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetDoubleProperty() throws Exception {
-        node.setProperty(null, 0.0);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetInputStreamProperty() throws Exception {
-        node.setProperty(null, (InputStream)null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetLongProperty() throws Exception {
-        node.setProperty(null, 0L);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetNodeProperty() throws Exception {
-        node.setProperty(null, (Node)null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringProperty() throws Exception {
-        node.setProperty(null, (String)null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringsProperty() throws Exception {
-        node.setProperty(null, (String[])null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValueProperty() throws Exception {
-        node.setProperty(null, (Value)null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValuesProperty() throws Exception {
-        node.setProperty(null, (Value[])null);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringPropertyWithType() throws Exception {
-        node.setProperty(null, (String)null, 0);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringsPropertyWithType() throws Exception {
-        node.setProperty(null, (String[])null, 0);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValuePropertyWithType() throws Exception {
-        node.setProperty(null, (Value)null, 0);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValuesPropertyWithType() throws Exception {
-        node.setProperty(null, (Value[])null, 0);
-    }
-
     @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotAllowUnlock() throws Exception {
         node.unlock();

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -286,6 +286,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.AbstractJcrProperty#isMultiple()
+         */
+        @Override
+        boolean isMultiple() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see javax.jcr.Property#getNode()
          */
         @SuppressWarnings( "synthetic-access" )

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 00:03:04 UTC (rev 802)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 18:52:26 UTC (rev 803)
@@ -37,7 +37,13 @@
 import org.apache.jackrabbit.test.RepositoryStub;
 import org.apache.jackrabbit.test.api.AddNodeTest;
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
+import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
+import org.apache.jackrabbit.test.api.SetValueBooleanTest;
+import org.apache.jackrabbit.test.api.SetValueDateTest;
+import org.apache.jackrabbit.test.api.SetValueDoubleTest;
+import org.apache.jackrabbit.test.api.SetValueLongTest;
+import org.apache.jackrabbit.test.api.SetValueReferenceTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -152,14 +158,14 @@
             // addTestSuite(NodeTest.class);
             // addTestSuite(NodeUUIDTest.class);
             // addTestSuite(NodeOrderableChildNodesTest.class);
-            // addTestSuite(PropertyTest.class);
+            addTestSuite(PropertyTest.class);
             //
             // addTestSuite(SetValueBinaryTest.class);
-            // addTestSuite(SetValueBooleanTest.class);
-            // addTestSuite(SetValueDateTest.class);
-            // addTestSuite(SetValueDoubleTest.class);
-            // addTestSuite(SetValueLongTest.class);
-            // addTestSuite(SetValueReferenceTest.class);
+            addTestSuite(SetValueBooleanTest.class);
+            addTestSuite(SetValueDateTest.class);
+            addTestSuite(SetValueDoubleTest.class);
+            addTestSuite(SetValueLongTest.class);
+            addTestSuite(SetValueReferenceTest.class);
             // addTestSuite(SetValueStringTest.class);
             // addTestSuite(SetValueConstraintViolationExceptionTest.class);
             // addTestSuite(SetValueValueFormatExceptionTest.class);



From dna-commits at lists.jboss.org  Thu Apr  2 15:02:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 02 Apr 2009 15:02:06 -0400
Subject: [dna-commits] DNA SVN: r804 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LpSAw-0004FK-N1@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-02 15:02:05 -0400 (Thu, 02 Apr 2009)
New Revision: 804

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
Log:
DNA-340 Projection of Node Types onto System View

Applied the patch, which stores the node type information in the repository graph, with one exception.  For various reasons (including those related to performance of running so many TCK unit tests), we want to be able to have a JcrRepository configuration property that says whether the node types should be stored in the graph.  So, the "batch.execute()" line was commented out at the end of the RepositoryNodeTypeManager.projectOnto(Graph,Path) method.  This essentially prevents the node types from being added, but still ensures that the "/jcr:system/dna:nodeTypes" node exists.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -63,6 +63,14 @@
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
+        JcrNodeType nodeType = findType(JcrNtLexicon.NODE_TYPE);
+
+        if (nodeType == null) {
+            String baseTypeName = JcrNtLexicon.NODE_TYPE.getString(context.getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.NODE_TYPES.getString(context.getNamespaceRegistry());
+            throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+        }
+
         // Stubbing in child node and property definitions for now
         JcrNodeType namespace = new JcrNodeType(
                                                 context,
@@ -104,25 +112,39 @@
                                                                                                               new JcrNodeType[] {namespace})}),
                                                  NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType system = new JcrNodeType(
-                                             context,
-                                             NO_NODE_TYPE_MANAGER,
-                                             DnaLexicon.SYSTEM,
-                                             Arrays.asList(new JcrNodeType[] {base}),
-                                             NO_PRIMARY_ITEM_NAME,
-                                             Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                          context,
-                                                                                                          null,
-                                                                                                          DnaLexicon.NAMESPACES,
-                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                                                          true,
-                                                                                                          true,
-                                                                                                          true,
-                                                                                                          false,
-                                                                                                          DnaLexicon.NAMESPACES,
-                                                                                                          new JcrNodeType[] {namespaces})}),
-                                             NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        JcrNodeType dnaNodeTypes = new JcrNodeType(
+                                                   context,
+                                                   NO_NODE_TYPE_MANAGER,
+                                                   DnaLexicon.NODE_TYPES,
+                                                   Arrays.asList(new JcrNodeType[] {base}),
+                                                   NO_PRIMARY_ITEM_NAME,
+                                                   Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                context,
+                                                                                                                null,
+                                                                                                                null,
+                                                                                                                OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                false,
+                                                                                                                false,
+                                                                                                                true,
+                                                                                                                true,
+                                                                                                                JcrNtLexicon.NODE_TYPE,
+                                                                                                                new JcrNodeType[] {nodeType})}),
+                                                   NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
+        JcrNodeType system = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.SYSTEM,
+                                             Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME,
+                                             Arrays.asList(new JcrNodeDefinition[] {
+                                                 new JcrNodeDefinition(context, null, DnaLexicon.NAMESPACES,
+                                                                       OnParentVersionBehavior.VERSION.getJcrValue(), true, true,
+                                                                       true, false, DnaLexicon.NAMESPACES,
+                                                                       new JcrNodeType[] {namespaces}),
+
+                                                 new JcrNodeDefinition(context, null, JcrLexicon.NODE_TYPES,
+                                                                       OnParentVersionBehavior.VERSION.getJcrValue(), true, true,
+                                                                       true, false, DnaLexicon.NODE_TYPES,
+                                                                       new JcrNodeType[] {dnaNodeTypes})}), NO_PROPERTIES,
+                                             NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
         JcrNodeType root = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.ROOT, Arrays.asList(new JcrNodeType[] {base,
             referenceable}), NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
             new JcrNodeDefinition(context, null, JcrLexicon.SYSTEM, OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
@@ -137,8 +159,7 @@
                                       NO_DEFAULT_VALUES, PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}), NOT_MIXIN,
                                            ORDERABLE_CHILD_NODES);
 
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, dnaNodeTypes, namespaces, namespace,}));
 
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -33,6 +33,7 @@
 
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
+    public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");
     public static final Name ROOT = new BasicName(Namespace.URI, "root");
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
     public static final Name URI = new BasicName(Namespace.URI, "uri");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -55,6 +55,7 @@
     public static final Name MERGE_FAILED = new BasicName(Namespace.URI, "mergeFailed");
     public static final Name MULTIPLE = new BasicName(Namespace.URI, "multiple");
     public static final Name NAME = new BasicName(Namespace.URI, "name");
+    public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");
     public static final Name NODE_TYPE_NAME = new BasicName(Namespace.URI, "nodeTypeName");
     public static final Name ON_PARENT_VERSION = new BasicName(Namespace.URI, "onParentVersion");
     public static final Name PREDECESSORS = new BasicName(Namespace.URI, "predecessors");

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-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -110,7 +110,7 @@
      * Reference to the JCR query manager for this workspace.
      */
     private final JcrQueryManager queryManager;
-    
+
     /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
@@ -153,8 +153,14 @@
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
 
         // This must be initialized after the session
-        this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repository.getRepositoryTypeManager());
+        RepositoryNodeTypeManager repoTypeManager = repository.getRepositoryTypeManager();
+        this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repoTypeManager);
         this.queryManager = new JcrQueryManager(this.session);
+
+        Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(root,
+                                                                                     JcrLexicon.SYSTEM,
+                                                                                     JcrLexicon.NODE_TYPES);
+        repoTypeManager.projectOnto(this.graph, parentOfTypeNodes);
     }
 
     final String getSourceName() {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -30,12 +30,22 @@
 import java.util.List;
 import java.util.Map;
 import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.OnParentVersionAction;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.XmlNameEncoder;
 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.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
 
 /**
  * The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
@@ -50,12 +60,19 @@
 @Immutable
 class RepositoryNodeTypeManager {
 
+    private final ExecutionContext context;
     private final Map<Name, JcrNodeType> nodeTypes;
     private final Map<PropertyDefinitionId, JcrPropertyDefinition> propertyDefinitions;
     private final Map<NodeDefinitionId, JcrNodeDefinition> childNodeDefinitions;
+    private final PropertyFactory propertyFactory;
+    private final PathFactory pathFactory;
 
     RepositoryNodeTypeManager( ExecutionContext context,
                                JcrNodeTypeSource source ) {
+        this.context = context;
+        this.propertyFactory = context.getPropertyFactory();
+        this.pathFactory = context.getValueFactories().getPathFactory();
+
         Collection<JcrNodeType> types = source.getNodeTypes();
         propertyDefinitions = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
         childNodeDefinitions = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
@@ -78,21 +95,21 @@
 
     public Collection<JcrNodeType> getMixinNodeTypes() {
         List<JcrNodeType> types = new ArrayList<JcrNodeType>(nodeTypes.size());
-        
+
         for (JcrNodeType nodeType : nodeTypes.values()) {
             if (nodeType.isMixin()) types.add(nodeType);
         }
-        
+
         return types;
     }
 
     public Collection<JcrNodeType> getPrimaryNodeTypes() {
         List<JcrNodeType> types = new ArrayList<JcrNodeType>(nodeTypes.size());
-        
+
         for (JcrNodeType nodeType : nodeTypes.values()) {
             if (!nodeType.isMixin()) types.add(nodeType);
         }
-        
+
         return types;
     }
 
@@ -594,4 +611,211 @@
                                                                                       skipProtected);
         return false;
     }
+
+    /**
+     * Projects the node types onto the provided graph under the location of <code>parentOfTypeNodes</code>. If no node currently
+     * exists at that path, one will be created and assigned a primary type of {@code DnaLexicon.NODE_TYPES}.
+     * <p>
+     * All node creation is performed through the graph layer. If the primary type of the node at <code>parentOfPathNodes</code>
+     * does not contain a residual definition that allows child nodes of type <code>nt:nodeType</code>, this method will create
+     * nodes for which the JCR layer cannot determine the corresponding node definition. This WILL corrupt the graph from a JCR
+     * standpoint and make it unusable through the DNA JCR layer.
+     * </p>
+     * <p>
+     * For each node type, a node is created as a child node of <code>parentOfPathNodes</code>. The created node has a name that
+     * corresponds to the node types name and a primary type of <code>nt:nodeType</code>. All other properties and child nodes for
+     * the newly created node are added in a manner consistent with the guidance provide in section 6.7.22 of the JCR 1.0
+     * specification and section 4.7.24 of the (draft) JCR 2.0 specification where possible.
+     * </p>
+     * 
+     * @param graph the graph onto which the type information should be projected
+     * @param parentOfTypeNodes the path under which the type information should be projected
+     */
+    void projectOnto( Graph graph,
+                      Path parentOfTypeNodes ) {
+        assert graph != null;
+        assert parentOfTypeNodes != null;
+
+        // Make sure that the parent of the type nodes exists in the graph.
+        try {
+            graph.getNodeAt(parentOfTypeNodes);
+        } catch (PathNotFoundException pnfe) {
+            PropertyFactory propertyFactory = context.getPropertyFactory();
+            graph.create(parentOfTypeNodes,
+                         propertyFactory.create(JcrLexicon.PRIMARY_TYPE,
+                                                DnaLexicon.NODE_TYPES.getString(context.getNamespaceRegistry())));
+        }
+
+        Graph.Batch batch = graph.batch();
+
+        for (JcrNodeType nodeType : nodeTypes.values()) {
+            projectNodeTypeOnto(nodeType, parentOfTypeNodes, batch);
+        }
+
+        // TODO: Add back in; see DNA-340 ...
+        // batch.execute();
+    }
+
+    /**
+     * Projects the node types onto the provided graph under the location of <code>parentOfTypeNodes</code>. The operations needed
+     * to create the node (and any child nodes or properties) will be added to the batch specified in <code>batch</code>.
+     * 
+     * @param nodeType the node type to be projected
+     * @param parentOfTypeNodes the path under which the type information should be projected
+     * @param batch the batch to which any required graph modification operations should be added
+     * @see #projectOnto(Graph, Path)
+     */
+    private void projectNodeTypeOnto( JcrNodeType nodeType,
+                                      Path parentOfTypeNodes,
+                                      Graph.Batch batch ) {
+        assert nodeType != null;
+        assert parentOfTypeNodes != null;
+        assert batch != null;
+
+        Path nodeTypePath = pathFactory.create(parentOfTypeNodes, nodeType.getInternalName());
+
+        NodeType[] supertypes = nodeType.getDeclaredSupertypes();
+        List<Name> supertypeNames = new ArrayList<Name>(supertypes.length);
+        for (int i = 0; i < supertypes.length; i++) {
+            supertypeNames.add(((JcrNodeType)supertypes[i]).getInternalName());
+        }
+
+        List<Property> propsList = new ArrayList<Property>();
+        propsList.add(propertyFactory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.NODE_TYPE));
+        propsList.add(propertyFactory.create(JcrLexicon.IS_MIXIN, nodeType.isMixin()));
+
+        if (nodeType.getPrimaryItemName() != null) {
+            propsList.add(propertyFactory.create(JcrLexicon.PRIMARY_ITEM_NAME, nodeType.getPrimaryItemName()));
+        }
+
+        propsList.add(propertyFactory.create(JcrLexicon.NODE_TYPE_NAME, nodeType.getName()));
+        propsList.add(propertyFactory.create(JcrLexicon.HAS_ORDERABLE_CHILD_NODES, nodeType.hasOrderableChildNodes()));
+        propsList.add(propertyFactory.create(JcrLexicon.SUPERTYPES, supertypeNames));
+
+        batch.create(nodeTypePath).with(propsList).and();
+
+        PropertyDefinition[] propertyDefs = nodeType.getDeclaredPropertyDefinitions();
+        for (int i = 0; i < propertyDefs.length; i++) {
+            projectPropertyDefinitionOnto(propertyDefs[i], nodeTypePath, batch);
+        }
+
+        NodeDefinition[] childNodeDefs = nodeType.getDeclaredChildNodeDefinitions();
+        for (int i = 0; i < childNodeDefs.length; i++) {
+            projectChildNodeDefinitionOnto(childNodeDefs[i], nodeTypePath, batch);
+        }
+
+    }
+
+    /**
+     * Projects a single property definition onto the provided graph under the location of <code>nodeTypePath</code>. The
+     * operations needed to create the property definition and any of its properties will be added to the batch specified in
+     * <code>batch</code>.
+     * <p>
+     * All node creation is performed through the graph layer. If the primary type of the node at <code>nodeTypePath</code> does
+     * not contain a residual definition that allows child nodes of type <code>nt:propertyDefinition</code>, this method creates
+     * nodes for which the JCR layer cannot determine the corresponding node definition. This WILL corrupt the graph from a JCR
+     * standpoint and make it unusable through the DNA JCR layer.
+     * </p>
+     * 
+     * @param propertyDef the property definition to be projected
+     * @param nodeTypePath the path under which the property definition should be projected
+     * @param batch the batch to which any required graph modification operations should be added
+     * @see #projectOnto(Graph, Path)
+     */
+    private void projectPropertyDefinitionOnto( PropertyDefinition propertyDef,
+                                                Path nodeTypePath,
+                                                Graph.Batch batch ) {
+        assert propertyDef != null;
+        assert nodeTypePath != null;
+        assert batch != null;
+
+        JcrPropertyDefinition jcrPropDef = (JcrPropertyDefinition)propertyDef;
+        String propName = jcrPropDef.getInternalName().getString(context.getNamespaceRegistry(), new XmlNameEncoder());
+        Path propDefPath = pathFactory.create(nodeTypePath, JcrLexicon.PROPERTY_DEFINITION);
+
+        List<Property> propsList = new ArrayList<Property>();
+        propsList.add(propertyFactory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.PROPERTY_DEFINITION));
+
+        if (!JcrNodeType.RESIDUAL_ITEM_NAME.equals(jcrPropDef.getName())) {
+            propsList.add(propertyFactory.create(JcrLexicon.NAME, propName));
+        }
+        propsList.add(propertyFactory.create(JcrLexicon.AUTO_CREATED, jcrPropDef.isAutoCreated()));
+        propsList.add(propertyFactory.create(JcrLexicon.MANDATORY, jcrPropDef.isMandatory()));
+        propsList.add(propertyFactory.create(JcrLexicon.MULTIPLE, jcrPropDef.isMultiple()));
+        propsList.add(propertyFactory.create(JcrLexicon.PROTECTED, jcrPropDef.isProtected()));
+        propsList.add(propertyFactory.create(JcrLexicon.ON_PARENT_VERSION,
+                                             OnParentVersionAction.nameFromValue(jcrPropDef.getOnParentVersion())));
+        propsList.add(propertyFactory.create(JcrLexicon.REQUIRED_TYPE, PropertyType.nameFromValue(jcrPropDef.getRequiredType())));
+
+        Value[] defaultValues = jcrPropDef.getDefaultValues();
+        if (defaultValues.length > 0) {
+            String[] defaultsAsString = new String[defaultValues.length];
+
+            for (int i = 0; i < defaultValues.length; i++) {
+                try {
+                    defaultsAsString[i] = defaultValues[i].getString();
+                } catch (RepositoryException re) {
+                    // Really shouldn't get here as all values are convertible to string
+                    throw new IllegalStateException(re);
+                }
+            }
+            propsList.add(propertyFactory.create(JcrLexicon.DEFAULT_VALUES, (Object[])defaultsAsString));
+        }
+
+        String[] valueConstraints = jcrPropDef.getValueConstraints();
+        if (valueConstraints.length > 0) {
+            propsList.add(propertyFactory.create(JcrLexicon.DEFAULT_VALUES, (Object[])valueConstraints));
+        }
+        batch.create(propDefPath).with(propsList).and();
+    }
+
+    /**
+     * Projects a single child node definition onto the provided graph under the location of <code>nodeTypePath</code>. The
+     * operations needed to create the child node definition and any of its properties will be added to the batch specified in
+     * <code>batch</code>.
+     * <p>
+     * All node creation is performed through the graph layer. If the primary type of the node at <code>nodeTypePath</code> does
+     * not contain a residual definition that allows child nodes of type <code>nt:childNodeDefinition</code>, this method creates
+     * nodes for which the JCR layer cannot determine the corresponding node definition. This WILL corrupt the graph from a JCR
+     * standpoint and make it unusable through the DNA JCR layer.
+     * </p>
+     * 
+     * @param childNodeDef the child node definition to be projected
+     * @param nodeTypePath the path under which the child node definition should be projected
+     * @param batch the batch to which any required graph modification operations should be added
+     * @see #projectOnto(Graph, Path)
+     */
+    private void projectChildNodeDefinitionOnto( NodeDefinition childNodeDef,
+                                                 Path nodeTypePath,
+                                                 Graph.Batch batch ) {
+        assert childNodeDef != null;
+        assert nodeTypePath != null;
+        assert batch != null;
+
+        JcrNodeDefinition jcrNodeDef = (JcrNodeDefinition)childNodeDef;
+        String nodeName = jcrNodeDef.getInternalName().getString(context.getNamespaceRegistry(), new XmlNameEncoder());
+        Path nodeDefPath = pathFactory.create(nodeTypePath, JcrLexicon.CHILD_NODE_DEFINITION);
+
+        List<Property> propsList = new ArrayList<Property>();
+        propsList.add(propertyFactory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.CHILD_NODE_DEFINITION));
+
+        if (!JcrNodeType.RESIDUAL_ITEM_NAME.equals(jcrNodeDef.getName())) {
+            propsList.add(propertyFactory.create(JcrLexicon.NAME, nodeName));
+        }
+
+        if (jcrNodeDef.getDefaultPrimaryType() != null) {
+            propsList.add(propertyFactory.create(JcrLexicon.DEFAULT_PRIMARY_TYPE, jcrNodeDef.getDefaultPrimaryType().getName()));
+        }
+
+        propsList.add(propertyFactory.create(JcrLexicon.REQUIRED_PRIMARY_TYPES, jcrNodeDef.getRequiredPrimaryTypeNames()));
+        propsList.add(propertyFactory.create(JcrLexicon.SAME_NAME_SIBLINGS, jcrNodeDef.allowsSameNameSiblings()));
+        propsList.add(propertyFactory.create(JcrLexicon.ON_PARENT_VERSION,
+                                             OnParentVersionAction.nameFromValue(jcrNodeDef.getOnParentVersion())));
+        propsList.add(propertyFactory.create(JcrLexicon.AUTO_CREATED, jcrNodeDef.isAutoCreated()));
+        propsList.add(propertyFactory.create(JcrLexicon.MANDATORY, jcrNodeDef.isMandatory()));
+        propsList.add(propertyFactory.create(JcrLexicon.PROTECTED, jcrNodeDef.isProtected()));
+
+        batch.create(nodeDefPath).with(propsList).and();
+    }
+
 }

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-04-02 18:52:26 UTC (rev 803)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -55,7 +55,8 @@
     private Map<String, Object> sessionAttributes;
     @Mock
     private JcrRepository repository;
-
+    private RepositoryNodeTypeManager repoManager;
+    
     @Before
     public void beforeEach() throws Exception {
         final String repositorySourceName = "repository";
@@ -93,7 +94,14 @@
 
         // Stub out the repository, since we only need a few methods ...
         MockitoAnnotations.initMocks(this);
+        
+        JcrNodeTypeSource source = null;
+        source = new JcrBuiltinNodeTypeSource(context, source);
+        source = new DnaBuiltinNodeTypeSource(context, source);
+        repoManager = new RepositoryNodeTypeManager(context, source);
+        
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
+        stub(repository.getRepositoryTypeManager()).toReturn(repoManager);
         stub(repository.getConnectionFactory()).toReturn(connectionFactory);
 
         // Now create the workspace ...

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java	2009-04-02 19:02:05 UTC (rev 804)
@@ -0,0 +1,504 @@
+/*
+ * 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;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.OnParentVersionAction;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+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.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+public class RepositoryNodeTypeManagerTest {
+
+    private String workspaceName;
+    private ExecutionContext context;
+    private InMemoryRepositorySource source;
+    private JcrWorkspace workspace;
+    private JcrSession session;
+    private Graph graph;
+    private RepositoryConnectionFactory connectionFactory;
+    private RepositoryNodeTypeManager repoTypeManager;
+    private Map<String, Object> sessionAttributes;
+    @Mock
+    private JcrRepository repository;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        workspaceName = "workspace1";
+        final String repositorySourceName = "repository";
+
+        // Set up the source ...
+        source = new InMemoryRepositorySource();
+        source.setName(workspaceName);
+        source.setDefaultWorkspaceName(workspaceName);
+
+        // Set up the execution context ...
+        context = new ExecutionContext();
+        // Register the test namespace
+        context.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
+
+        // Set up the initial content ...
+        graph = Graph.create(source, context);
+
+        // Make sure the path to the namespaces exists ...
+        graph.create("/jcr:system"); // .and().create("/jcr:system/dna:namespaces");
+        graph.set("jcr:primaryType").on("/jcr:system").to(DnaLexicon.SYSTEM);
+
+        graph.create("/a").and().create("/a/b").and().create("/a/b/c");
+        graph.set("jcr:mixinTypes").on("/a").to(JcrMixLexicon.REFERENCEABLE);
+
+        // Stub out the connection factory ...
+        connectionFactory = new RepositoryConnectionFactory() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
+             */
+            @SuppressWarnings( "synthetic-access" )
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                return repositorySourceName.equals(sourceName) ? source.getConnection() : null;
+            }
+        };
+
+        // Stub out the repository, since we only need a few methods ...
+        JcrNodeTypeSource source = null;
+        source = new JcrBuiltinNodeTypeSource(this.context, source);
+        source = new DnaBuiltinNodeTypeSource(this.context, source);
+        repoTypeManager = new RepositoryNodeTypeManager(context, source);
+        stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
+        stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
+        stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+
+        // Set up the session attributes ...
+        sessionAttributes = new HashMap<String, Object>();
+        sessionAttributes.put("attribute1", "value1");
+
+        // Now create the workspace ...
+        workspace = new JcrWorkspace(repository, workspaceName, context, sessionAttributes);
+
+        // Create the session and log in ...
+        session = (JcrSession)workspace.getSession();
+
+        graph.set("jcr:primaryType").on("/jcr:system/dna:namespaces").to(DnaLexicon.NAMESPACES);
+
+    }
+
+    @After
+    public void after() throws Exception {
+        if (session != null && session.isLive()) {
+            session.logout();
+        }
+    }
+
+    @Test
+    public void shouldOnlyHaveOneDnaNamespacesNode() throws Exception {
+        NamespaceRegistry registry = context.getNamespaceRegistry();
+
+        Node rootNode = session.getRootNode();
+        assertThat(rootNode, is(notNullValue()));
+
+        Node systemNode = rootNode.getNode(JcrLexicon.SYSTEM.getString(registry));
+        assertThat(systemNode, is(notNullValue()));
+
+        NodeIterator namespacesNodes = systemNode.getNodes(DnaLexicon.NAMESPACES.getString(registry));
+        assertEquals(namespacesNodes.getSize(), 1);
+    }
+
+    @Test
+    public void shouldOnlyHaveOneNodeTypesNode() throws Exception {
+        NamespaceRegistry registry = context.getNamespaceRegistry();
+
+        Node rootNode = session.getRootNode();
+        assertThat(rootNode, is(notNullValue()));
+
+        Node systemNode = rootNode.getNode(JcrLexicon.SYSTEM.getString(registry));
+        assertThat(systemNode, is(notNullValue()));
+
+        NodeIterator nodeTypesNodes = systemNode.getNodes(JcrLexicon.NODE_TYPES.getString(registry));
+        assertEquals(nodeTypesNodes.getSize(), 1);
+    }
+
+    @Test
+    public void shouldAllowMultipleSiblingsDefinitionIfOneSibling() throws Exception {
+        NamespaceRegistry registry = context.getNamespaceRegistry();
+
+        // There's no definition for this node or for a * child node that does not allow SNS
+        JcrNodeDefinition def = repoTypeManager.findChildNodeDefinition(JcrNtLexicon.NODE_TYPE,
+                                                                        Collections.<Name>emptyList(),
+                                                                        JcrLexicon.PROPERTY_DEFINITION,
+                                                                        JcrNtLexicon.PROPERTY_DEFINITION,
+                                                                        1,
+                                                                        false);
+
+        assertThat(def, is(notNullValue()));
+        assertEquals(def.getName(), JcrLexicon.PROPERTY_DEFINITION.getString(registry));
+    }
+
+    public void shouldProjectOntoWorkspaceGraph() throws Exception {
+        // projectOnto is called in the JcrWorkspace constructor... just test that the nodes show up
+        NamespaceRegistry registry = context.getNamespaceRegistry();
+
+        Node rootNode = session.getRootNode();
+        assertThat(rootNode, is(notNullValue()));
+
+        Node systemNode = rootNode.getNode(JcrLexicon.SYSTEM.getString(registry));
+        assertThat(systemNode, is(notNullValue()));
+
+        Node typesNode = systemNode.getNode(JcrLexicon.NODE_TYPES.getString(registry));
+        assertThat(typesNode, is(notNullValue()));
+
+        Collection<JcrNodeType> allNodeTypes = repoTypeManager.getAllNodeTypes();
+        assertThat(allNodeTypes.size(), greaterThan(0));
+        for (JcrNodeType nodeType : allNodeTypes) {
+            Node typeNode = typesNode.getNode(nodeType.getName());
+
+            compareNodeTypes(nodeType, typeNode, registry);
+
+        }
+    }
+
+    private void compareNodeTypes( JcrNodeType nodeType,
+                                   Node typeNode,
+                                   NamespaceRegistry registry ) throws Exception {
+        assertThat(nodeType.isMixin(), is(typeNode.getProperty(JcrLexicon.IS_MIXIN.getString(registry)).getBoolean()));
+        assertThat(nodeType.hasOrderableChildNodes(),
+                   is(typeNode.getProperty(JcrLexicon.HAS_ORDERABLE_CHILD_NODES.getString(registry)).getBoolean()));
+        try {
+            assertThat(nodeType.getPrimaryItemName(), is(typeNode.getProperty(JcrLexicon.PRIMARY_ITEM_NAME.getString(registry))
+                                                                 .getString()));
+        } catch (PathNotFoundException pnfe) {
+            assertThat(nodeType.getPrimaryItemName(), is(nullValue()));
+        }
+        assertThat(nodeType.getName(), is(typeNode.getProperty(JcrLexicon.NODE_TYPE_NAME.getString(registry)).getString()));
+
+        NodeType[] supertypesFromManager = nodeType.getDeclaredSupertypes();
+        Value[] supertypesFromGraph = new Value[0];
+        try {
+            supertypesFromGraph = typeNode.getProperty(JcrLexicon.SUPERTYPES.getString(registry)).getValues();
+        } catch (PathNotFoundException pnfe) {
+            // Not a mandatory property
+        }
+
+        assertThat(supertypesFromGraph.length, is(supertypesFromManager.length));
+        for (int i = 0; i < supertypesFromManager.length; i++) {
+            assertEquals(supertypesFromManager[i].getName(), supertypesFromGraph[i].getString());
+        }
+
+        Map<PropertyDefinitionKey, Node> propertyDefsFromGraph = new HashMap<PropertyDefinitionKey, Node>();
+        NodeIterator properties = typeNode.getNodes(JcrLexicon.PROPERTY_DEFINITION.getString(registry));
+        while (properties.hasNext()) {
+            Node def = properties.nextNode();
+            propertyDefsFromGraph.put(new PropertyDefinitionKey(def, registry), def);
+        }
+
+        PropertyDefinition[] propertyDefs = nodeType.getDeclaredPropertyDefinitions();
+        for (int i = 0; i < propertyDefs.length; i++) {
+            JcrPropertyDefinition propertyDef = (JcrPropertyDefinition)propertyDefs[i];
+            Node propNode = propertyDefsFromGraph.get(new PropertyDefinitionKey(propertyDef));
+            compareProperty(propertyDef, propNode, registry);
+        }
+
+        Map<NodeDefinitionKey, Node> nodeDefsFromGraph = new HashMap<NodeDefinitionKey, Node>();
+        NodeIterator nodes = typeNode.getNodes(JcrLexicon.CHILD_NODE_DEFINITION.getString(registry));
+        while (nodes.hasNext()) {
+            Node def = nodes.nextNode();
+            nodeDefsFromGraph.put(new NodeDefinitionKey(def, registry), def);
+        }
+
+        NodeDefinition[] nodeDefs = nodeType.getDeclaredChildNodeDefinitions();
+        for (int i = 0; i < nodeDefs.length; i++) {
+            JcrNodeDefinition childNodeDef = (JcrNodeDefinition)nodeDefs[i];
+            Node childNodeNode = nodeDefsFromGraph.get(new NodeDefinitionKey(childNodeDef));
+
+            compareChildNode(childNodeDef, childNodeNode, registry);
+        }
+    }
+
+    private void compareChildNode( JcrNodeDefinition nodeDef,
+                                   Node childNodeNode,
+                                   NamespaceRegistry registry ) throws Exception {
+        assertThat(childNodeNode, is(notNullValue()));
+
+        try {
+            Property nameProp = childNodeNode.getProperty(JcrLexicon.NAME.getString(registry));
+            assertEquals(nameProp.getString(), nodeDef.getName());
+        } catch (PathNotFoundException pnfe) {
+            assertThat(nodeDef.getName(), is(JcrNodeType.RESIDUAL_ITEM_NAME));
+        }
+
+        Set<Name> requiredPrimaryTypeNames = nodeDef.getRequiredPrimaryTypeNames();
+        try {
+            Value[] requiredPrimaryTypes = childNodeNode.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES.getString(registry))
+                                                        .getValues();
+            assertEquals(requiredPrimaryTypes.length, requiredPrimaryTypeNames.size());
+            for (int i = 0; i < requiredPrimaryTypes.length; i++) {
+                Name rptName = context.getValueFactories().getNameFactory().create(requiredPrimaryTypes[i].getString());
+                assertEquals(requiredPrimaryTypeNames.contains(rptName), true);
+            }
+        } catch (PathNotFoundException pnfe) {
+            assertEquals(requiredPrimaryTypeNames.size(), 0);
+        }
+
+        try {
+            Property nameProp = childNodeNode.getProperty(JcrLexicon.DEFAULT_PRIMARY_TYPE.getString(registry));
+            assertEquals(nameProp.getString(), nodeDef.getDefaultPrimaryType().getName());
+        } catch (PathNotFoundException pnfe) {
+            assertThat(nodeDef.getDefaultPrimaryType(), is(nullValue()));
+        }
+
+        assertEquals(nodeDef.isAutoCreated(), childNodeNode.getProperty(JcrLexicon.AUTO_CREATED.getString(registry)).getBoolean());
+        assertEquals(nodeDef.isMandatory(), childNodeNode.getProperty(JcrLexicon.MANDATORY.getString(registry)).getBoolean());
+        assertEquals(nodeDef.isProtected(), childNodeNode.getProperty(JcrLexicon.PROTECTED.getString(registry)).getBoolean());
+        assertEquals(nodeDef.allowsSameNameSiblings(),
+                     childNodeNode.getProperty(JcrLexicon.SAME_NAME_SIBLINGS.getString(registry)).getBoolean());
+        assertEquals(nodeDef.getOnParentVersion(),
+                     OnParentVersionAction.valueFromName(childNodeNode.getProperty(JcrLexicon.ON_PARENT_VERSION.getString(registry))
+                                                                      .getString()));
+
+    }
+
+    private void compareProperty( JcrPropertyDefinition propertyDef,
+                                  Node propNode,
+                                  NamespaceRegistry registry ) throws Exception {
+        assertThat(propNode, is(notNullValue()));
+
+        try {
+            Property nameProp = propNode.getProperty(JcrLexicon.NAME.getString(registry));
+            assertEquals(nameProp.getString(), propertyDef.getName());
+        } catch (PathNotFoundException pnfe) {
+            assertThat(propertyDef.getName(), is(JcrNodeType.RESIDUAL_ITEM_NAME));
+        }
+
+        assertEquals(propertyDef.isAutoCreated(), propNode.getProperty(JcrLexicon.AUTO_CREATED.getString(registry)).getBoolean());
+        assertEquals(propertyDef.isMandatory(), propNode.getProperty(JcrLexicon.MANDATORY.getString(registry)).getBoolean());
+        assertEquals(propertyDef.isMultiple(), propNode.getProperty(JcrLexicon.MULTIPLE.getString(registry)).getBoolean());
+        assertEquals(propertyDef.isProtected(), propNode.getProperty(JcrLexicon.PROTECTED.getString(registry)).getBoolean());
+        assertEquals(propertyDef.getOnParentVersion(),
+                     OnParentVersionAction.valueFromName(propNode.getProperty(JcrLexicon.ON_PARENT_VERSION.getString(registry))
+                                                                 .getString()));
+        assertEquals(propertyDef.getRequiredType(),
+                     PropertyType.valueFromName(propNode.getProperty(JcrLexicon.REQUIRED_TYPE.getString(registry)).getString()));
+
+        Value[] defaultValues = propertyDef.getDefaultValues();
+        try {
+            Property defaultsProp = propNode.getProperty(JcrLexicon.DEFAULT_VALUES.getString(registry));
+            Value[] defaultsFromGraph = defaultsProp.getValues();
+
+            assertEquals(defaultValues.length, defaultsFromGraph.length);
+            for (int i = 0; i < defaultValues.length; i++) {
+                assertEquals(defaultValues[i].getString(), defaultsFromGraph[i].getString());
+            }
+        } catch (PathNotFoundException pnfe) {
+            assertEquals(defaultValues.length, 0);
+
+        }
+
+        String[] constraintValues = propertyDef.getValueConstraints();
+        try {
+            Property constraintsProp = propNode.getProperty(JcrLexicon.VALUE_CONSTRAINTS.getString(registry));
+            Value[] constraintsFromGraph = constraintsProp.getValues();
+
+            assertEquals(constraintValues.length, constraintsFromGraph.length);
+            for (int i = 0; i < constraintValues.length; i++) {
+                assertEquals(constraintValues[i], constraintsFromGraph[i].getString());
+            }
+        } catch (PathNotFoundException pnfe) {
+            assertEquals(constraintValues.length, 0);
+        }
+    }
+
+    // @Test
+    // public void shouldInferEmptyFromBadPath() throws Exception {
+    // PathFactory pathFactory = context.getValueFactories().getPathFactory();
+    // assertThat(repoTypeManager.inferFrom(graph, pathFactory.create("/a")).isEmpty(), is(true));
+    // }
+    //
+    // @Test
+    // public void shouldInferEmptyFromInvalidPath() throws Exception {
+    // PathFactory pathFactory = context.getValueFactories().getPathFactory();
+    // assertThat(repoTypeManager.inferFrom(graph, pathFactory.create("/abc")).isEmpty(), is(true));
+    // }
+    //    
+    // @Test
+    // public void shouldInferOwnTypesFromOwnProjection() throws Exception {
+    // PathFactory pathFactory = context.getValueFactories().getPathFactory();
+    // Path nodeTypePath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, JcrLexicon.NODE_TYPES);
+    //        
+    // Collection<JcrNodeType> inferredTypes = repoTypeManager.inferFrom(graph, nodeTypePath);
+    //        
+    // Collection<JcrNodeType> allNodeTypes = repoTypeManager.getAllNodeTypes();
+    // assertThat(inferredTypes.size(), is(allNodeTypes.size()));
+    //        
+    // Map<Name, JcrNodeType> allNodeTypesByName = new HashMap<Name, JcrNodeType>();
+    // for (JcrNodeType jnt : allNodeTypes) {
+    // allNodeTypesByName.put(jnt.getInternalName(), jnt);
+    // }
+    //        
+    // for (JcrNodeType nodeType : inferredTypes) {
+    // JcrNodeType other = allNodeTypesByName.get(nodeType.getInternalName());
+    //            
+    // System.out.println("Checking: " + other.getName());
+    //            
+    // assertThat(other, is(notNullValue()));
+    // assertThat(areSame(nodeType, other), is(true));
+    // }
+    // }
+
+    /**
+     * Internal class that encapsulates composite unique identifier for property definitions
+     */
+    class PropertyDefinitionKey {
+        String keyString;
+
+        PropertyDefinitionKey( Node node,
+                               NamespaceRegistry registry ) throws Exception {
+            String propertyName = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+            try {
+                propertyName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+            } catch (PathNotFoundException pnfe) {
+                // Ignorable, means name is not set.
+            }
+            String requiredType = node.getProperty(JcrLexicon.REQUIRED_TYPE.getString(registry)).getString();
+            boolean allowsMultiple = node.getProperty(JcrLexicon.MULTIPLE.getString(registry)).getBoolean();
+
+            this.keyString = propertyName + "-" + requiredType + "-" + allowsMultiple;
+        }
+
+        PropertyDefinitionKey( PropertyDefinition def ) {
+            String requiredType = PropertyType.nameFromValue(def.getRequiredType());
+
+            this.keyString = def.getName() + "-" + requiredType + "-" + def.isMultiple();
+        }
+
+        @Override
+        public boolean equals( Object ob ) {
+            if (!(ob instanceof PropertyDefinitionKey)) {
+                return false;
+            }
+
+            return keyString.equals(((PropertyDefinitionKey)ob).keyString);
+        }
+
+        @Override
+        public int hashCode() {
+            return keyString.hashCode();
+        }
+    }
+
+    /**
+     * Internal class that encapsulates composite unique identifier for child node definitions
+     */
+    class NodeDefinitionKey {
+        String keyString;
+
+        NodeDefinitionKey( Node node,
+                           NamespaceRegistry registry ) throws Exception {
+            StringBuffer buff = new StringBuffer();
+
+            try {
+                String nodeName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+                buff.append(nodeName);
+            } catch (PathNotFoundException pnfe) {
+                // Ignorable, means name is not set.
+                buff.append(JcrNodeType.RESIDUAL_ITEM_NAME);
+            }
+
+            try {
+                Value[] requiredTypes = node.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES.getString(registry)).getValues();
+
+                for (int i = 0; i < requiredTypes.length; i++) {
+                    buff.append('_').append(requiredTypes[i].getString());
+                }
+            } catch (PathNotFoundException pnfe) {
+                // No required types. Weird, but not debilitating.
+            }
+
+            buff.append('_').append(node.getProperty(JcrLexicon.SAME_NAME_SIBLINGS.getString(registry)).getBoolean());
+
+            this.keyString = buff.toString();
+        }
+
+        NodeDefinitionKey( NodeDefinition def ) {
+            StringBuffer buff = new StringBuffer();
+            buff.append(def.getName());
+
+            NodeType[] requiredTypes = def.getRequiredPrimaryTypes();
+            for (int i = 0; i < requiredTypes.length; i++) {
+                buff.append('_').append(requiredTypes[i].getName());
+            }
+
+            buff.append('_').append(def.allowsSameNameSiblings());
+
+            this.keyString = buff.toString();
+        }
+
+        @Override
+        public boolean equals( Object ob ) {
+            if (!(ob instanceof NodeDefinitionKey)) {
+                return false;
+            }
+
+            return keyString.equals(((NodeDefinitionKey)ob).keyString);
+        }
+
+        @Override
+        public int hashCode() {
+            return keyString.hashCode();
+        }
+    }
+
+}


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



From dna-commits at lists.jboss.org  Thu Apr  2 16:15:10 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 02 Apr 2009 16:15:10 -0400
Subject: [dna-commits] DNA SVN: r805 - in trunk:
	dna-jcr/src/test/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LpTJe-0001Aj-1R@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-02 16:15:09 -0400 (Thu, 02 Apr 2009)
New Revision: 805

Modified:
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
   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/RepositoryNodeTypeManager.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-340 Projection of Node Types onto System View

Applied the 2nd patch, which adds support to JcrRepository for configurable options, with a single option as to whether or not the node types should be projected under "/jcr:system/dna:nodeTypes".  Minor change from the patch was to reorder the constructor parameters of JcrRepository to match the other existing constructor.

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-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -26,6 +26,7 @@
 import java.lang.reflect.Method;
 import java.security.AccessControlContext;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -71,11 +72,31 @@
 @ThreadSafe
 public class JcrRepository implements Repository {
 
+    /**
+     * List of settings for the {@code JcrRepository}.
+     * <p>
+     * The attributes that may be set are:
+     * <ul>
+     * <li>{@code PROJECT_NODE_TYPES} (boolean) - if {@code true}, project the registered node types onto the system view at the
+     * path {@code /jcr:system/jcr:nodeTypes}.</li>
+     * </ul>
+     * </p>
+     */
+    public enum Settings {
+        /**
+         * Flag that defines whether or not the node types should be exposed as content under the "
+         * <code>/jcr:system/dna:nodeTypes</code>" node. By default, the flag is false.
+         */
+        PROJECT_NODE_TYPES,
+
+    }
+
     private final String sourceName;
     private final Map<String, String> descriptors;
     private final ExecutionContext executionContext;
     private final RepositoryConnectionFactory connectionFactory;
     private final RepositoryNodeTypeManager repositoryTypeManager;
+    private final Map<Settings, String> settings;
 
     /**
      * Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -90,24 +111,26 @@
     public JcrRepository( ExecutionContext executionContext,
                           RepositoryConnectionFactory connectionFactory,
                           String repositorySourceName ) {
-        this(null, executionContext, connectionFactory, repositorySourceName);
+        this(executionContext, connectionFactory, repositorySourceName, null, null);
     }
 
     /**
      * Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
      * establish {@link Session sessions} to the underlying repository source upon {@link #login() login}.
      * 
-     * @param descriptors The {@link #getDescriptorKeys() descriptors} for this repository; may be <code>null</code>.
      * @param executionContext the execution context in which this repository is to operate
      * @param connectionFactory the factory for repository connections
      * @param repositorySourceName the name of the repository source (in the connection factory) that should be used
+     * @param descriptors the {@link #getDescriptorKeys() descriptors} for this repository; may be <code>null</code>.
+     * @param settings the optional {@link Settings settings} for this repository; may be null
      * @throws IllegalArgumentException If <code>executionContextFactory</code> or <code>connectionFactory</code> is
      *         <code>null</code>.
      */
-    public JcrRepository( Map<String, String> descriptors,
-                          ExecutionContext executionContext,
+    public JcrRepository( ExecutionContext executionContext,
                           RepositoryConnectionFactory connectionFactory,
-                          String repositorySourceName ) {
+                          String repositorySourceName,
+                          Map<String, String> descriptors,
+                          Map<Settings, String> settings ) {
         CheckArg.isNotNull(executionContext, "executionContext");
         CheckArg.isNotNull(connectionFactory, "connectionFactory");
         CheckArg.isNotNull(repositorySourceName, "repositorySourceName");
@@ -146,21 +169,28 @@
         modifiableDescriptors.put(Repository.SPEC_NAME_DESC, JcrI18n.SPEC_NAME_DESC.text());
         modifiableDescriptors.put(Repository.SPEC_VERSION_DESC, "1.0");
         this.descriptors = Collections.unmodifiableMap(modifiableDescriptors);
-        
+
         JcrNodeTypeSource source = null;
         source = new JcrBuiltinNodeTypeSource(this.executionContext);
         source = new DnaBuiltinNodeTypeSource(this.executionContext, source);
         this.repositoryTypeManager = new RepositoryNodeTypeManager(this.executionContext, source);
+
+        if (settings == null) {
+            this.settings = Collections.<Settings, String>emptyMap();
+        } else {
+            this.settings = Collections.unmodifiableMap(new EnumMap<Settings, String>(settings));
+        }
     }
 
     /**
      * Returns the repository-level node type manager
+     * 
      * @return the repository-level node type manager
      */
     RepositoryNodeTypeManager getRepositoryTypeManager() {
         return repositoryTypeManager;
     }
-    
+
     /**
      * Get the name of the repository source that this repository is using.
      * 
@@ -322,8 +352,10 @@
         }
 
         // Create the workspace, which will create its own session ...
+        boolean shouldProjectNodeTypes = Boolean.valueOf(settings.get(Settings.PROJECT_NODE_TYPES));
+
         sessionAttributes = Collections.unmodifiableMap(sessionAttributes);
-        JcrWorkspace workspace = new JcrWorkspace(this, workspaceName, execContext, sessionAttributes);
+        JcrWorkspace workspace = new JcrWorkspace(this, workspaceName, execContext, sessionAttributes, shouldProjectNodeTypes);
         return workspace.getSession();
     }
 }

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-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -120,6 +120,15 @@
                   String workspaceName,
                   ExecutionContext context,
                   Map<String, Object> sessionAttributes ) {
+        this(repository, workspaceName, context, sessionAttributes, true);
+    }
+
+    JcrWorkspace( JcrRepository repository,
+                  String workspaceName,
+                  ExecutionContext context,
+                  Map<String, Object> sessionAttributes,
+                  boolean projectTypesOntoSystemView ) {
+
         assert workspaceName != null;
         assert context != null;
         assert repository != null;
@@ -157,10 +166,12 @@
         this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repoTypeManager);
         this.queryManager = new JcrQueryManager(this.session);
 
-        Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(root,
-                                                                                     JcrLexicon.SYSTEM,
-                                                                                     JcrLexicon.NODE_TYPES);
-        repoTypeManager.projectOnto(this.graph, parentOfTypeNodes);
+        if (projectTypesOntoSystemView) {
+            Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(root,
+                                                                                         JcrLexicon.SYSTEM,
+                                                                                         JcrLexicon.NODE_TYPES);
+            repoTypeManager.projectOnto(this.graph, parentOfTypeNodes);
+        }
     }
 
     final String getSourceName() {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -37,6 +37,7 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.version.OnParentVersionAction;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.common.text.XmlNameEncoder;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -60,6 +61,8 @@
 @Immutable
 class RepositoryNodeTypeManager {
 
+    private static final TextEncoder NAME_ENCODER = new XmlNameEncoder();
+
     private final ExecutionContext context;
     private final Map<Name, JcrNodeType> nodeTypes;
     private final Map<PropertyDefinitionId, JcrPropertyDefinition> propertyDefinitions;
@@ -652,8 +655,7 @@
             projectNodeTypeOnto(nodeType, parentOfTypeNodes, batch);
         }
 
-        // TODO: Add back in; see DNA-340 ...
-        // batch.execute();
+        batch.execute();
     }
 
     /**
@@ -730,7 +732,7 @@
         assert batch != null;
 
         JcrPropertyDefinition jcrPropDef = (JcrPropertyDefinition)propertyDef;
-        String propName = jcrPropDef.getInternalName().getString(context.getNamespaceRegistry(), new XmlNameEncoder());
+        String propName = jcrPropDef.getInternalName().getString(context.getNamespaceRegistry(), NAME_ENCODER);
         Path propDefPath = pathFactory.create(nodeTypePath, JcrLexicon.PROPERTY_DEFINITION);
 
         List<Property> propsList = new ArrayList<Property>();
@@ -793,7 +795,7 @@
         assert batch != null;
 
         JcrNodeDefinition jcrNodeDef = (JcrNodeDefinition)childNodeDef;
-        String nodeName = jcrNodeDef.getInternalName().getString(context.getNamespaceRegistry(), new XmlNameEncoder());
+        String nodeName = jcrNodeDef.getInternalName().getString(context.getNamespaceRegistry(), NAME_ENCODER);
         Path nodeDefPath = pathFactory.create(nodeTypePath, JcrLexicon.CHILD_NODE_DEFINITION);
 
         List<Property> propsList = new ArrayList<Property>();
@@ -817,5 +819,4 @@
 
         batch.create(nodeDefPath).with(propsList).and();
     }
-
 }

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-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -103,27 +103,27 @@
 
         // Set up the repository ...
         descriptors = new HashMap<String, String>();
-        repository = new JcrRepository(descriptors, context, connectionFactory, sourceName);
+        repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, null);
     }
 
     @Test
     public void shouldAllowNullDescriptors() {
-        new JcrRepository(null, context, connectionFactory, sourceName);
+        new JcrRepository(context, connectionFactory, sourceName, null, null);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowNullExecutionContext() throws Exception {
-        new JcrRepository(descriptors, null, connectionFactory, sourceName);
+        new JcrRepository(null, connectionFactory, sourceName, descriptors, null);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowNullConnectionFactories() throws Exception {
-        new JcrRepository(descriptors, context, null, sourceName);
+        new JcrRepository(context, null, sourceName, descriptors, null);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowNullSourceName() throws Exception {
-        new JcrRepository(descriptors, context, connectionFactory, null);
+        new JcrRepository(context, connectionFactory, null, descriptors, null);
     }
 
     @Test( expected = IllegalArgumentException.class )
@@ -148,7 +148,7 @@
 
     @Test
     public void shouldProvideBuiltInDescriptorsWhenNotSuppliedDescriptors() {
-        Repository repository = new JcrRepository(descriptors, context, connectionFactory, sourceName);
+        Repository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, null);
         testDescriptorKeys(repository);
         testDescriptorValues(repository);
     }
@@ -157,7 +157,7 @@
     public void shouldProvideUserSuppliedDescriptors() {
         Map<String, String> descriptors = new HashMap<String, String>();
         descriptors.put("property", "value");
-        Repository repository = new JcrRepository(descriptors, context, connectionFactory, sourceName);
+        Repository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, null);
         testDescriptorKeys(repository);
         testDescriptorValues(repository);
         assertThat(repository.getDescriptor("property"), is("value"));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -28,6 +28,7 @@
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Properties;
 import javax.jcr.Credentials;
 import javax.jcr.Repository;
@@ -54,6 +55,7 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.JcrRepository.Settings;
 
 /**
  * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests. Note that technically these are not the
@@ -276,9 +278,10 @@
             source.setName("TestRepositorySource");
 
             // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
+            Map<Settings, String> settings = Collections.singletonMap(Settings.PROJECT_NODE_TYPES, "false");
             connection = source.getConnection();
-            repository = new JcrRepository(Collections.<String, String>emptyMap(), executionContext.create(accessControlContext),
-                                           connectionFactory, source.getName());
+            repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(),
+                                           null, settings);
 
             // Make sure the path to the namespaces exists ...
             Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
@@ -291,7 +294,7 @@
                 executionContext.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
                 executionContext.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
                 executionContext.getNamespaceRegistry().register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
-                executionContext.getNamespaceRegistry().register("sv", "http://www.jcp.org/jcr/sv/1.0");
+                executionContext.getNamespaceRegistry().register(JcrSvLexicon.Namespace.PREFIX, JcrSvLexicon.Namespace.URI);
 
                 Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
                 GraphImporter importer = new GraphImporter(graph);

Modified: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-02 19:02:05 UTC (rev 804)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-02 20:15:09 UTC (rev 805)
@@ -28,6 +28,7 @@
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Properties;
 import javax.jcr.Credentials;
 import javax.jcr.Repository;
@@ -42,6 +43,7 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.JcrRepository.Settings;
 
 /**
  * Class with TCK repository stub. This class does not contain any tests.
@@ -84,9 +86,10 @@
         source.setName("TestRepositorySource");
 
         // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
+        Map<Settings, String> settings = Collections.singletonMap(Settings.PROJECT_NODE_TYPES, "true");
         connection = source.getConnection();
-        repository = new JcrRepository(Collections.<String, String>emptyMap(), executionContext.create(accessControlContext),
-                                       connectionFactory, source.getName());
+        repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(), null,
+                                       settings);
 
         // Make sure the path to the namespaces exists ...
         Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
@@ -99,7 +102,7 @@
             executionContext.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
             executionContext.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
             executionContext.getNamespaceRegistry().register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
-            executionContext.getNamespaceRegistry().register("sv", "http://www.jcp.org/jcr/sv/1.0");
+            executionContext.getNamespaceRegistry().register(JcrSvLexicon.Namespace.PREFIX, JcrSvLexicon.Namespace.URI);
 
             Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
             GraphImporter importer = new GraphImporter(graph);



From dna-commits at lists.jboss.org  Thu Apr  2 16:39:44 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 02 Apr 2009 16:39:44 -0400
Subject: [dna-commits] DNA SVN: r806 - in trunk:
	dna-jcr/src/test/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LpThQ-0000cf-Hl@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-02 16:39:44 -0400 (Thu, 02 Apr 2009)
New Revision: 806

Modified:
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
   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/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
Log:
DNA-340 Projection of Node Types onto System View

Changed the name of JcrRepository.Settings to JcrRepository.Options, added a mechanism for setting up defaults, and added a public method to get the unmodifiable options map.  Also changed JcrWorkspace to use the repository's options map (rather than a custom boolean constructor parameter) to determine whether it should project the node types as content.

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-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -73,30 +73,47 @@
 public class JcrRepository implements Repository {
 
     /**
-     * List of settings for the {@code JcrRepository}.
-     * <p>
-     * The attributes that may be set are:
-     * <ul>
-     * <li>{@code PROJECT_NODE_TYPES} (boolean) - if {@code true}, project the registered node types onto the system view at the
-     * path {@code /jcr:system/jcr:nodeTypes}.</li>
-     * </ul>
-     * </p>
+     * The available options for the {@code JcrRepository}.
      */
-    public enum Settings {
+    public enum Options {
+
         /**
-         * Flag that defines whether or not the node types should be exposed as content under the "
-         * <code>/jcr:system/dna:nodeTypes</code>" node. By default, the flag is false.
+         * Flag that defines whether or not the node types should be exposed as content under the "{@code
+         * /jcr:system/jcr:nodeTypes}" node. Value is either "<code>true</code>" or "<code>false</code>" (default).
+         * 
+         * @see DefaultOptions#PROJECT_NODE_TYPES
          */
         PROJECT_NODE_TYPES,
+    }
 
+    /**
+     * The default values for each of the {@link Options}.
+     */
+    public static class DefaultOptions {
+        /**
+         * The default value for the {@link Options#PROJECT_NODE_TYPES} option is {@value} .
+         */
+        public static final String PROJECT_NODE_TYPES = Boolean.FALSE.toString();
     }
 
+    /**
+     * The static unmodifiable map of default options, which are initialized in the static initializer.
+     */
+    protected static final Map<Options, String> DEFAULT_OPTIONS;
+
+    static {
+        // Initialize the unmodifiable map of default options ...
+        EnumMap<Options, String> defaults = new EnumMap<Options, String>(Options.class);
+        defaults.put(Options.PROJECT_NODE_TYPES, DefaultOptions.PROJECT_NODE_TYPES);
+        DEFAULT_OPTIONS = Collections.<Options, String>unmodifiableMap(defaults);
+    }
+
     private final String sourceName;
     private final Map<String, String> descriptors;
     private final ExecutionContext executionContext;
     private final RepositoryConnectionFactory connectionFactory;
     private final RepositoryNodeTypeManager repositoryTypeManager;
-    private final Map<Settings, String> settings;
+    private final Map<Options, String> options;
 
     /**
      * Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -122,7 +139,7 @@
      * @param connectionFactory the factory for repository connections
      * @param repositorySourceName the name of the repository source (in the connection factory) that should be used
      * @param descriptors the {@link #getDescriptorKeys() descriptors} for this repository; may be <code>null</code>.
-     * @param settings the optional {@link Settings settings} for this repository; may be null
+     * @param options the optional {@link Options settings} for this repository; may be null
      * @throws IllegalArgumentException If <code>executionContextFactory</code> or <code>connectionFactory</code> is
      *         <code>null</code>.
      */
@@ -130,7 +147,7 @@
                           RepositoryConnectionFactory connectionFactory,
                           String repositorySourceName,
                           Map<String, String> descriptors,
-                          Map<Settings, String> settings ) {
+                          Map<Options, String> options ) {
         CheckArg.isNotNull(executionContext, "executionContext");
         CheckArg.isNotNull(connectionFactory, "connectionFactory");
         CheckArg.isNotNull(repositorySourceName, "repositorySourceName");
@@ -175,10 +192,13 @@
         source = new DnaBuiltinNodeTypeSource(this.executionContext, source);
         this.repositoryTypeManager = new RepositoryNodeTypeManager(this.executionContext, source);
 
-        if (settings == null) {
-            this.settings = Collections.<Settings, String>emptyMap();
+        if (options == null) {
+            this.options = DEFAULT_OPTIONS;
         } else {
-            this.settings = Collections.unmodifiableMap(new EnumMap<Settings, String>(settings));
+            // Initialize with defaults, then add supplied options ...
+            EnumMap<Options, String> localOptions = new EnumMap<Options, String>(DEFAULT_OPTIONS);
+            localOptions.putAll(options);
+            this.options = Collections.unmodifiableMap(localOptions);
         }
     }
 
@@ -192,6 +212,15 @@
     }
 
     /**
+     * Get the options as configured for this repository.
+     * 
+     * @return the unmodifiable options; never null
+     */
+    public Map<Options, String> getOptions() {
+        return options;
+    }
+
+    /**
      * Get the name of the repository source that this repository is using.
      * 
      * @return the name of the RepositorySource
@@ -352,10 +381,8 @@
         }
 
         // Create the workspace, which will create its own session ...
-        boolean shouldProjectNodeTypes = Boolean.valueOf(settings.get(Settings.PROJECT_NODE_TYPES));
-
         sessionAttributes = Collections.unmodifiableMap(sessionAttributes);
-        JcrWorkspace workspace = new JcrWorkspace(this, workspaceName, execContext, sessionAttributes, shouldProjectNodeTypes);
+        JcrWorkspace workspace = new JcrWorkspace(this, workspaceName, execContext, sessionAttributes);
         return workspace.getSession();
     }
 }

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-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -58,6 +58,7 @@
 import org.jboss.dna.graph.property.PropertyFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.basic.GraphNamespaceRegistry;
+import org.jboss.dna.jcr.JcrRepository.Options;
 import org.xml.sax.ContentHandler;
 
 /**
@@ -120,15 +121,7 @@
                   String workspaceName,
                   ExecutionContext context,
                   Map<String, Object> sessionAttributes ) {
-        this(repository, workspaceName, context, sessionAttributes, true);
-    }
 
-    JcrWorkspace( JcrRepository repository,
-                  String workspaceName,
-                  ExecutionContext context,
-                  Map<String, Object> sessionAttributes,
-                  boolean projectTypesOntoSystemView ) {
-
         assert workspaceName != null;
         assert context != null;
         assert repository != null;
@@ -166,7 +159,7 @@
         this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repoTypeManager);
         this.queryManager = new JcrQueryManager(this.session);
 
-        if (projectTypesOntoSystemView) {
+        if (Boolean.valueOf(repository.getOptions().get(Options.PROJECT_NODE_TYPES))) {
             Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(root,
                                                                                          JcrLexicon.SYSTEM,
                                                                                          JcrLexicon.NODE_TYPES);

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-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -154,6 +154,13 @@
     }
 
     @Test
+    public void shouldHaveDefaultOptionsWhenNotOverridden() {
+        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, null);
+        assertThat(repository.getOptions().get(JcrRepository.Options.PROJECT_NODE_TYPES),
+                   is(JcrRepository.DefaultOptions.PROJECT_NODE_TYPES));
+    }
+
+    @Test
     public void shouldProvideUserSuppliedDescriptors() {
         Map<String, String> descriptors = new HashMap<String, String>();
         descriptors.put("property", "value");

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -55,7 +55,7 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Settings;
+import org.jboss.dna.jcr.JcrRepository.Options;
 
 /**
  * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests. Note that technically these are not the
@@ -278,10 +278,10 @@
             source.setName("TestRepositorySource");
 
             // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
-            Map<Settings, String> settings = Collections.singletonMap(Settings.PROJECT_NODE_TYPES, "false");
+            Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
             connection = source.getConnection();
             repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(),
-                                           null, settings);
+                                           null, options);
 
             // Make sure the path to the namespaces exists ...
             Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java	2009-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.stub;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -70,6 +71,7 @@
     private RepositoryConnectionFactory connectionFactory;
     private RepositoryNodeTypeManager repoTypeManager;
     private Map<String, Object> sessionAttributes;
+    private Map<JcrRepository.Options, String> options;
     @Mock
     private JcrRepository repository;
 
@@ -122,6 +124,11 @@
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
         stub(repository.getConnectionFactory()).toReturn(connectionFactory);
 
+        // Stub out the repository options ...
+        options = new EnumMap<JcrRepository.Options, String>(JcrRepository.Options.class);
+        options.put(JcrRepository.Options.PROJECT_NODE_TYPES, Boolean.TRUE.toString());
+        stub(repository.getOptions()).toReturn(options);
+
         // Set up the session attributes ...
         sessionAttributes = new HashMap<String, Object>();
         sessionAttributes.put("attribute1", "value1");

Modified: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-02 20:15:09 UTC (rev 805)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-02 20:39:44 UTC (rev 806)
@@ -43,7 +43,7 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Settings;
+import org.jboss.dna.jcr.JcrRepository.Options;
 
 /**
  * Class with TCK repository stub. This class does not contain any tests.
@@ -86,10 +86,10 @@
         source.setName("TestRepositorySource");
 
         // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
-        Map<Settings, String> settings = Collections.singletonMap(Settings.PROJECT_NODE_TYPES, "true");
+        Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "true");
         connection = source.getConnection();
         repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(), null,
-                                       settings);
+                                       options);
 
         // Make sure the path to the namespaces exists ...
         Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);



From dna-commits at lists.jboss.org  Mon Apr  6 10:37:58 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 06 Apr 2009 10:37:58 -0400
Subject: [dna-commits] DNA SVN: r807 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LqpxW-0002gz-0V@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-06 10:37:57 -0400 (Mon, 06 Apr 2009)
New Revision: 807

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-342 Wrong Values is Cast When Setting Property with Castable Type

Applied patch to correct the use of a wrong variable.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-02 20:39:44 UTC (rev 806)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-06 14:37:57 UTC (rev 807)
@@ -583,7 +583,7 @@
                 // A cast is required ...
                 org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
                 ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
-                objValue = factory.create(value);
+                objValue = factory.create(objValue);
             }
             Property dnaProp = propertyFactory.create(name, objValue);
 



From dna-commits at lists.jboss.org  Mon Apr  6 11:55:23 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 06 Apr 2009 11:55:23 -0400
Subject: [dna-commits] DNA SVN: r808 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LqrAR-0001ls-GM@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-06 11:55:23 -0400 (Mon, 06 Apr 2009)
New Revision: 808

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-344 JcrSession.getValueFactory.create(String, int) Does Not Fail Fast as per Specification

Applied patch to correct the behavior.

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-04-06 14:37:57 UTC (rev 807)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-06 15:55:23 UTC (rev 808)
@@ -44,6 +44,7 @@
 import javax.jcr.SimpleCredentials;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
 import javax.jcr.Workspace;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
@@ -451,8 +452,10 @@
         return new ValueFactory() {
 
             public Value createValue( String value,
-                                      int propertyType ) {
-                return new JcrValue(valueFactories, sessionCache, propertyType, value);
+                                      int propertyType ) 
+                throws ValueFormatException
+            {
+                return new JcrValue(valueFactories, sessionCache, propertyType, convertValueToType(value, propertyType));
             }
 
             public Value createValue( Node value ) throws RepositoryException {
@@ -485,6 +488,79 @@
             public Value createValue( String value ) {
                 return new JcrValue(valueFactories, sessionCache, PropertyType.STRING, value);
             }
+            
+            Object convertValueToType(Object value, int toType) throws ValueFormatException { 
+                switch (toType) {
+                    case PropertyType.BOOLEAN:
+                        try {
+                            return valueFactories.getBooleanFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+
+                    case PropertyType.DATE:
+                        try {
+                            return valueFactories.getDateFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+
+                    case PropertyType.NAME:
+                        try {
+                            return valueFactories.getNameFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+
+                    case PropertyType.PATH:
+                        try {
+                            return valueFactories.getPathFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+
+                    case PropertyType.REFERENCE:
+                        try {
+                            return valueFactories.getReferenceFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+                    case PropertyType.DOUBLE:
+                        try {
+                            return valueFactories.getDoubleFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+                    case PropertyType.LONG:
+                        try {
+                            return valueFactories.getLongFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+
+                        // Anything can be converted to these types
+                    case PropertyType.BINARY:
+                        try {
+                            return valueFactories.getBinaryFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+                    case PropertyType.STRING:
+                        try {
+                            return valueFactories.getStringFactory().create(value);
+                        } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                            throw new ValueFormatException(vfe);
+                        }
+                    case PropertyType.UNDEFINED:
+                        return value;
+
+                    default:
+                        assert false : "Unexpected JCR property type " + toType;
+                        // This should still throw an exception even if assertions are turned off
+                        throw new IllegalStateException("Invalid property type " + toType);
+                }
+            }
+
         };
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-06 14:37:57 UTC (rev 807)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-06 15:55:23 UTC (rev 808)
@@ -40,11 +40,13 @@
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
 import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
+import org.apache.jackrabbit.test.api.SessionUUIDTest;
 import org.apache.jackrabbit.test.api.SetValueBooleanTest;
 import org.apache.jackrabbit.test.api.SetValueDateTest;
 import org.apache.jackrabbit.test.api.SetValueDoubleTest;
 import org.apache.jackrabbit.test.api.SetValueLongTest;
 import org.apache.jackrabbit.test.api.SetValueReferenceTest;
+import org.apache.jackrabbit.test.api.ValueFactoryTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -156,7 +158,7 @@
             addTestSuite(NamespaceRegistryTest.class);
             // addTestSuite(ReferencesTest.class);
             // addTestSuite(SessionTest.class);
-            // addTestSuite(SessionUUIDTest.class);
+            addTestSuite(SessionUUIDTest.class);
             // addTestSuite(NodeTest.class);
             // addTestSuite(NodeUUIDTest.class);
             // addTestSuite(NodeOrderableChildNodesTest.class);
@@ -216,8 +218,8 @@
             //
             // addTestSuite(DocumentViewImportTest.class);
             // addTestSuite(SerializationTest.class);
-            //
-            // addTestSuite(ValueFactoryTest.class);
+
+            addTestSuite(ValueFactoryTest.class);
         }
     }
 
@@ -337,6 +339,18 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
+         */
+        @Override
+        public Credentials getReadWriteCredentials() {
+            // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
+            // SimpleCredentials.
+            return credentials;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
          */
         @Override



From dna-commits at lists.jboss.org  Tue Apr  7 17:23:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 07 Apr 2009 17:23:28 -0400
Subject: [dna-commits] DNA SVN: r809 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory
	and 13 other directories.
Message-ID: <E1LrIlU-0005nS-Mb@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-07 17:23:28 -0400 (Tue, 07 Apr 2009)
New Revision: 809

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.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/RequestBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/RemovePropertiesRequestTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.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/DestroyWorkspaceRequest.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/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.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/request/UpdatePropertiesRequestTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.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/JpaConnectorReadingTest.java
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
   trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
Log:
DNA-341 Graph API behavior when removing all property values doesn't reflect that of JCR

Applied patch that changes the connector API.  Specifically the semantics and a few method signatures of UpdatePropertiesRequest class were changed to no longer assume that an empty property should be removed; rather, the names of properties that are to be removed are tracked explicitly, and the number of values in a property no longer has any relationship to whether the property is to be removed.  All connectors were changed to apply these new semantics.

A number of other changes to the connector API were made.  New SetPropertyRequest and RemovePropertyRequest requests types were added as being more efficient when a single property is being set or removed.  These are optional requests, meaning that the RequestProcessor has default implementations for the corresponding "process" methods that convert the reqeusts to UpdatedPropertiesRequest.  Thus, a connector doesn't have to implement these, but it may be more efficient for it to do so.  The RemovedPropertiesRequest was also removed, since it didn't offer any benefit over UpdatePropertiesRequest (and since it is simpler to only have one).  All connectors were updated to reflect these semantics.

Also, the Graph batch implementation was improved to optimize adjacent calls to set/remove properties on the same node.  In other words, if a batch is used to set a property on node A and then is immediately used to set another property on the same node, the batch will combine the two requests.  However, this optimization does not apply if any other operations are performed between the set/remove property operations.

Finally, a minor error was found and corrected in how the connector unit tests reported the (trivial) metrics for creating nodes. The total times (line 2) were correct, but the average and the times for additional nodes (line 3) were all incorrect.

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -43,6 +43,7 @@
 import java.util.UUID;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.collection.EmptyIterator;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.cache.CachePolicy;
 import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -60,27 +61,22 @@
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.BatchRequestBuilder;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
 import org.jboss.dna.graph.request.CompositeRequest;
-import org.jboss.dna.graph.request.CopyBranchRequest;
 import org.jboss.dna.graph.request.CreateNodeRequest;
 import org.jboss.dna.graph.request.CreateWorkspaceRequest;
-import org.jboss.dna.graph.request.DeleteBranchRequest;
-import org.jboss.dna.graph.request.GetWorkspacesRequest;
 import org.jboss.dna.graph.request.InvalidRequestException;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
-import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
 import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadBranchRequest;
-import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.ReadPropertyRequest;
-import org.jboss.dna.graph.request.RemovePropertiesRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.RequestBuilder;
 import org.jboss.dna.graph.request.UnsupportedRequestException;
-import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
 import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
@@ -97,6 +93,13 @@
 @NotThreadSafe
 public class Graph {
 
+    protected static final Iterator<Property> EMPTY_PROPERTIES = new EmptyIterator<Property>();
+    protected static final Iterable<Property> NO_PROPERTIES = new Iterable<Property>() {
+        public final Iterator<Property> iterator() {
+            return EMPTY_PROPERTIES;
+        }
+    };
+
     /**
      * Create a graph instance that uses the supplied repository and {@link ExecutionContext context}.
      * 
@@ -157,8 +160,8 @@
     private final String sourceName;
     private final RepositoryConnectionFactory connectionFactory;
     private final ExecutionContext context;
-    private final RequestQueue requestQueue;
-    private final Conjunction<Graph> nextGraph;
+    protected final RequestBuilder requests;
+    protected final Conjunction<Graph> nextGraph;
     private Workspace currentWorkspace;
 
     protected Graph( String sourceName,
@@ -175,7 +178,13 @@
                 return Graph.this;
             }
         };
-        this.requestQueue = new GraphRequestQueue();
+        this.requests = new RequestBuilder() {
+            @Override
+            protected <T extends Request> T process( T request ) {
+                Graph.this.execute(request);
+                return request;
+            }
+        };
     }
 
     /**
@@ -207,16 +216,12 @@
         return context;
     }
 
-    /*package*/RequestQueue queue() {
-        return this.requestQueue;
-    }
-
     /**
      * Obtain a connection to the source, execute the supplied request, and check the request for {@link Request#getError()
      * errors}. If an error is found, then it is thrown (or wrapped by a {@link RepositorySourceException} if the error is not a
      * {@link RuntimeException}.
      * <p>
-     * This method is called by one of the {@link RequestQueue} implementations.
+     * This method is called automatically when the {@link #requests request builder} creates each request.
      * </p>
      * 
      * @param request the request to be executed (may be a {@link CompositeRequest}.
@@ -314,9 +319,7 @@
      * @return the set of workspace names; never null
      */
     public Set<String> getWorkspaces() {
-        GetWorkspacesRequest request = new GetWorkspacesRequest();
-        this.requestQueue.submit(request);
-        return request.getAvailableWorkspaceNames();
+        return requests.getWorkspaces().getAvailableWorkspaceNames();
     }
 
     /**
@@ -329,8 +332,7 @@
      *         workspace name but the source does not have a default workspace
      */
     public Workspace useWorkspace( String workspaceName ) {
-        VerifyWorkspaceRequest request = new VerifyWorkspaceRequest(workspaceName);
-        requestQueue.submit(request); // will throw error if there is one ...
+        VerifyWorkspaceRequest request = requests.verifyWorkspace(workspaceName);
         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
     }
 
@@ -349,8 +351,7 @@
              * @see org.jboss.dna.graph.Graph.NameWorkspace#named(java.lang.String)
              */
             public Workspace named( String workspaceName ) {
-                CreateWorkspaceRequest request = new CreateWorkspaceRequest(workspaceName, CreateConflictBehavior.DO_NOT_CREATE);
-                queue().submit(request); // will throw error if there is one ...
+                CreateWorkspaceRequest request = requests.createWorkspace(workspaceName, CreateConflictBehavior.DO_NOT_CREATE);
                 return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
             }
 
@@ -360,9 +361,8 @@
              * @see org.jboss.dna.graph.Graph.CreateWorkspace#namedSomethingLike(java.lang.String)
              */
             public Workspace namedSomethingLike( String workspaceName ) {
-                CreateWorkspaceRequest request = new CreateWorkspaceRequest(workspaceName,
-                                                                            CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME);
-                queue().submit(request); // will throw error if there is one ...
+                CreateWorkspaceRequest request = requests.createWorkspace(workspaceName,
+                                                                          CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME);
                 return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
             }
 
@@ -379,11 +379,10 @@
                      * @see org.jboss.dna.graph.Graph.NameWorkspace#named(java.lang.String)
                      */
                     public Workspace named( String nameOfWorkspaceToCreate ) {
-                        CloneWorkspaceRequest request = new CloneWorkspaceRequest(nameOfWorkspaceToClone,
-                                                                                  nameOfWorkspaceToCreate,
-                                                                                  CreateConflictBehavior.DO_NOT_CREATE,
-                                                                                  CloneConflictBehavior.DO_NOT_CLONE);
-                        queue().submit(request); // will throw error if there is one ...
+                        CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
+                                                                                nameOfWorkspaceToCreate,
+                                                                                CreateConflictBehavior.DO_NOT_CREATE,
+                                                                                CloneConflictBehavior.DO_NOT_CLONE);
                         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
                     }
 
@@ -393,12 +392,10 @@
                      * @see org.jboss.dna.graph.Graph.NameWorkspace#namedSomethingLike(java.lang.String)
                      */
                     public Workspace namedSomethingLike( String nameOfWorkspaceToCreate ) {
-                        CloneWorkspaceRequest request = new CloneWorkspaceRequest(
-                                                                                  nameOfWorkspaceToClone,
-                                                                                  nameOfWorkspaceToCreate,
-                                                                                  CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME,
-                                                                                  CloneConflictBehavior.DO_NOT_CLONE);
-                        queue().submit(request); // will throw error if there is one ...
+                        CloneWorkspaceRequest request = requests.cloneWorkspace(nameOfWorkspaceToClone,
+                                                                                nameOfWorkspaceToCreate,
+                                                                                CreateConflictBehavior.CREATE_WITH_ADJUSTED_NAME,
+                                                                                CloneConflictBehavior.DO_NOT_CLONE);
                         return setWorkspace(request.getActualWorkspaceName(), request.getActualLocationOfRoot());
                     }
                 };
@@ -419,7 +416,7 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( Node from ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, from.getLocation());
+        return move(from.getLocation());
     }
 
     /**
@@ -435,7 +432,17 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( Location from ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, from);
+        return new MoveAction<Conjunction<Graph>>(this.nextGraph, from) {
+            @Override
+            protected Conjunction<Graph> submit( Locations from,
+                                                 Location into ) {
+                String workspaceName = getCurrentWorkspaceName();
+                do {
+                    requests.moveBranch(from.getLocation(), into, workspaceName);
+                } while ((from = from.next()) != null);
+                return and();
+            }
+        };
     }
 
     /**
@@ -451,7 +458,7 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( String fromPath ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, Location.create(createPath(fromPath)));
+        return move(Location.create(createPath(fromPath)));
     }
 
     /**
@@ -467,7 +474,7 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( Path from ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, Location.create(from));
+        return move(Location.create(from));
     }
 
     /**
@@ -483,7 +490,7 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( UUID from ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, Location.create(from));
+        return move(Location.create(from));
     }
 
     /**
@@ -500,7 +507,7 @@
      *         be moved
      */
     public Move<Conjunction<Graph>> move( Property idProperty ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, Location.create(idProperty));
+        return move(Location.create(idProperty));
     }
 
     /**
@@ -519,8 +526,7 @@
      */
     public Move<Conjunction<Graph>> move( Property firstIdProperty,
                                           Property... additionalIdProperties ) {
-        return new MoveAction<Conjunction<Graph>>(this.nextGraph, this.requestQueue, Location.create(firstIdProperty,
-                                                                                                     additionalIdProperties));
+        return move(Location.create(firstIdProperty, additionalIdProperties));
     }
 
     /**
@@ -536,7 +542,7 @@
      *         be copied
      */
     public Copy<Graph> copy( Node from ) {
-        return new CopyAction<Graph>(this, this.requestQueue, from.getLocation());
+        return copy(from.getLocation());
     }
 
     /**
@@ -552,7 +558,23 @@
      *         be copied
      */
     public Copy<Graph> copy( Location from ) {
-        return new CopyAction<Graph>(this, this.requestQueue, from);
+        return new CopyAction<Graph>(this, from) {
+            @Override
+            protected Graph submit( Locations from,
+                                    Location into,
+                                    Name childName ) {
+                String workspaceName = getCurrentWorkspaceName();
+                do {
+                    requests.copyBranch(from.getLocation(),
+                                        workspaceName,
+                                        into,
+                                        workspaceName,
+                                        childName,
+                                        NodeConflictBehavior.APPEND);
+                } while ((from = from.next()) != null);
+                return and();
+            }
+        };
     }
 
     /**
@@ -568,7 +590,7 @@
      *         be copied
      */
     public Copy<Graph> copy( String fromPath ) {
-        return new CopyAction<Graph>(this, this.requestQueue, Location.create(createPath(fromPath)));
+        return copy(Location.create(createPath(fromPath)));
     }
 
     /**
@@ -584,7 +606,7 @@
      *         be copied
      */
     public Copy<Graph> copy( Path from ) {
-        return new CopyAction<Graph>(this, this.requestQueue, Location.create(from));
+        return copy(Location.create(from));
     }
 
     /**
@@ -600,7 +622,7 @@
      *         be copied
      */
     public Copy<Graph> copy( UUID from ) {
-        return new CopyAction<Graph>(this, this.requestQueue, Location.create(from));
+        return copy(Location.create(from));
     }
 
     /**
@@ -617,7 +639,7 @@
      *         be copied
      */
     public Copy<Graph> copy( Property idProperty ) {
-        return new CopyAction<Graph>(this, this.requestQueue, Location.create(idProperty));
+        return copy(Location.create(idProperty));
     }
 
     /**
@@ -636,7 +658,7 @@
      */
     public Copy<Graph> copy( Property firstIdProperty,
                              Property... additionalIdProperties ) {
-        return new CopyAction<Graph>(this, this.requestQueue, Location.create(firstIdProperty, additionalIdProperties));
+        return copy(Location.create(firstIdProperty, additionalIdProperties));
     }
 
     /**
@@ -646,7 +668,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( Node at ) {
-        this.requestQueue.submit(new DeleteBranchRequest(at.getLocation(), getCurrentWorkspaceName()));
+        requests.deleteBranch(at.getLocation(), getCurrentWorkspaceName());
         return nextGraph;
     }
 
@@ -657,7 +679,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( Location at ) {
-        this.requestQueue.submit(new DeleteBranchRequest(at, getCurrentWorkspaceName()));
+        requests.deleteBranch(at, getCurrentWorkspaceName());
         return nextGraph;
     }
 
@@ -668,8 +690,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( String atPath ) {
-        this.requestQueue.submit(new DeleteBranchRequest(Location.create(createPath(atPath)), getCurrentWorkspaceName()));
-        return nextGraph;
+        return delete(Location.create(createPath(atPath)));
     }
 
     /**
@@ -679,8 +700,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( Path at ) {
-        this.requestQueue.submit(new DeleteBranchRequest(Location.create(at), getCurrentWorkspaceName()));
-        return nextGraph;
+        return delete(Location.create(at));
     }
 
     /**
@@ -690,8 +710,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( UUID at ) {
-        this.requestQueue.submit(new DeleteBranchRequest(Location.create(at), getCurrentWorkspaceName()));
-        return nextGraph;
+        return delete(Location.create(at));
     }
 
     /**
@@ -702,8 +721,7 @@
      * @return an object that may be used to start another request
      */
     public Conjunction<Graph> delete( Property idProperty ) {
-        this.requestQueue.submit(new DeleteBranchRequest(Location.create(idProperty), getCurrentWorkspaceName()));
-        return nextGraph;
+        return delete(Location.create(idProperty));
     }
 
     /**
@@ -716,9 +734,7 @@
      */
     public Conjunction<Graph> delete( Property firstIdProperty,
                                       Property... additionalIdProperties ) {
-        this.requestQueue.submit(new DeleteBranchRequest(Location.create(firstIdProperty, additionalIdProperties),
-                                                         getCurrentWorkspaceName()));
-        return nextGraph;
+        return delete(Location.create(firstIdProperty, additionalIdProperties));
     }
 
     /**
@@ -758,7 +774,7 @@
             private final List<Property> properties = new LinkedList<Property>();
 
             public CreateAt<Graph> and( UUID uuid ) {
-                PropertyFactory factory = queue().getGraph().getContext().getPropertyFactory();
+                PropertyFactory factory = getContext().getPropertyFactory();
                 properties.add(factory.create(DnaLexicon.UUID, uuid));
                 return this;
             }
@@ -777,7 +793,7 @@
 
             public CreateAt<Graph> and( String name,
                                         Object... values ) {
-                ExecutionContext context = queue().getGraph().getContext();
+                ExecutionContext context = getContext();
                 PropertyFactory factory = context.getPropertyFactory();
                 NameFactory nameFactory = context.getValueFactories().getNameFactory();
                 properties.add(factory.create(nameFactory.create(name), values));
@@ -786,7 +802,7 @@
 
             public CreateAt<Graph> and( Name name,
                                         Object... values ) {
-                ExecutionContext context = queue().getGraph().getContext();
+                ExecutionContext context = getContext();
                 PropertyFactory factory = context.getPropertyFactory();
                 properties.add(factory.create(name, values));
                 return this;
@@ -830,20 +846,18 @@
 
             public Location getLocation() {
                 Location parentLoc = Location.create(parent);
-                CreateNodeRequest request = new CreateNodeRequest(parentLoc, workspaceName, childName, this.properties);
-                queue().submit(request); // immediate
+                CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
                 return request.getActualLocationOfNode();
             }
 
             public Node getNode() {
                 Location parentLoc = Location.create(parent);
-                CreateNodeRequest request = new CreateNodeRequest(parentLoc, workspaceName, childName, this.properties);
-                queue().submit(request); // immediate
+                CreateNodeRequest request = requests.createNode(parentLoc, workspaceName, childName, this.properties.iterator());
                 return getNodeAt(request.getActualLocationOfNode());
             }
 
             public Graph and() {
-                queue().submit(new CreateNodeRequest(Location.create(parent), workspaceName, childName, this.properties));
+                requests.createNode(Location.create(parent), workspaceName, childName, this.properties.iterator());
                 return Graph.this;
             }
         };
@@ -864,7 +878,7 @@
         Path at = createPath(atPath);
         Path parent = at.getParent();
         Name child = at.getLastSegment().getName();
-        this.requestQueue.submit(new CreateNodeRequest(Location.create(parent), getCurrentWorkspaceName(), child));
+        requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, EMPTY_PROPERTIES);
         return nextGraph;
     }
 
@@ -882,7 +896,7 @@
     public Conjunction<Graph> create( final Path at ) {
         Path parent = at.getParent();
         Name child = at.getLastSegment().getName();
-        this.requestQueue.submit(new CreateNodeRequest(Location.create(parent), getCurrentWorkspaceName(), child));
+        requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, EMPTY_PROPERTIES);
         return nextGraph;
     }
 
@@ -903,7 +917,7 @@
         Path at = createPath(atPath);
         Path parent = at.getParent();
         Name child = at.getLastSegment().getName();
-        this.requestQueue.submit(new CreateNodeRequest(Location.create(parent), getCurrentWorkspaceName(), child, properties));
+        requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties);
         return nextGraph;
     }
 
@@ -924,7 +938,7 @@
         CheckArg.isNotNull(at, "at");
         Path parent = at.getParent();
         Name child = at.getLastSegment().getName();
-        this.requestQueue.submit(new CreateNodeRequest(Location.create(parent), getCurrentWorkspaceName(), child, properties));
+        requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties);
         return nextGraph;
     }
 
@@ -945,7 +959,7 @@
         CheckArg.isNotNull(at, "at");
         Path parent = at.getParent();
         Name child = at.getLastSegment().getName();
-        this.requestQueue.submit(new CreateNodeRequest(Location.create(parent), getCurrentWorkspaceName(), child, properties));
+        requests.createNode(Location.create(parent), getCurrentWorkspaceName(), child, properties.iterator());
         return nextGraph;
     }
 
@@ -964,27 +978,24 @@
         final NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
         CheckArg.isNotNull(parent, "parent");
         return new CreateNode<Conjunction<Graph>>() {
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> node( String name,
                                             Property... properties ) {
                 Name child = nameFactory.create(name);
-                requestQueue.submit(new CreateNodeRequest(parent, getCurrentWorkspaceName(), child, properties));
+                requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
                 return nextGraph;
             }
 
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> node( String name,
                                             Iterator<Property> properties ) {
                 Name child = nameFactory.create(name);
-                requestQueue.submit(new CreateNodeRequest(parent, getCurrentWorkspaceName(), child, properties));
+                requests.createNode(parent, getCurrentWorkspaceName(), child, properties);
                 return nextGraph;
             }
 
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> node( String name,
                                             Iterable<Property> properties ) {
                 Name child = nameFactory.create(name);
-                requestQueue.submit(new CreateNodeRequest(parent, getCurrentWorkspaceName(), child, properties));
+                requests.createNode(parent, getCurrentWorkspaceName(), child, properties.iterator());
                 return nextGraph;
             }
         };
@@ -998,10 +1009,8 @@
      */
     public On<Conjunction<Graph>> set( final Property... properties ) {
         return new On<Conjunction<Graph>>() {
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> on( Location location ) {
-                UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(), properties);
-                queue().submit(request);
+                requests.setProperties(location, getCurrentWorkspaceName(), properties);
                 return nextGraph;
             }
 
@@ -1053,7 +1062,6 @@
      */
     public SetValues<Conjunction<Graph>> set( final Name propertyName ) {
         return new SetValues<Conjunction<Graph>>() {
-            @SuppressWarnings( "synthetic-access" )
             public SetValuesTo<Conjunction<Graph>> on( final Location location ) {
                 return new SetValuesTo<Conjunction<Graph>>() {
                     public Conjunction<Graph> to( Node value ) {
@@ -1063,17 +1071,13 @@
                     public Conjunction<Graph> to( Location value ) {
                         Reference ref = (Reference)convertReferenceValue(value);
                         Property property = getContext().getPropertyFactory().create(propertyName, ref);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
 
                     protected Conjunction<Graph> toValue( Object value ) {
                         Property property = getContext().getPropertyFactory().create(propertyName, value);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
 
@@ -1160,9 +1164,7 @@
                     public Conjunction<Graph> to( Object value ) {
                         value = convertReferenceValue(value);
                         Property property = getContext().getPropertyFactory().create(propertyName, value);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
 
@@ -1173,9 +1175,7 @@
                             otherValues[i] = convertReferenceValue(otherValues[i]);
                         }
                         Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
 
@@ -1186,9 +1186,7 @@
                             valueList.add(value);
                         }
                         Property property = getContext().getPropertyFactory().create(propertyName, valueList);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
 
@@ -1199,9 +1197,7 @@
                             valueList.add(value);
                         }
                         Property property = getContext().getPropertyFactory().create(propertyName, valueList);
-                        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                      property);
-                        queue().submit(request);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
                         return nextGraph;
                     }
                 };
@@ -1368,10 +1364,8 @@
      */
     public On<Conjunction<Graph>> remove( final Name... propertyNames ) {
         return new On<Conjunction<Graph>>() {
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> on( Location location ) {
-                RemovePropertiesRequest request = new RemovePropertiesRequest(location, getCurrentWorkspaceName(), propertyNames);
-                queue().submit(request);
+                requests.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
                 return nextGraph;
             }
 
@@ -1408,17 +1402,16 @@
      * @param propertyNames the names of the properties to be removed
      * @return the remove request object that should be used to specify the node from which the properties are to be removed.
      */
-    public On<Conjunction<Graph>> remove( String... propertyNames ) {
+    public On<Conjunction<Graph>> remove( final String... propertyNames ) {
         NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
-        final List<Name> names = new LinkedList<Name>();
-        for (String propertyName : propertyNames) {
-            names.add(nameFactory.create(propertyName));
+        int number = propertyNames.length;
+        final Name[] names = new Name[number];
+        for (int i = 0; i != number; ++i) {
+            names[i] = nameFactory.create(propertyNames[i]);
         }
         return new On<Conjunction<Graph>>() {
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> on( Location location ) {
-                RemovePropertiesRequest request = new RemovePropertiesRequest(location, getCurrentWorkspaceName(), names);
-                queue().submit(request);
+                requests.removeProperties(location, getCurrentWorkspaceName(), names);
                 return nextGraph;
             }
 
@@ -1458,9 +1451,7 @@
     public On<Collection<Property>> getProperties() {
         return new On<Collection<Property>>() {
             public Collection<Property> on( Location location ) {
-                ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location, getCurrentWorkspaceName());
-                queue().submit(request);
-                return request.getProperties();
+                return requests.readAllProperties(location, getCurrentWorkspaceName()).getProperties();
             }
 
             public Collection<Property> on( String path ) {
@@ -1500,9 +1491,7 @@
     public On<Map<Name, Property>> getPropertiesByName() {
         return new On<Map<Name, Property>>() {
             public Map<Name, Property> on( Location location ) {
-                ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location, getCurrentWorkspaceName());
-                queue().submit(request);
-                return request.getPropertiesByName();
+                return requests.readAllProperties(location, getCurrentWorkspaceName()).getPropertiesByName();
             }
 
             public Map<Name, Property> on( String path ) {
@@ -1567,9 +1556,7 @@
             }
 
             public List<Location> of( Location at ) {
-                ReadAllChildrenRequest request = new ReadAllChildrenRequest(at, getCurrentWorkspaceName());
-                queue().submit(request);
-                return request.getChildren();
+                return requests.readAllChildren(at, getCurrentWorkspaceName()).getChildren();
             }
 
             public BlockOfChildren<List<Location>> inBlockOf( final int blockSize ) {
@@ -1598,21 +1585,15 @@
                             }
 
                             public List<Location> under( Location at ) {
-                                ReadBlockOfChildrenRequest request = new ReadBlockOfChildrenRequest(at,
-                                                                                                    getCurrentWorkspaceName(),
-                                                                                                    startingIndex, blockSize);
-                                queue().submit(request);
-                                return request.getChildren();
+                                return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
+                                               .getChildren();
                             }
                         };
                     }
 
                     public List<Location> startingAfter( final Location previousSibling ) {
-                        ReadNextBlockOfChildrenRequest request = new ReadNextBlockOfChildrenRequest(previousSibling,
-                                                                                                    getCurrentWorkspaceName(),
-                                                                                                    blockSize);
-                        queue().submit(request);
-                        return request.getChildren();
+                        return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
+                                       .getChildren();
                     }
 
                     public List<Location> startingAfter( String pathOfPreviousSibling ) {
@@ -1688,9 +1669,7 @@
             }
 
             public Property on( Location at ) {
-                ReadPropertyRequest request = new ReadPropertyRequest(at, getCurrentWorkspaceName(), name);
-                queue().submit(request);
-                return request.getProperty();
+                return requests.readProperty(at, getCurrentWorkspaceName(), name).getProperty();
             }
         };
     }
@@ -1712,9 +1691,7 @@
      * @return the node that is read from the repository
      */
     public Node getNodeAt( Location location ) {
-        ReadNodeRequest request = new ReadNodeRequest(location, getCurrentWorkspaceName());
-        this.requestQueue.submit(request);
-        return new GraphNode(request);
+        return new GraphNode(requests.readNode(location, getCurrentWorkspaceName()));
     }
 
     /**
@@ -1794,9 +1771,7 @@
     public At<Subgraph> getSubgraphOfDepth( final int depth ) {
         return new At<Subgraph>() {
             public Subgraph at( Location location ) {
-                ReadBranchRequest request = new ReadBranchRequest(location, getCurrentWorkspaceName(), depth);
-                queue().submit(request);
-                return new SubgraphResults(request);
+                return new SubgraphResults(requests.readBranch(location, getCurrentWorkspaceName(), depth));
             }
 
             public Subgraph at( String path ) {
@@ -1868,7 +1843,6 @@
                 return into(Location.create(uuid));
             }
 
-            @SuppressWarnings( "synthetic-access" )
             public Conjunction<Graph> into( Location at ) throws IOException, SAXException {
                 GraphImporter importer = new GraphImporter(Graph.this);
                 importer.importXml(uri, at, skipRootElement).execute(); // 'importXml' creates and uses a new batch
@@ -1925,7 +1899,7 @@
      * @see Results
      */
     public Batch batch() {
-        return new Batch(new LinkedList<Request>());
+        return new Batch(null);
     }
 
     /**
@@ -1934,14 +1908,14 @@
      * {@link #getSourceName() repository source}. The {@link Results results} are not available until the {@link Batch#execute()}
      * method is invoked.
      * 
-     * @param queue the queue where all batched requests should be placed
+     * @param builder the request builder that should be used; may not be null
      * @return the batch object used to build and accumulate multiple requests and to submit them all for processing at once.
      * @see Batch#execute()
      * @see Results
      */
-    public Batch batch( LinkedList<Request> queue ) {
-        CheckArg.isNotNull(queue, "queue");
-        return new Batch(queue);
+    public Batch batch( BatchRequestBuilder builder ) {
+        CheckArg.isNotNull(builder, "builder");
+        return new Batch(builder);
     }
 
     /**
@@ -1953,13 +1927,13 @@
      */
     @Immutable
     public final class Batch implements Executable<Node> {
-        protected final CompositingRequestQueue requestQueue;
+        protected final BatchRequestBuilder requestQueue;
         protected final BatchConjunction nextRequests;
         protected final String workspaceName;
         protected boolean executed = false;
 
-        /*package*/Batch( LinkedList<Request> queue ) {
-            this.requestQueue = new CompositingRequestQueue(queue);
+        /*package*/Batch( BatchRequestBuilder builder ) {
+            this.requestQueue = builder != null ? builder : new BatchRequestBuilder();
             this.workspaceName = Graph.this.getCurrentWorkspaceName();
             this.nextRequests = new BatchConjunction() {
                 public Batch and() {
@@ -1967,8 +1941,7 @@
                 }
 
                 public Results execute() {
-                    executed = true;
-                    return Batch.this.requestQueue.execute();
+                    return Batch.this.execute();
                 }
             };
         }
@@ -1988,7 +1961,7 @@
          * @return true if there are some requests in this batch that need to be executed, or false execution is not required
          */
         public boolean isExecuteRequired() {
-            return !executed || requestQueue.size() != 0;
+            return !executed || requestQueue.hasRequests();
         }
 
         /**
@@ -2028,8 +2001,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( Node from ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, from.getLocation());
+            return move(from.getLocation());
         }
 
         /**
@@ -2044,9 +2016,19 @@
          * @return the object that can be used to specify addition nodes to be moved or the location of the node where the node is
          *         to be moved
          */
-        public Move<BatchConjunction> move( Location from ) {
+        public final Move<BatchConjunction> move( Location from ) {
             assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, from);
+            return new MoveAction<BatchConjunction>(this.nextRequests, from) {
+                @Override
+                protected BatchConjunction submit( Locations from,
+                                                   Location into ) {
+                    String workspaceName = getCurrentWorkspaceName();
+                    do {
+                        requestQueue.moveBranch(from.getLocation(), into, workspaceName);
+                    } while ((from = from.next()) != null);
+                    return and();
+                }
+            };
         }
 
         /**
@@ -2062,8 +2044,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( String fromPath ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(createPath(fromPath)));
+            return move(Location.create(createPath(fromPath)));
         }
 
         /**
@@ -2079,8 +2060,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( Path from ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(from));
+            return move(Location.create(from));
         }
 
         /**
@@ -2096,8 +2076,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( UUID from ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(from));
+            return move(Location.create(from));
         }
 
         /**
@@ -2114,8 +2093,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( Property idProperty ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(idProperty));
+            return move(Location.create(idProperty));
         }
 
         /**
@@ -2134,9 +2112,7 @@
          */
         public Move<BatchConjunction> move( Property firstIdProperty,
                                             Property... additionalIdProperties ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(firstIdProperty,
-                                                                                                          additionalIdProperties));
+            return move(Location.create(firstIdProperty, additionalIdProperties));
         }
 
         /**
@@ -2153,8 +2129,7 @@
          *         to be moved
          */
         public Move<BatchConjunction> move( Iterable<Property> idProperties ) {
-            assertNotExecuted();
-            return new MoveAction<BatchConjunction>(this.nextRequests, this.requestQueue, Location.create(idProperties));
+            return move(Location.create(idProperties));
         }
 
         /**
@@ -2170,8 +2145,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( Node from ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, from.getLocation());
+            return copy(from.getLocation());
         }
 
         /**
@@ -2188,7 +2162,18 @@
          */
         public Copy<BatchConjunction> copy( Location from ) {
             assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, from);
+            return new CopyAction<BatchConjunction>(this.nextRequests, from) {
+                @Override
+                protected BatchConjunction submit( Locations from,
+                                                   Location into,
+                                                   Name copyName ) {
+                    String workspaceName = getCurrentWorkspaceName();
+                    do {
+                        requestQueue.copyBranch(from.getLocation(), workspaceName, into, workspaceName, copyName);
+                    } while ((from = from.next()) != null);
+                    return and();
+                }
+            };
         }
 
         /**
@@ -2204,8 +2189,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( String fromPath ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(createPath(fromPath)));
+            return copy(Location.create(createPath(fromPath)));
         }
 
         /**
@@ -2221,8 +2205,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( Path from ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(from));
+            return copy(Location.create(from));
         }
 
         /**
@@ -2238,8 +2221,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( UUID from ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(from));
+            return copy(Location.create(from));
         }
 
         /**
@@ -2256,8 +2238,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( Property idProperty ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(idProperty));
+            return copy(Location.create(idProperty));
         }
 
         /**
@@ -2276,9 +2257,7 @@
          */
         public Copy<BatchConjunction> copy( Property firstIdProperty,
                                             Property... additionalIdProperties ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(firstIdProperty,
-                                                                                                     additionalIdProperties));
+            return copy(Location.create(firstIdProperty, additionalIdProperties));
         }
 
         /**
@@ -2295,8 +2274,7 @@
          *         is to be copied
          */
         public Copy<BatchConjunction> copy( Iterable<Property> idProperties ) {
-            assertNotExecuted();
-            return new CopyAction<BatchConjunction>(nextRequests, this.requestQueue, Location.create(idProperties));
+            return copy(Location.create(idProperties));
         }
 
         /**
@@ -2310,9 +2288,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( Node at ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(at.getLocation(), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(at.getLocation());
         }
 
         /**
@@ -2327,7 +2303,7 @@
          */
         public BatchConjunction delete( Location at ) {
             assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(at, getCurrentWorkspaceName()));
+            this.requestQueue.deleteBranch(at, getCurrentWorkspaceName());
             return nextRequests;
         }
 
@@ -2342,9 +2318,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( String atPath ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(createPath(atPath)), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(createPath(atPath)));
         }
 
         /**
@@ -2358,9 +2332,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( Path at ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(at), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(at));
         }
 
         /**
@@ -2374,9 +2346,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( UUID at ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(at), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(at));
         }
 
         /**
@@ -2390,9 +2360,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( Property idProperty ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(idProperty), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(idProperty));
         }
 
         /**
@@ -2409,10 +2377,7 @@
          */
         public BatchConjunction delete( Property firstIdProperty,
                                         Property... additionalIdProperties ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(firstIdProperty, additionalIdProperties),
-                                                             getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(firstIdProperty, additionalIdProperties));
         }
 
         /**
@@ -2427,9 +2392,7 @@
          * @return an object that may be used to start another request
          */
         public BatchConjunction delete( Iterable<Property> idProperties ) {
-            assertNotExecuted();
-            this.requestQueue.submit(new DeleteBranchRequest(Location.create(idProperties), getCurrentWorkspaceName()));
-            return nextRequests;
+            return delete(Location.create(idProperties));
         }
 
         /**
@@ -2444,11 +2407,7 @@
          *         node where the node is to be created
          */
         public Create<Batch> create( String atPath ) {
-            assertNotExecuted();
-            Path at = createPath(atPath);
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name);
+            return create(createPath(atPath));
         }
 
         /**
@@ -2465,11 +2424,7 @@
          */
         public Create<Batch> create( String atPath,
                                      Property property ) {
-            assertNotExecuted();
-            Path at = createPath(atPath);
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name).with(property);
+            return create(createPath(atPath)).with(property);
         }
 
         /**
@@ -2488,12 +2443,7 @@
         public Create<Batch> create( String atPath,
                                      Property firstProperty,
                                      Property... additionalProperties ) {
-            assertNotExecuted();
-            Path at = createPath(atPath);
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name).with(firstProperty,
-                                                                                                                              additionalProperties);
+            return create(createPath(atPath)).with(firstProperty, additionalProperties);
         }
 
         /**
@@ -2507,14 +2457,38 @@
          * @return the object that can be used to specify addition properties for the new node to be copied or the location of the
          *         node where the node is to be created
          */
-        public Create<Batch> create( Path at ) {
+        public final Create<Batch> create( Path at ) {
             assertNotExecuted();
             CheckArg.isNotNull(at, "at");
             Path parent = at.getParent();
             Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name);
+            return create(Location.create(parent), name);
         }
 
+        protected final CreateAction<Batch> create( Location parent,
+                                                    Name child ) {
+            return new CreateAction<Batch>(this, parent, getCurrentWorkspaceName(), child) {
+                @Override
+                protected Batch submit( Location parent,
+                                        String workspaceName,
+                                        Name childName,
+                                        Collection<Property> properties ) {
+                    requestQueue.createNode(parent, workspaceName, childName, properties.iterator());
+                    return Batch.this;
+                }
+
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.graph.Graph.Executable#execute()
+                 */
+                public Results execute() {
+                    and();
+                    return Batch.this.execute();
+                }
+            };
+        }
+
         /**
          * Begin the request to create a node located at the supplied path.
          * <p>
@@ -2529,12 +2503,7 @@
          */
         public Create<Batch> create( Path at,
                                      Iterable<Property> properties ) {
-            assertNotExecuted();
-            CheckArg.isNotNull(at, "at");
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            CreateAction<Batch> action = new CreateAction<Batch>(this, requestQueue, Location.create(parent),
-                                                                 getCurrentWorkspaceName(), name);
+            Create<Batch> action = create(at);
             for (Property property : properties) {
                 action.and(property);
             }
@@ -2555,11 +2524,7 @@
          */
         public Create<Batch> create( Path at,
                                      Property property ) {
-            assertNotExecuted();
-            CheckArg.isNotNull(at, "at");
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name).with(property);
+            return create(at).with(property);
         }
 
         /**
@@ -2578,12 +2543,7 @@
         public Create<Batch> create( Path at,
                                      Property firstProperty,
                                      Property... additionalProperties ) {
-            assertNotExecuted();
-            CheckArg.isNotNull(at, "at");
-            Path parent = at.getParent();
-            Name name = at.getLastSegment().getName();
-            return new CreateAction<Batch>(this, requestQueue, Location.create(parent), getCurrentWorkspaceName(), name).with(firstProperty,
-                                                                                                                              additionalProperties);
+            return create(at).with(firstProperty, additionalProperties);
         }
 
         /**
@@ -2595,7 +2555,14 @@
          */
         public CreateNodeNamed<Batch> createUnder( Location parent ) {
             CheckArg.isNotNull(parent, "parent");
-            return new CreateNodeNamedAction<Batch>(this, requestQueue, parent, getCurrentWorkspaceName());
+            return new CreateNodeNamedAction<Batch>(this, parent) {
+                @Override
+                protected CreateAction<Batch> createWith( Batch batch,
+                                                          Location parent,
+                                                          Name childName ) {
+                    return Batch.this.create(parent, childName);
+                }
+            };
         }
 
         /**
@@ -2607,8 +2574,7 @@
         public On<BatchConjunction> set( final Property... properties ) {
             return new On<BatchConjunction>() {
                 public BatchConjunction on( Location location ) {
-                    UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(), properties);
-                    requestQueue.submit(request);
+                    requestQueue.setProperties(location, getCurrentWorkspaceName(), properties);
                     return nextRequests;
                 }
 
@@ -2669,17 +2635,13 @@
                         public BatchConjunction to( Location value ) {
                             Reference ref = (Reference)convertReferenceValue(value);
                             Property property = getContext().getPropertyFactory().create(propertyName, ref);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
                         protected BatchConjunction toValue( Object value ) {
                             Property property = getContext().getPropertyFactory().create(propertyName, value);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
@@ -2766,9 +2728,7 @@
                         public BatchConjunction to( Object value ) {
                             value = convertReferenceValue(value);
                             Property property = getContext().getPropertyFactory().create(propertyName, value);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
@@ -2779,9 +2739,7 @@
                                 otherValues[i] = convertReferenceValue(otherValues[i]);
                             }
                             Property property = getContext().getPropertyFactory().create(propertyName, firstValue, otherValues);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
@@ -2792,9 +2750,7 @@
                                 valueList.add(value);
                             }
                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
@@ -2805,9 +2761,7 @@
                                 valueList.add(value);
                             }
                             Property property = getContext().getPropertyFactory().create(propertyName, valueList);
-                            UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                          property);
-                            requestQueue.submit(request);
+                            requestQueue.setProperty(location, getCurrentWorkspaceName(), property);
                             return nextRequests;
                         }
 
@@ -2976,9 +2930,7 @@
         public On<BatchConjunction> remove( final Name... propertyNames ) {
             return new On<BatchConjunction>() {
                 public BatchConjunction on( Location location ) {
-                    RemovePropertiesRequest request = new RemovePropertiesRequest(location, getCurrentWorkspaceName(),
-                                                                                  propertyNames);
-                    requestQueue.submit(request);
+                    requestQueue.removeProperties(location, getCurrentWorkspaceName(), propertyNames);
                     return nextRequests;
                 }
 
@@ -3017,14 +2969,14 @@
          */
         public On<BatchConjunction> remove( String... propertyNames ) {
             NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
-            final List<Name> names = new LinkedList<Name>();
-            for (String propertyName : propertyNames) {
-                names.add(nameFactory.create(propertyName));
+            int number = propertyNames.length;
+            final Name[] names = new Name[number];
+            for (int i = 0; i != number; ++i) {
+                names[i] = nameFactory.create(propertyNames[i]);
             }
             return new On<BatchConjunction>() {
                 public BatchConjunction on( Location location ) {
-                    RemovePropertiesRequest request = new RemovePropertiesRequest(location, getCurrentWorkspaceName(), names);
-                    requestQueue.submit(request);
+                    requestQueue.removeProperties(location, getCurrentWorkspaceName(), names);
                     return nextRequests;
                 }
 
@@ -3081,8 +3033,7 @@
          */
         public BatchConjunction read( Location location ) {
             assertNotExecuted();
-            ReadNodeRequest request = new ReadNodeRequest(location, getCurrentWorkspaceName());
-            requestQueue.submit(request);
+            requestQueue.readNode(location, getCurrentWorkspaceName());
             return nextRequests;
         }
 
@@ -3216,8 +3167,7 @@
                 }
 
                 public BatchConjunction on( Location at ) {
-                    ReadPropertyRequest request = new ReadPropertyRequest(at, getCurrentWorkspaceName(), name);
-                    requestQueue.submit(request);
+                    requestQueue.readProperty(at, getCurrentWorkspaceName(), name);
                     return Batch.this.nextRequests;
                 }
             };
@@ -3237,8 +3187,7 @@
             assertNotExecuted();
             return new On<BatchConjunction>() {
                 public BatchConjunction on( Location location ) {
-                    ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(location, getCurrentWorkspaceName());
-                    requestQueue.submit(request);
+                    requestQueue.readAllProperties(location, getCurrentWorkspaceName());
                     return Batch.this.nextRequests;
                 }
 
@@ -3308,8 +3257,7 @@
                 }
 
                 public BatchConjunction of( Location at ) {
-                    ReadAllChildrenRequest request = new ReadAllChildrenRequest(at, getCurrentWorkspaceName());
-                    requestQueue.submit(request);
+                    requestQueue.readAllChildren(at, getCurrentWorkspaceName());
                     return Batch.this.nextRequests;
                 }
             };
@@ -3330,8 +3278,7 @@
             assertNotExecuted();
             return new At<BatchConjunction>() {
                 public BatchConjunction at( Location location ) {
-                    ReadBranchRequest request = new ReadBranchRequest(location, getCurrentWorkspaceName(), depth);
-                    requestQueue.submit(request);
+                    requestQueue.readBranch(location, getCurrentWorkspaceName());
                     return Batch.this.nextRequests;
                 }
 
@@ -3368,7 +3315,17 @@
          * @see org.jboss.dna.graph.Graph.Executable#execute()
          */
         public Results execute() {
-            return this.requestQueue.execute();
+            executed = true;
+            Request request = requestQueue.pop();
+            if (request == null) {
+                return new BatchResults();
+            }
+            Graph.this.execute(request);
+            if (request instanceof CompositeRequest) {
+                CompositeRequest composite = (CompositeRequest)request;
+                return new BatchResults(composite.getRequests());
+            }
+            return new BatchResults(request);
         }
     }
 
@@ -4700,116 +4657,6 @@
     }
 
     // ----------------------------------------------------------------------------------------------------------------
-    // RequestQueue and the different implementations
-    // ----------------------------------------------------------------------------------------------------------------
-
-    /**
-     * A queue to which each each {@link AbstractAction} can submit its {@link Request} objects, either in single or multiple.
-     * This interface abstracts away from the {@link AbstractAction} what it is to do with its {@link Request} objects, allowing
-     * the same <code>AbstractAction</code> classes to be used by the {@link Graph} and {@link Graph.Batch} components.
-     * 
-     * @author Randall Hauch
-     */
-    protected interface RequestQueue extends Executable<Node> {
-        Graph getGraph();
-
-        void submit( Request request );
-
-        void submit( List<Request> requests );
-
-    }
-
-    /**
-     * A RequestQueue that is used by the Graph instance to immediately execute the submitted requests.
-     * 
-     * @author Randall Hauch
-     */
-    @NotThreadSafe
-    protected class GraphRequestQueue implements RequestQueue {
-        public Graph getGraph() {
-            return Graph.this;
-        }
-
-        public void submit( Request request ) {
-            // Execute the request immediately ...
-            Graph.this.execute(request);
-        }
-
-        public void submit( List<Request> requests ) {
-            Request request = CompositeRequest.with(requests);
-            // Execute the request immediately ...
-            Graph.this.execute(request);
-        }
-
-        public Results execute() {
-            throw new UnsupportedOperationException();
-        }
-    }
-
-    /**
-     * A RequestQueue that is used by the {@link Graph.Batch} component to enqueue {@link Request}s until they are to be submitted
-     * to the repository connections.
-     * 
-     * @author Randall Hauch
-     */
-    @NotThreadSafe
-    /*package*/class CompositingRequestQueue implements RequestQueue {
-        private final LinkedList<Request> requests;
-
-        CompositingRequestQueue( LinkedList<Request> queue ) {
-            assert queue != null;
-            this.requests = queue;
-        }
-
-        public Graph getGraph() {
-            return Graph.this;
-        }
-
-        public List<Request> getRequests() {
-            return this.requests;
-        }
-
-        /**
-         * Determine the number of requests that are currently in the queue.
-         * 
-         * @return the number of currently-enqueued requests
-         */
-        public int size() {
-            return requests.size();
-        }
-
-        public void submit( Request request ) {
-            if (!requests.isEmpty() && request instanceof UpdatePropertiesRequest) {
-                // If the previous request was also an update, then maybe they can be merged ...
-                Request previous = requests.getLast();
-                if (previous instanceof UpdatePropertiesRequest) {
-                    // They can be merged if the have the same location ...
-                    UpdatePropertiesRequest next = (UpdatePropertiesRequest)request;
-                    UpdatePropertiesRequest prev = (UpdatePropertiesRequest)previous;
-                    if (next.on().equals(prev.on())) {
-                        requests.removeLast();
-                        requests.add(prev.mergeWith(next));
-                    }
-                }
-            }
-            this.requests.add(request);
-        }
-
-        public void submit( List<Request> requests ) {
-            this.requests.addAll(requests);
-        }
-
-        public Results execute() {
-            if (!requests.isEmpty()) {
-                // Execute the requests ...
-                Request request = CompositeRequest.with(requests);
-                Graph.this.execute(request);
-            }
-            return new BatchResults(requests);
-        }
-    }
-
-    // ----------------------------------------------------------------------------------------------------------------
     // Node Implementation
     // ----------------------------------------------------------------------------------------------------------------
     @Immutable
@@ -4920,6 +4767,39 @@
             }
         }
 
+        /*package*/BatchResults( Request request ) {
+            if (request instanceof ReadAllPropertiesRequest) {
+                ReadAllPropertiesRequest read = (ReadAllPropertiesRequest)request;
+                getOrCreateNode(read.getActualLocationOfNode()).setProperties(read.getPropertiesByName());
+            } else if (request instanceof ReadPropertyRequest) {
+                ReadPropertyRequest read = (ReadPropertyRequest)request;
+                getOrCreateNode(read.getActualLocationOfNode()).addProperty(read.getProperty());
+            } else if (request instanceof ReadNodeRequest) {
+                ReadNodeRequest read = (ReadNodeRequest)request;
+                BatchResultsNode node = getOrCreateNode(read.getActualLocationOfNode());
+                node.setProperties(read.getPropertiesByName());
+                node.setChildren(read.getChildren());
+            } else if (request instanceof ReadBlockOfChildrenRequest) {
+                throw new IllegalStateException();
+            } else if (request instanceof ReadAllChildrenRequest) {
+                ReadAllChildrenRequest read = (ReadAllChildrenRequest)request;
+                getOrCreateNode(read.getActualLocationOfNode()).setChildren(read.getChildren());
+            } else if (request instanceof ReadBranchRequest) {
+                ReadBranchRequest read = (ReadBranchRequest)request;
+                for (Location location : read) {
+                    BatchResultsNode node = getOrCreateNode(location);
+                    node.setProperties(read.getPropertiesFor(location));
+                    node.setChildren(read.getChildren(location));
+                }
+            }
+            for (Map.Entry<Path, BatchResultsNode> entry : nodes.entrySet()) {
+                entry.getValue().freeze();
+            }
+        }
+
+        /*package*/BatchResults() {
+        }
+
         private BatchResultsNode getOrCreateNode( Location location ) {
             BatchResultsNode node = nodes.get(location);
             if (node == null) {
@@ -5290,20 +5170,13 @@
     // Action Implementations
     // ----------------------------------------------------------------------------------------------------------------
     @Immutable
-    protected static abstract class AbstractAction<T> implements Conjunction<T>, Executable<Node> {
-        private final RequestQueue queue;
+    protected abstract class AbstractAction<T> implements Conjunction<T> {
         private final T afterConjunction;
 
-        /*package*/AbstractAction( T afterConjunction,
-                                    RequestQueue queue ) {
-            this.queue = queue;
+        /*package*/AbstractAction( T afterConjunction ) {
             this.afterConjunction = afterConjunction;
         }
 
-        /*package*/RequestQueue queue() {
-            return this.queue;
-        }
-
         /*package*/T afterConjunction() {
             return this.afterConjunction;
         }
@@ -5313,22 +5186,17 @@
         }
 
         /*package*/Path createPath( String path ) {
-            return queue.getGraph().getContext().getValueFactories().getPathFactory().create(path);
+            return Graph.this.getContext().getValueFactories().getPathFactory().create(path);
         }
-
-        public Results execute() {
-            return queue.execute();
-        }
     }
 
     @NotThreadSafe
-    protected class MoveAction<T> extends AbstractAction<T> implements Move<T> {
+    protected abstract class MoveAction<T> extends AbstractAction<T> implements Move<T> {
         private final Locations from;
 
         /*package*/MoveAction( T afterConjunction,
-                                RequestQueue queue,
                                 Location from ) {
-            super(afterConjunction, queue);
+            super(afterConjunction);
             this.from = new Locations(from);
         }
 
@@ -5371,65 +5239,46 @@
         /**
          * Submit any requests to move the targets into the supplied parent location
          * 
+         * @param from the location(s) that are being moved; never null
          * @param into the parent location
          * @return this object, for method chaining
          */
-        private T submit( Location into ) {
-            String workspaceName = getCurrentWorkspaceName();
-            if (this.from.hasNext()) {
-                List<Request> requests = new LinkedList<Request>();
-                Locations locations = this.from;
-                while (locations.hasNext()) {
-                    Location location = locations.getLocation();
-                    requests.add(new MoveBranchRequest(location, into, workspaceName));
-                    locations = locations.next();
-                }
-                queue().submit(requests);
-            } else {
-                queue().submit(new MoveBranchRequest(this.from.getLocation(), into, workspaceName));
-            }
-            return and();
-        }
+        protected abstract T submit( Locations from,
+                                     Location into );
 
         public T into( Location into ) {
-            return submit(into);
+            return submit(from, into);
         }
 
         public T into( Path into ) {
-            return submit(Location.create(into));
+            return submit(from, Location.create(into));
         }
 
         public T into( UUID into ) {
-            return submit(Location.create(into));
+            return submit(from, Location.create(into));
         }
 
         public T into( Property firstIdProperty,
                        Property... additionalIdProperties ) {
-            return submit(Location.create(firstIdProperty, additionalIdProperties));
+            return submit(from, Location.create(firstIdProperty, additionalIdProperties));
         }
 
         public T into( Property into ) {
-            return submit(Location.create(into));
+            return submit(from, Location.create(into));
         }
 
         public T into( String into ) {
-            return submit(Location.create(createPath(into)));
+            return submit(from, Location.create(createPath(into)));
         }
-
-        @Override
-        public Results execute() {
-            return queue().execute();
-        }
     }
 
     @NotThreadSafe
-    protected class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
+    protected abstract class CopyAction<T> extends AbstractAction<T> implements Copy<T> {
         private final Locations from;
 
         /*package*/CopyAction( T afterConjunction,
-                                RequestQueue queue,
                                 Location from ) {
-            super(afterConjunction, queue);
+            super(afterConjunction);
             this.from = new Locations(from);
         }
 
@@ -5472,51 +5321,38 @@
         /**
          * Submit any requests to move the targets into the supplied parent location
          * 
+         * @param from the locations that are being copied
          * @param into the parent location
          * @param nameForCopy the name that should be used for the copy, or null if the name should be the same as the original
          * @return this object, for method chaining
          */
-        private T submit( Location into,
-                          Name nameForCopy ) {
-            String workspaceName = getCurrentWorkspaceName();
-            if (this.from.hasNext()) {
-                List<Request> requests = new LinkedList<Request>();
-                Locations locations = this.from;
-                while (locations.hasNext()) {
-                    Location location = locations.getLocation();
-                    requests.add(new CopyBranchRequest(location, workspaceName, into, workspaceName, nameForCopy));
-                    locations = locations.next();
-                }
-                queue().submit(requests);
-            } else {
-                queue().submit(new CopyBranchRequest(this.from.getLocation(), workspaceName, into, workspaceName, nameForCopy));
-            }
-            return and();
-        }
+        protected abstract T submit( Locations from,
+                                     Location into,
+                                     Name nameForCopy );
 
         public T into( Location into ) {
-            return submit(into, null);
+            return submit(from, into, null);
         }
 
         public T into( Path into ) {
-            return submit(Location.create(into), null);
+            return submit(from, Location.create(into), null);
         }
 
         public T into( UUID into ) {
-            return submit(Location.create(into), null);
+            return submit(from, Location.create(into), null);
         }
 
         public T into( Property firstIdProperty,
                        Property... additionalIdProperties ) {
-            return submit(Location.create(firstIdProperty, additionalIdProperties), null);
+            return submit(from, Location.create(firstIdProperty, additionalIdProperties), null);
         }
 
         public T into( Property into ) {
-            return submit(Location.create(into), null);
+            return submit(from, Location.create(into), null);
         }
 
         public T into( String into ) {
-            return submit(Location.create(createPath(into)), null);
+            return submit(from, Location.create(createPath(into)), null);
         }
 
         public T to( Location desiredLocation ) {
@@ -5528,7 +5364,7 @@
                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredLocation));
             }
             Path parent = desiredPath.getParent();
-            return submit(Location.create(parent), desiredPath.getLastSegment().getName());
+            return submit(from, Location.create(parent), desiredPath.getLastSegment().getName());
         }
 
         public T to( Path desiredPath ) {
@@ -5536,77 +5372,72 @@
                 throw new IllegalArgumentException(GraphI18n.unableToCopyToTheRoot.text(this.from, desiredPath));
             }
             Path parent = desiredPath.getParent();
-            return submit(Location.create(parent), desiredPath.getLastSegment().getName());
+            return submit(from, Location.create(parent), desiredPath.getLastSegment().getName());
         }
 
         public T to( String desiredPath ) {
             return to(createPath(desiredPath));
         }
-
-        @Override
-        public Results execute() {
-            return queue().execute();
-        }
     }
 
     @NotThreadSafe
-    protected static class CreateAction<T> extends AbstractAction<T> implements Create<T> {
+    protected abstract class CreateAction<T> extends AbstractAction<T> implements Create<T> {
         private final String workspaceName;
         private final Location parent;
         private final Name childName;
-        private final List<Property> properties = new LinkedList<Property>();
+        private final Map<Name, Property> properties = new HashMap<Name, Property>();
+        private boolean submitted = false;
 
         /*package*/CreateAction( T afterConjunction,
-                                  RequestQueue queue,
                                   Location parent,
                                   String workspaceName,
                                   Name childName ) {
-            super(afterConjunction, queue);
+            super(afterConjunction);
             this.parent = parent;
             this.workspaceName = workspaceName;
             this.childName = childName;
         }
 
         public Create<T> and( UUID uuid ) {
-            PropertyFactory factory = queue().getGraph().getContext().getPropertyFactory();
-            properties.add(factory.create(DnaLexicon.UUID, uuid));
+            PropertyFactory factory = getContext().getPropertyFactory();
+            properties.put(DnaLexicon.UUID, factory.create(DnaLexicon.UUID, uuid));
             return this;
         }
 
         public Create<T> and( Property property ) {
-            properties.add(property);
+            properties.put(property.getName(), property);
             return this;
         }
 
         public Create<T> and( Iterable<Property> properties ) {
             for (Property property : properties) {
-                this.properties.add(property);
+                this.properties.put(property.getName(), property);
             }
             return this;
         }
 
         public Create<T> and( String name,
                               Object... values ) {
-            ExecutionContext context = queue().getGraph().getContext();
+            ExecutionContext context = getContext();
             PropertyFactory factory = context.getPropertyFactory();
             NameFactory nameFactory = context.getValueFactories().getNameFactory();
-            properties.add(factory.create(nameFactory.create(name), values));
+            Name propertyName = nameFactory.create(name);
+            properties.put(propertyName, factory.create(propertyName, values));
             return this;
         }
 
         public Create<T> and( Name name,
                               Object... values ) {
-            ExecutionContext context = queue().getGraph().getContext();
-            PropertyFactory factory = context.getPropertyFactory();
-            properties.add(factory.create(name, values));
+            PropertyFactory factory = getContext().getPropertyFactory();
+            properties.put(name, factory.create(name, values));
             return this;
         }
 
         public Create<T> and( Property property,
                               Property... additionalProperties ) {
-            properties.add(property);
+            properties.put(property.getName(), property);
             for (Property additionalProperty : additionalProperties) {
-                properties.add(additionalProperty);
+                properties.put(additionalProperty.getName(), additionalProperty);
             }
             return this;
         }
@@ -5638,43 +5469,44 @@
             return and(name, values);
         }
 
+        protected abstract T submit( Location parent,
+                                     String workspaceName,
+                                     Name childName,
+                                     Collection<Property> properties );
+
         @Override
         public T and() {
-            this.queue().submit(new CreateNodeRequest(parent, workspaceName, childName, this.properties));
+            if (!submitted) {
+                submit(parent, workspaceName, childName, this.properties.values());
+                submitted = true;
+            }
             return super.and();
         }
-
-        @Override
-        public Results execute() {
-            this.queue().submit(new CreateNodeRequest(parent, workspaceName, childName, this.properties));
-            return queue().execute();
-        }
     }
 
     @NotThreadSafe
-    protected static class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
+    protected abstract class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
         private final Location parent;
-        private final String workspaceName;
 
-        /*package*/CreateNodeNamedAction( T afterConjunction,
-                                           RequestQueue queue,
-                                           Location parent,
-                                           String workspaceName ) {
-            super(afterConjunction, queue);
+        protected CreateNodeNamedAction( T afterConjunction,
+                                         Location parent ) {
+            super(afterConjunction);
             this.parent = parent;
-            this.workspaceName = workspaceName;
         }
 
         public CreateAction<T> nodeNamed( String name ) {
-            ExecutionContext context = queue().getGraph().getContext();
-            NameFactory factory = context.getValueFactories().getNameFactory();
+            NameFactory factory = getContext().getValueFactories().getNameFactory();
             Name nameObj = factory.create(name);
-            return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, nameObj);
+            return createWith(afterConjunction(), parent, nameObj);
         }
 
         public CreateAction<T> nodeNamed( Name name ) {
-            return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, name);
+            return createWith(afterConjunction(), parent, name);
         }
+
+        protected abstract CreateAction<T> createWith( T afterConjunction,
+                                                       Location parent,
+                                                       Name nodeName );
     }
 
     @Immutable

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -254,7 +254,7 @@
             if (firstProperty == null) {
                 batch.create(path).and();
             } else {
-                batch.create(path, firstProperty, additionalProperties);
+                batch.create(path, firstProperty, additionalProperties).and();
             }
         }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -208,12 +208,13 @@
         InMemoryNode node = getTargetNode(workspace, request, request.on());
         if (node == null) return;
         // Now set (or remove) the properties to the supplied node ...
-        for (Property property : request.properties()) {
-            Name propName = property.getName();
-            if (property.size() == 0) {
-                node.getProperties().remove(propName);
+        for (Map.Entry<Name, Property> propertyEntry : request.properties().entrySet()) {
+            Property property = propertyEntry.getValue();
+            if (property == null) {
+                node.getProperties().remove(propertyEntry.getKey());
                 continue;
             }
+            Name propName = property.getName();
             if (!propName.equals(DnaLexicon.UUID)) {
                 node.getProperties().put(propName, property);
             }

Added: 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	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -0,0 +1,671 @@
+/*
+ * 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.
+ *
+ * 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.request;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.NodeConflictBehavior;
+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.request.CloneWorkspaceRequest.CloneConflictBehavior;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
+
+/**
+ * A component that can be used to build up a list of requests. This implementation does perform some simple optimizations, such
+ * as combining adjacent compatible requests.
+ * <p>
+ * This builder can be used to add multiple requests. When the enqueued requests are to be processed, calling {@link #pop()} will
+ * remove and return the enqueued requests (as a {@link CompositeRequest} if there is more than one enqueued request).
+ * </p>
+ */
+public class BatchRequestBuilder {
+
+    private LinkedList<Request> requests;
+    private NodeChange pendingRequest;
+
+    public BatchRequestBuilder() {
+        this.requests = new LinkedList<Request>();
+    }
+
+    public BatchRequestBuilder( LinkedList<Request> requests ) {
+        this.requests = requests != null ? requests : new LinkedList<Request>();
+    }
+
+    /**
+     * Determine whether this builder has built any requests.
+     * 
+     * @return true if there are requests (i.e., {@link #pop()} will return a non-null request), or false if there are no requests
+     */
+    public boolean hasRequests() {
+        return pendingRequest != null || !requests.isEmpty();
+    }
+
+    /**
+     * Finish any pending request
+     */
+    public void finishPendingRequest() {
+        if (pendingRequest != null) {
+            // There's a pending request, we need to build it ...
+            add(pendingRequest.toRequest());
+        }
+    }
+
+    /**
+     * Remove and return any requests that have been built by this builder since the last call to this method. This method will
+     * return null if no requests have been built. If only one request was built, then it will be returned. If multiple requests
+     * have been built, then this method will return a {@link CompositeRequest} containing them.
+     * 
+     * @return the request (or {@link CompositeRequest}) representing those requests that this builder has created since the last
+     *         call to this method, or null if there are no requests to return
+     */
+    public Request pop() {
+        int number = requests.size();
+        if (pendingRequest != null) {
+            // There's a pending request, we need to build it ...
+            Request newRequest = pendingRequest.toRequest();
+            if (number == 0) {
+                // There's no other request ...
+                return newRequest;
+            }
+            // We have at least one other request, so add the pending request ...
+            add(newRequest);
+            ++number;
+        } else {
+            // There is no pending request ...
+            if (number == 0) {
+                // And no enqueued request ...
+                return null;
+            }
+            if (number == 1) {
+                // There's only one request, so return just the one ...
+                Request result = requests.getFirst();
+                requests.clear();
+                return result;
+            }
+        }
+        assert number >= 2;
+        // Build a composite request (reusing the existing list), and then replace the list
+        Request result = CompositeRequest.with(requests);
+        requests = new LinkedList<Request>();
+        return result;
+    }
+
+    protected final BatchRequestBuilder add( Request request ) {
+        addPending();
+        requests.add(request);
+        return this;
+    }
+
+    protected final BatchRequestBuilder addPending() {
+        if (pendingRequest != null) {
+            requests.add(pendingRequest.toRequest());
+            pendingRequest = null;
+        }
+        return this;
+    }
+
+    /**
+     * Add a request to obtain the information about the available workspaces.
+     * 
+     * @return this builder for method chaining; never null
+     */
+    public BatchRequestBuilder getWorkspaces() {
+        return add(new GetWorkspacesRequest());
+    }
+
+    /**
+     * Add a request to verify the existance of the named workspace.
+     * 
+     * @param workspaceName the desired name of the workspace, or null if the source's default workspace should be used
+     * @return this builder for method chaining; never null
+     */
+    public BatchRequestBuilder verifyWorkspace( String workspaceName ) {
+        return add(new VerifyWorkspaceRequest(workspaceName));
+    }
+
+    /**
+     * Add a request to create a new workspace, and specify the behavior should a workspace already exists with a name that
+     * matches the desired name for the new workspace.
+     * 
+     * @param desiredNameOfNewWorkspace the desired name of the new workspace
+     * @param createConflictBehavior the behavior if a workspace already exists with the same name, or null if the default
+     *        behavior should be used
+     * @return this builder for method chaining; never null
+     */
+    public BatchRequestBuilder createWorkspace( String desiredNameOfNewWorkspace,
+                                                CreateConflictBehavior createConflictBehavior ) {
+        return add(new CreateWorkspaceRequest(desiredNameOfNewWorkspace, createConflictBehavior));
+    }
+
+    /**
+     * Add a request to clone an existing workspace to create a new workspace, and specify the behavior should a workspace already
+     * exists with a name that matches the desired name for the new workspace.
+     * 
+     * @param nameOfWorkspaceToBeCloned the name of the existing workspace that is to be cloned
+     * @param desiredNameOfTargetWorkspace the desired name of the target workspace
+     * @param createConflictBehavior the behavior if a workspace already exists with the same name
+     * @param cloneConflictBehavior the behavior if the workspace to be cloned does not exist
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the either workspace name is null
+     */
+    public BatchRequestBuilder cloneWorkspace( String nameOfWorkspaceToBeCloned,
+                                               String desiredNameOfTargetWorkspace,
+                                               CreateConflictBehavior createConflictBehavior,
+                                               CloneConflictBehavior cloneConflictBehavior ) {
+        return add(new CloneWorkspaceRequest(nameOfWorkspaceToBeCloned, desiredNameOfTargetWorkspace, createConflictBehavior,
+                                             cloneConflictBehavior));
+    }
+
+    /**
+     * Add a request to destroy an existing workspace.
+     * 
+     * @param workspaceName the name of the workspace that is to be destroyed
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the workspace name is null
+     */
+    public BatchRequestBuilder destroyWorkspace( String workspaceName ) {
+        return add(new DestroyWorkspaceRequest(workspaceName));
+    }
+
+    /**
+     * Add a request to verify the existance and location of a node at the supplied location.
+     * 
+     * @param at the location of the node to be verified
+     * @param workspaceName the name of the workspace containing the node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder verifyNodeExists( Location at,
+                                                 String workspaceName ) {
+        return add(new VerifyNodeExistsRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder readNode( Location at,
+                                         String workspaceName ) {
+        return add(new ReadNodeRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the children of a node at the supplied location in the designated workspace.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder readAllChildren( Location of,
+                                                String workspaceName ) {
+        return add(new ReadAllChildrenRequest(of, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder readAllProperties( Location of,
+                                                  String workspaceName ) {
+        return add(new ReadAllPropertiesRequest(of, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @param propertyName the name of the property to read
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder readProperty( Location of,
+                                             String workspaceName,
+                                             Name propertyName ) {
+        return add(new ReadPropertyRequest(of, workspaceName, propertyName));
+    }
+
+    /**
+     * Add a request to read the branch at the supplied location, to a maximum depth of 2.
+     * 
+     * @param at the location of the branch
+     * @param workspaceName the name of the workspace containing the branch
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
+     */
+    public BatchRequestBuilder readBranch( Location at,
+                                           String workspaceName ) {
+        return add(new ReadBranchRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the branch (of given depth) at the supplied location.
+     * 
+     * @param at the location of the branch
+     * @param workspaceName the name of the workspace containing the branch
+     * @param maxDepth the maximum depth to read
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
+     */
+    public BatchRequestBuilder readBranch( Location at,
+                                           String workspaceName,
+                                           int maxDepth ) {
+        return add(new ReadBranchRequest(at, workspaceName, maxDepth));
+    }
+
+    /**
+     * Add a request to read a block of the children of a node at the supplied location. The block is defined by the starting
+     * index of the first child and the number of children to include. Note that this index is <i>not</i> the
+     * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in
+     * an array.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace containing the parent
+     * @param startingIndex the zero-based index of the first child to be included in the block
+     * @param count the maximum number of children that should be included in the block
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null, if <code>startingIndex</code> is negative, or
+     *         if <code>count</count> is less than 1.
+     */
+    public BatchRequestBuilder readBlockOfChildren( Location of,
+                                                    String workspaceName,
+                                                    int startingIndex,
+                                                    int count ) {
+        return add(new ReadBlockOfChildrenRequest(of, workspaceName, startingIndex, count));
+    }
+
+    /**
+     * Add a request to read those children of a node that are immediately after a supplied sibling node.
+     * 
+     * @param startingAfter the location of the previous sibling that was the last child of the previous block of children read
+     * @param workspaceName the name of the workspace containing the node
+     * @param count the maximum number of children that should be included in the block
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the workspace name or <code>startingAfter</code> location is null, or if
+     *         <code>count</count> is less than 1.
+     */
+    public BatchRequestBuilder readNextBlockOfChildren( Location startingAfter,
+                                                        String workspaceName,
+                                                        int count ) {
+        return add(new ReadNextBlockOfChildrenRequest(startingAfter, workspaceName, count));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public BatchRequestBuilder createNode( Location parentLocation,
+                                           String workspaceName,
+                                           Name childName,
+                                           Iterator<Property> properties ) {
+        return add(new CreateNodeRequest(parentLocation, workspaceName, childName, CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR,
+                                         properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
+     *        location
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public BatchRequestBuilder createNode( Location parentLocation,
+                                           String workspaceName,
+                                           Name childName,
+                                           Iterator<Property> properties,
+                                           NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return add(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public BatchRequestBuilder createNode( Location parentLocation,
+                                           String workspaceName,
+                                           Name childName,
+                                           Property[] properties ) {
+        return add(new CreateNodeRequest(parentLocation, workspaceName, childName, CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR,
+                                         properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
+     *        location
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public BatchRequestBuilder createNode( Location parentLocation,
+                                           String workspaceName,
+                                           Name childName,
+                                           Property[] properties,
+                                           NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return add(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
+    }
+
+    /**
+     * Add a request to update the property on the node at the supplied location. This request will create the property if it does
+     * not yet exist.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param property the new property on the node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
+     */
+    public BatchRequestBuilder setProperty( Location on,
+                                            String workspaceName,
+                                            Property property ) {
+        // If there's a pending request ...
+        if (pendingRequest != null) {
+            // Compare the supplied location with that of the pending request
+            if (pendingRequest.location.equals(on)) {
+                // They are the same location, so we can add the properties to the pending request ...
+                pendingRequest.pendingProperties.put(property.getName(), property);
+                return this;
+            }
+            // Not the exact same location, so push the existing pending request ...
+            addPending();
+        }
+
+        // Record this operation as a pending change ...
+        pendingRequest = new NodeChange(on, workspaceName);
+        pendingRequest.pendingProperties.put(property.getName(), property);
+        return this;
+    }
+
+    /**
+     * Add a request to update the properties on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param properties the new properties on the node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
+     */
+    public BatchRequestBuilder setProperties( Location on,
+                                              String workspaceName,
+                                              Property... properties ) {
+        // If there's a pending request ...
+        if (pendingRequest != null) {
+            // Compare the supplied location with that of the pending request
+            if (pendingRequest.location.equals(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);
+                }
+                return this;
+            }
+            // Not the exact same location, so push the existing pending request ...
+            addPending();
+        }
+
+        // Record this operation as a pending change ...
+        pendingRequest = new NodeChange(on, workspaceName);
+        for (Property property : properties) {
+            pendingRequest.pendingProperties.put(property.getName(), property);
+        }
+        return this;
+    }
+
+    /**
+     * Add a request to remove the property with the supplied name from the given node. Supplying a name for a property that does
+     * not exist will not cause an error.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param propertyName the name of the property that is to be removed
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
+     */
+    public BatchRequestBuilder removeProperty( Location on,
+                                               String workspaceName,
+                                               Name propertyName ) {
+        // If there's a pending request ...
+        if (pendingRequest != null) {
+            // Compare the supplied location with that of the pending request
+            if (pendingRequest.location.equals(on)) {
+                // They are the same location, so we can add the properties to the pending request ...
+                pendingRequest.pendingProperties.put(propertyName, null);
+                return this;
+            }
+            // Not the exact same location, so push the existing pending request ...
+            addPending();
+        }
+
+        // Record this operation as a pending change ...
+        pendingRequest = new NodeChange(on, workspaceName);
+        pendingRequest.pendingProperties.put(propertyName, null);
+        return this;
+    }
+
+    /**
+     * Add a request to remove from the node the properties with the supplied names. Supplying a name for a property that does not
+     * exist will not cause an error.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param propertyNames the names of the properties that are to be removed
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
+     */
+    public BatchRequestBuilder removeProperties( Location on,
+                                                 String workspaceName,
+                                                 Name... propertyNames ) {
+        // If there's a pending request ...
+        if (pendingRequest != null) {
+            // Compare the supplied location with that of the pending request
+            if (pendingRequest.location.equals(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);
+                }
+                return this;
+            }
+            // Not the exact same location, so push the existing pending request ...
+            addPending();
+        }
+
+        // Record this operation as a pending change ...
+        pendingRequest = new NodeChange(on, workspaceName);
+        for (Name propertyName : propertyNames) {
+            pendingRequest.pendingProperties.put(propertyName, null);
+        }
+        return this;
+    }
+
+    /**
+     * Add a request to rename the node at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param newName the new name for the node
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder renameNode( Location at,
+                                           String workspaceName,
+                                           Name newName ) {
+        return add(new RenameNodeRequest(at, workspaceName, newName));
+    }
+
+    /**
+     * Add a request to copy a branch to another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be copied
+     * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
+     * @param into the location of the existing node into which the copy should be placed
+     * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
+     * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
+     *        used
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if either of the locations or workspace names are null
+     */
+    public BatchRequestBuilder copyBranch( Location from,
+                                           String fromWorkspace,
+                                           Location into,
+                                           String intoWorkspace,
+                                           Name nameForCopy ) {
+        return add(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace, nameForCopy,
+                                         CopyBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
+    }
+
+    /**
+     * Add a request to copy a branch to another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be copied
+     * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
+     * @param into the location of the existing node into which the copy should be placed
+     * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
+     * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
+     *        used
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location, or null if the default conflict behavior should be used
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if either of the locations or workspace names are null
+     */
+    public BatchRequestBuilder copyBranch( Location from,
+                                           String fromWorkspace,
+                                           Location into,
+                                           String intoWorkspace,
+                                           Name nameForCopy,
+                                           NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CopyBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return add(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace, nameForCopy, conflictBehavior));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public BatchRequestBuilder moveBranch( Location from,
+                                           Location into,
+                                           String workspaceName ) {
+        return add(new MoveBranchRequest(from, into, workspaceName, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public BatchRequestBuilder moveBranch( Location from,
+                                           Location into,
+                                           String workspaceName,
+                                           NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return add(new MoveBranchRequest(from, into, workspaceName, conflictBehavior));
+    }
+
+    /**
+     * Add a request to delete a branch.
+     * 
+     * @param at the location of the top node in the existing branch that is to be deleted
+     * @param workspaceName the name of the workspace containing the parent
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public BatchRequestBuilder deleteBranch( Location at,
+                                             String workspaceName ) {
+        return add(new DeleteBranchRequest(at, workspaceName));
+    }
+
+    protected class NodeChange {
+        protected final Location location;
+        protected final String workspaceName;
+        protected final Map<Name, Property> pendingProperties = new HashMap<Name, Property>();
+
+        protected NodeChange( Location location,
+                              String workspaceName ) {
+            this.location = location;
+            this.workspaceName = workspaceName;
+        }
+
+        protected Request toRequest() {
+            if (pendingProperties.size() == 1) {
+                Map.Entry<Name, Property> entry = pendingProperties.entrySet().iterator().next();
+                Property property = entry.getValue();
+                if (property == null) {
+                    return new RemovePropertyRequest(location, workspaceName, entry.getKey());
+                }
+                return new SetPropertyRequest(location, workspaceName, property);
+            }
+            return new UpdatePropertiesRequest(location, workspaceName, pendingProperties);
+        }
+    }
+
+}


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

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -73,6 +73,7 @@
      * @param desiredNameOfTargetWorkspace the desired name of the target workspace
      * @param createConflictBehavior the behavior if a workspace already exists with the same name
      * @param cloneConflictBehavior the behavior if the workspace to be cloned does not exist
+     * @throws IllegalArgumentException if the either workspace name is null
      */
     public CloneWorkspaceRequest( String nameOfWorkspaceToBeCloned,
                                   String desiredNameOfTargetWorkspace,

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -46,7 +46,7 @@
      * 
      * @param at the location of the top node in the existing branch that is to be deleted
      * @param workspaceName the name of the workspace containing the parent
-     * @throws IllegalArgumentException if the location is null
+     * @throws IllegalArgumentException if the location or workspace name is null
      */
     public DeleteBranchRequest( Location at,
                                 String workspaceName ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DestroyWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DestroyWorkspaceRequest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DestroyWorkspaceRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -38,6 +38,7 @@
      * Create a request to destroy an existing workspace.
      * 
      * @param workspaceName the name of the workspace that is to be destroyed
+     * @throws IllegalArgumentException if the workspace name is null
      */
     public DestroyWorkspaceRequest( String workspaceName ) {
         CheckArg.isNotNull(workspaceName, "workspaceName");

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -1,248 +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.request;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-
-/**
- * Instruction to remove properties from the node at the specified location.
- * 
- * @author Randall Hauch
- */
-public class RemovePropertiesRequest extends Request implements Iterable<Name>, ChangeRequest {
-
-    private static final long serialVersionUID = 1L;
-
-    private final Location from;
-    private final String workspaceName;
-    private final Set<Name> propertyNames;
-    private Location actualLocation;
-
-    /**
-     * Create a request to remove the properties with the given names from the node at the supplied location.
-     * 
-     * @param from the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param propertyNames the names of the properties to be removed from the node
-     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
-     */
-    public RemovePropertiesRequest( Location from,
-                                    String workspaceName,
-                                    Name... propertyNames ) {
-        CheckArg.isNotNull(from, "from");
-        CheckArg.isNotEmpty(propertyNames, "propertyNames");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.from = from;
-        Set<Name> names = new HashSet<Name>();
-        for (Name name : propertyNames) {
-            if (name != null) names.add(name);
-        }
-        this.propertyNames = Collections.unmodifiableSet(names);
-    }
-
-    /**
-     * Create a request to remove the properties with the given names from the node at the supplied location.
-     * 
-     * @param from the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param propertyNames the names of the properties to be removed from the node
-     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
-     */
-    public RemovePropertiesRequest( Location from,
-                                    String workspaceName,
-                                    Iterable<Name> propertyNames ) {
-        CheckArg.isNotNull(from, "from");
-        CheckArg.isNotNull(propertyNames, "propertyNames");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.from = from;
-        Set<Name> names = new HashSet<Name>();
-        for (Name name : propertyNames) {
-            if (name != null) names.add(name);
-        }
-        this.propertyNames = Collections.unmodifiableSet(names);
-        CheckArg.isNotEmpty(this.propertyNames, "propertyNames");
-    }
-
-    /**
-     * Create a request to remove the properties with the given names from the node at the supplied location.
-     * 
-     * @param from the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param propertyNames the names of the properties to be removed from the node
-     * @throws IllegalArgumentException if the location is null or if there are no properties to remove
-     */
-    public RemovePropertiesRequest( Location from,
-                                    String workspaceName,
-                                    Iterator<Name> propertyNames ) {
-        CheckArg.isNotNull(from, "from");
-        CheckArg.isNotNull(propertyNames, "propertyNames");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.from = from;
-        Set<Name> names = new HashSet<Name>();
-        while (propertyNames.hasNext()) {
-            Name name = propertyNames.next();
-            if (name != null) names.add(name);
-        }
-        this.propertyNames = Collections.unmodifiableSet(names);
-        CheckArg.isNotEmpty(this.propertyNames, "propertyNames");
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.request.Request#isReadOnly()
-     */
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    /**
-     * Get the location defining the node from which the properties are to be removed.
-     * 
-     * @return the location of the node; never null
-     */
-    public Location from() {
-        return from;
-    }
-
-    /**
-     * Get the name of the workspace in which the node exists.
-     * 
-     * @return the name of the workspace; never null
-     */
-    public String inWorkspace() {
-        return workspaceName;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Iterable#iterator()
-     */
-    public Iterator<Name> iterator() {
-        return this.propertyNames.iterator();
-    }
-
-    /**
-     * Get the names of the properties that are to be removed from the node.
-     * 
-     * @return the collection of property names; never null and never empty
-     */
-    public Collection<Name> propertyNames() {
-        return propertyNames;
-    }
-
-    /**
-     * Sets the actual and complete location of the node whose properties were removed. This method must be called when processing
-     * the request, and the actual location must have a {@link Location#getPath() path}.
-     * 
-     * @param actual the actual location of the node being changed, or null if the {@link #from() current location} should be used
-     * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
-     *         location} as the {@link #from() current location}, or if the actual location does not have a path.
-     */
-    public void setActualLocationOfNode( Location actual ) {
-        if (!from.isSame(actual)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, from));
-        }
-        assert actual != null;
-        if (!actual.hasPath()) {
-            throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
-        }
-        this.actualLocation = actual;
-    }
-
-    /**
-     * Get the actual location of the node whose properties were removed.
-     * 
-     * @return the actual location, or null if the actual location was not set
-     */
-    public Location getActualLocationOfNode() {
-        return actualLocation;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
-     */
-    public boolean changes( String workspace,
-                            Path path ) {
-        return this.workspaceName.equals(workspace) && from.hasPath() && from.getPath().isAtOrBelow(path);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
-     */
-    public Location changedLocation() {
-        return from;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals( Object obj ) {
-        if (obj == this) return true;
-        if (this.getClass().isInstance(obj)) {
-            RemovePropertiesRequest that = (RemovePropertiesRequest)obj;
-            if (!this.from().equals(that.from())) return false;
-            if (!this.propertyNames().equals(that.propertyNames())) return false;
-            if (!this.inWorkspace().equals(that.inWorkspace())) return false;
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        if (propertyNames.size() == 1) {
-            return "remove from " + from() + " in the \"" + workspaceName + "\" workspace the property named "
-                   + propertyNames.iterator().next();
-        }
-        return "remove from " + from() + " in the \"" + workspaceName + "\" workspace the properties named " + propertyNames();
-    }
-
-}

Added: 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	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -0,0 +1,177 @@
+/*
+ * 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.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Instruction to remove the property with the supplied name from the node at the given location. This request has no net effect
+ * if the node does not contain a property with the supplied name.
+ * 
+ * @author Randall Hauch
+ */
+public class RemovePropertyRequest extends Request implements ChangeRequest {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Location from;
+    private final String workspaceName;
+    private final Name propertyName;
+    private Location actualLocation;
+
+    /**
+     * Create a request to remove a named property from the node at the supplied location.
+     * 
+     * @param from the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param propertyName the name of the property to be removed
+     * @throws IllegalArgumentException if the location, workspace name, or property name is null
+     */
+    public RemovePropertyRequest( Location from,
+                                  String workspaceName,
+                                  Name propertyName ) {
+        CheckArg.isNotNull(from, "from");
+        CheckArg.isNotNull(propertyName, "propertyName");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        this.workspaceName = workspaceName;
+        this.from = from;
+        this.propertyName = propertyName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#isReadOnly()
+     */
+    @Override
+    public boolean isReadOnly() {
+        return false;
+    }
+
+    /**
+     * Get the location defining the node from which the property is to be removed.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location from() {
+        return from;
+    }
+
+    /**
+     * Get the name of the workspace in which the node exists.
+     * 
+     * @return the name of the workspace; never null
+     */
+    public String inWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * Get the name of the property that is being removed.
+     * 
+     * @return the property name; never null
+     */
+    public Name propertyName() {
+        return propertyName;
+    }
+
+    /**
+     * Sets the actual and complete location of the node being updated. This method must be called when processing the request,
+     * 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.
+     */
+    public void setActualLocationOfNode( Location actual ) {
+        if (!from.isSame(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, from));
+        }
+        assert actual != null;
+        if (!actual.hasPath()) {
+            throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
+        }
+        this.actualLocation = actual;
+    }
+
+    /**
+     * Get the actual location of the node that was updated.
+     * 
+     * @return the actual location, or null if the actual location was not set
+     */
+    public Location getActualLocationOfNode() {
+        return actualLocation;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && from.hasPath() && from.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (this.getClass().isInstance(obj)) {
+            RemovePropertyRequest that = (RemovePropertyRequest)obj;
+            if (!this.from().equals(that.from())) return false;
+            if (!this.propertyName().equals(that.propertyName())) return false;
+            if (!this.inWorkspace().equals(that.inWorkspace())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return from;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "remove property " + propertyName() + " from " + from() + " in the \"" + workspaceName + "\" workspace";
+    }
+}


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

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -0,0 +1,494 @@
+/*
+ * 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.
+ *
+ * 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.request;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.NodeConflictBehavior;
+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.request.CloneWorkspaceRequest.CloneConflictBehavior;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
+
+/**
+ * A component that can be used to build requests while allowing different strategies for how requests are handled. Subclasses can
+ * simply override the {@link #process(Request)} method to define what happens with each request.
+ */
+public abstract class RequestBuilder {
+
+    /**
+     * Create a new builder.
+     */
+    protected RequestBuilder() {
+    }
+
+    protected abstract <T extends Request> T process( T request );
+
+    /**
+     * Add a request to obtain the information about the available workspaces.
+     * 
+     * @return the request; never null
+     */
+    public GetWorkspacesRequest getWorkspaces() {
+        GetWorkspacesRequest request = new GetWorkspacesRequest();
+        process(request);
+        return request;
+    }
+
+    /**
+     * Add a request to verify the existance of the named workspace.
+     * 
+     * @param workspaceName the desired name of the workspace, or null if the source's default workspace should be used
+     * @return the request; never null
+     */
+    public VerifyWorkspaceRequest verifyWorkspace( String workspaceName ) {
+        return process(new VerifyWorkspaceRequest(workspaceName));
+    }
+
+    /**
+     * Add a request to create a new workspace, and specify the behavior should a workspace already exists with a name that
+     * matches the desired name for the new workspace.
+     * 
+     * @param desiredNameOfNewWorkspace the desired name of the new workspace
+     * @param createConflictBehavior the behavior if a workspace already exists with the same name, or null if the default
+     *        behavior should be used
+     * @return the request; never null
+     */
+    public CreateWorkspaceRequest createWorkspace( String desiredNameOfNewWorkspace,
+                                                   CreateConflictBehavior createConflictBehavior ) {
+        return process(new CreateWorkspaceRequest(desiredNameOfNewWorkspace, createConflictBehavior));
+    }
+
+    /**
+     * Add a request to clone an existing workspace to create a new workspace, and specify the behavior should a workspace already
+     * exists with a name that matches the desired name for the new workspace.
+     * 
+     * @param nameOfWorkspaceToBeCloned the name of the existing workspace that is to be cloned
+     * @param desiredNameOfTargetWorkspace the desired name of the target workspace
+     * @param createConflictBehavior the behavior if a workspace already exists with the same name
+     * @param cloneConflictBehavior the behavior if the workspace to be cloned does not exist
+     * @return the request; never null
+     * @throws IllegalArgumentException if the either workspace name is null
+     */
+    public CloneWorkspaceRequest cloneWorkspace( String nameOfWorkspaceToBeCloned,
+                                                 String desiredNameOfTargetWorkspace,
+                                                 CreateConflictBehavior createConflictBehavior,
+                                                 CloneConflictBehavior cloneConflictBehavior ) {
+        return process(new CloneWorkspaceRequest(nameOfWorkspaceToBeCloned, desiredNameOfTargetWorkspace, createConflictBehavior,
+                                                 cloneConflictBehavior));
+    }
+
+    /**
+     * Add a request to destroy an existing workspace.
+     * 
+     * @param workspaceName the name of the workspace that is to be destroyed
+     * @return the request; never null
+     * @throws IllegalArgumentException if the workspace name is null
+     */
+    public DestroyWorkspaceRequest destroyWorkspace( String workspaceName ) {
+        return process(new DestroyWorkspaceRequest(workspaceName));
+    }
+
+    /**
+     * Add a request to verify the existance and location of a node at the supplied location.
+     * 
+     * @param at the location of the node to be verified
+     * @param workspaceName the name of the workspace containing the node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public VerifyNodeExistsRequest verifyNodeExists( Location at,
+                                                     String workspaceName ) {
+        return process(new VerifyNodeExistsRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public ReadNodeRequest readNode( Location at,
+                                     String workspaceName ) {
+        return process(new ReadNodeRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the children of a node at the supplied location in the designated workspace.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public ReadAllChildrenRequest readAllChildren( Location of,
+                                                   String workspaceName ) {
+        return process(new ReadAllChildrenRequest(of, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public ReadAllPropertiesRequest readAllProperties( Location of,
+                                                       String workspaceName ) {
+        return process(new ReadAllPropertiesRequest(of, workspaceName));
+    }
+
+    /**
+     * Add a request to read the properties and number of children of a node at the supplied location.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace
+     * @param propertyName the name of the property to read
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public ReadPropertyRequest readProperty( Location of,
+                                             String workspaceName,
+                                             Name propertyName ) {
+        return process(new ReadPropertyRequest(of, workspaceName, propertyName));
+    }
+
+    /**
+     * Add a request to read the branch at the supplied location, to a maximum depth of 2.
+     * 
+     * @param at the location of the branch
+     * @param workspaceName the name of the workspace containing the branch
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
+     */
+    public ReadBranchRequest readBranch( Location at,
+                                         String workspaceName ) {
+        return process(new ReadBranchRequest(at, workspaceName));
+    }
+
+    /**
+     * Add a request to read the branch (of given depth) at the supplied location.
+     * 
+     * @param at the location of the branch
+     * @param workspaceName the name of the workspace containing the branch
+     * @param maxDepth the maximum depth to read
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if the maximum depth is not positive
+     */
+    public ReadBranchRequest readBranch( Location at,
+                                         String workspaceName,
+                                         int maxDepth ) {
+        return process(new ReadBranchRequest(at, workspaceName, maxDepth));
+    }
+
+    /**
+     * Add a request to read a block of the children of a node at the supplied location. The block is defined by the starting
+     * index of the first child and the number of children to include. Note that this index is <i>not</i> the
+     * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in
+     * an array.
+     * 
+     * @param of the location of the node whose children are to be read
+     * @param workspaceName the name of the workspace containing the parent
+     * @param startingIndex the zero-based index of the first child to be included in the block
+     * @param count the maximum number of children that should be included in the block
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null, if <code>startingIndex</code> is negative, or
+     *         if <code>count</count> is less than 1.
+     */
+    public ReadBlockOfChildrenRequest readBlockOfChildren( Location of,
+                                                           String workspaceName,
+                                                           int startingIndex,
+                                                           int count ) {
+        return process(new ReadBlockOfChildrenRequest(of, workspaceName, startingIndex, count));
+    }
+
+    /**
+     * Add a request to read those children of a node that are immediately after a supplied sibling node.
+     * 
+     * @param startingAfter the location of the previous sibling that was the last child of the previous block of children read
+     * @param workspaceName the name of the workspace containing the node
+     * @param count the maximum number of children that should be included in the block
+     * @return the request; never null
+     * @throws IllegalArgumentException if the workspace name or <code>startingAfter</code> location is null, or if
+     *         <code>count</count> is less than 1.
+     */
+    public ReadNextBlockOfChildrenRequest readNextBlockOfChildren( Location startingAfter,
+                                                                   String workspaceName,
+                                                                   int count ) {
+        return process(new ReadNextBlockOfChildrenRequest(startingAfter, workspaceName, count));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public CreateNodeRequest createNode( Location parentLocation,
+                                         String workspaceName,
+                                         Name childName,
+                                         Iterator<Property> properties ) {
+        return process(new CreateNodeRequest(parentLocation, workspaceName, childName,
+                                             CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR, properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
+     *        location
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public CreateNodeRequest createNode( Location parentLocation,
+                                         String workspaceName,
+                                         Name childName,
+                                         Iterator<Property> properties,
+                                         NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return process(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public CreateNodeRequest createNode( Location parentLocation,
+                                         String workspaceName,
+                                         Name childName,
+                                         Property[] properties ) {
+        return process(new CreateNodeRequest(parentLocation, workspaceName, childName,
+                                             CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR, properties));
+    }
+
+    /**
+     * Add a request to create a node with the given properties under the supplied location.
+     * 
+     * @param parentLocation the location of the existing parent node, under which the new child should be created
+     * @param workspaceName the name of the workspace containing the parent
+     * @param childName the name of the new child to create under the existing parent
+     * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+     *        properties} for the new node
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
+     *        location
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location, workspace name, or child name is null
+     */
+    public CreateNodeRequest createNode( Location parentLocation,
+                                         String workspaceName,
+                                         Name childName,
+                                         Property[] properties,
+                                         NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CreateNodeRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return process(new CreateNodeRequest(parentLocation, workspaceName, childName, conflictBehavior, properties));
+    }
+
+    /**
+     * Add a request to update the property on the node at the supplied location. This request will create the property if it does
+     * not yet exist.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param property the new property on the node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
+     */
+    public SetPropertyRequest setProperty( Location on,
+                                           String workspaceName,
+                                           Property property ) {
+        return process(new SetPropertyRequest(on, workspaceName, property));
+    }
+
+    /**
+     * Add a request to update the properties on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param properties the new properties on the node
+     * @return the {@link SetPropertyRequest} or {@link UpdatePropertiesRequest} request, depending upon the number of properties
+     *         being set; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
+     */
+    public Request setProperties( Location on,
+                                  String workspaceName,
+                                  Property... properties ) {
+        if (properties.length == 1) {
+            return process(new SetPropertyRequest(on, workspaceName, properties[0]));
+        }
+        Map<Name, Property> propertyMap = new HashMap<Name, Property>();
+        for (Property property : properties) {
+            propertyMap.put(property.getName(), property);
+        }
+        return process(new UpdatePropertiesRequest(on, workspaceName, propertyMap));
+    }
+
+    /**
+     * Add a request to remove the property with the supplied name from the given node. Supplying a name for a property that does
+     * not exist will not cause an error.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param propertyName the name of the property that is to be removed
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
+     */
+    public RemovePropertyRequest removeProperty( Location on,
+                                                 String workspaceName,
+                                                 Name propertyName ) {
+        return process(new RemovePropertyRequest(on, workspaceName, propertyName));
+    }
+
+    /**
+     * Add a request to remove from the node the properties with the supplied names. Supplying a name for a property that does not
+     * exist will not cause an error.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param propertyNames the names of the properties that are to be removed
+     * @return the {@link RemovePropertyRequest} or {@link UpdatePropertiesRequest} request, depending upon the number of
+     *         properties being removed; never null
+     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to remove
+     */
+    public Request removeProperties( Location on,
+                                     String workspaceName,
+                                     Name... propertyNames ) {
+        if (propertyNames.length == 1) {
+            return process(new RemovePropertyRequest(on, workspaceName, propertyNames[0]));
+        }
+        Map<Name, Property> properties = new HashMap<Name, Property>();
+        for (Name propertyName : propertyNames) {
+            properties.put(propertyName, null);
+        }
+        return process(new UpdatePropertiesRequest(on, workspaceName, properties));
+    }
+
+    /**
+     * Add a request to rename the node at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param newName the new name for the node
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public RenameNodeRequest renameNode( Location at,
+                                         String workspaceName,
+                                         Name newName ) {
+        return process(new RenameNodeRequest(at, workspaceName, newName));
+    }
+
+    /**
+     * Add a request to copy a branch to another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be copied
+     * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
+     * @param into the location of the existing node into which the copy should be placed
+     * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
+     * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
+     *        used
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location, or null if the default conflict behavior should be used
+     * @return the request; never null
+     * @throws IllegalArgumentException if either of the locations or workspace names are null
+     */
+    public CopyBranchRequest copyBranch( Location from,
+                                         String fromWorkspace,
+                                         Location into,
+                                         String intoWorkspace,
+                                         Name nameForCopy,
+                                         NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = CopyBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return process(new CopyBranchRequest(from, fromWorkspace, into, intoWorkspace, nameForCopy, conflictBehavior));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest moveBranch( Location from,
+                                         Location into,
+                                         String workspaceName ) {
+        return process(new MoveBranchRequest(from, into, workspaceName, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest moveBranch( Location from,
+                                         Location into,
+                                         String workspaceName,
+                                         NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return process(new MoveBranchRequest(from, into, workspaceName, conflictBehavior));
+    }
+
+    /**
+     * Add a request to delete a branch.
+     * 
+     * @param at the location of the top node in the existing branch that is to be deleted
+     * @param workspaceName the name of the workspace containing the parent
+     * @return the request; never null
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public DeleteBranchRequest deleteBranch( Location at,
+                                             String workspaceName ) {
+        return process(new DeleteBranchRequest(at, workspaceName));
+    }
+}


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

Added: 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	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -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.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * Instruction to set a particular property on the node at the specified location. This request <i>never</i> removes the node,
+ * even if the property is empty.
+ * 
+ * @author Randall Hauch
+ */
+public class SetPropertyRequest extends Request implements ChangeRequest {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Location on;
+    private final String workspaceName;
+    private final Property property;
+    private Location actualLocation;
+
+    /**
+     * Create a request to set the property on the node at the supplied location.
+     * 
+     * @param on the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param property the new property on the node
+     * @throws IllegalArgumentException if the location, workspace name, or property is null
+     */
+    public SetPropertyRequest( Location on,
+                               String workspaceName,
+                               Property property ) {
+        CheckArg.isNotNull(on, "on");
+        CheckArg.isNotNull(property, "property");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        this.workspaceName = workspaceName;
+        this.on = on;
+        this.property = property;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#isReadOnly()
+     */
+    @Override
+    public boolean isReadOnly() {
+        return false;
+    }
+
+    /**
+     * Get the location defining the node that is to be updated.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location on() {
+        return on;
+    }
+
+    /**
+     * Get the name of the workspace in which the node exists.
+     * 
+     * @return the name of the workspace; never null
+     */
+    public String inWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * Get the property that is being set.
+     * 
+     * @return the new property; never null
+     */
+    public Property property() {
+        return property;
+    }
+
+    /**
+     * Sets the actual and complete location of the node being updated. This method must be called when processing the request,
+     * 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.
+     */
+    public void setActualLocationOfNode( Location actual ) {
+        if (!on.isSame(actual)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
+        }
+        assert actual != null;
+        if (!actual.hasPath()) {
+            throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
+        }
+        this.actualLocation = actual;
+    }
+
+    /**
+     * Get the actual location of the node that was updated.
+     * 
+     * @return the actual location, or null if the actual location was not set
+     */
+    public Location getActualLocationOfNode() {
+        return actualLocation;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && on.hasPath() && on.getPath().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (this.getClass().isInstance(obj)) {
+            SetPropertyRequest that = (SetPropertyRequest)obj;
+            if (!this.on().equals(that.on())) return false;
+            if (!this.property().equals(that.property())) return false;
+            if (!this.inWorkspace().equals(that.inWorkspace())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    public Location changedLocation() {
+        return on;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "set property " + property().getName() + " on " + on() + " in the \"" + workspaceName + "\" workspace to "
+               + property().getValuesAsArray();
+    }
+}


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

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -23,14 +23,8 @@
  */
 package org.jboss.dna.graph.request;
 
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
@@ -39,17 +33,32 @@
 import org.jboss.dna.graph.property.Property;
 
 /**
- * Instruction to update the properties on the node at the specified location. Any property with no values will be removed.
+ * Instruction to update the properties on the node at the specified location.
+ * <p>
+ * This request is capable of specifying that certain properties are to have new values and that other properties are to be
+ * removed. The request has a single map of properties keyed by their name. If a property is to be set with new values, the map
+ * will contain an entry with the property keyed by its name. However, if a property is to be removed, the entry will contain the
+ * property name for the key but will have a null entry value.
+ * </p>
+ * <p>
+ * The use of the map also ensures that a single property appears only once in the request (it either has new values or it is to
+ * be removed).
+ * </p>
+ * <p>
+ * Note that the number of values in a property (e.g., {@link Property#size()}, {@link Property#isEmpty()},
+ * {@link Property#isSingle()}, and {@link Property#isMultiple()}) has no influence on whether the property should be removed. It
+ * is possible for a property to have no values.
+ * </p>
  * 
  * @author Randall Hauch
  */
-public class UpdatePropertiesRequest extends Request implements Iterable<Property>, ChangeRequest {
+public class UpdatePropertiesRequest extends Request implements ChangeRequest {
 
     private static final long serialVersionUID = 1L;
 
     private final Location on;
     private final String workspaceName;
-    private final List<Property> properties;
+    private final Map<Name, Property> properties;
     private Location actualLocation;
 
     /**
@@ -57,90 +66,21 @@
      * 
      * @param on the location of the node to be read
      * @param workspaceName the name of the workspace containing the node
-     * @param properties the new properties on the node
+     * @param properties the map of properties (keyed by their name), which is reused without copying
      * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
      */
     public UpdatePropertiesRequest( Location on,
                                     String workspaceName,
-                                    Property... properties ) {
+                                    Map<Name, Property> properties ) {
         CheckArg.isNotNull(on, "on");
         CheckArg.isNotEmpty(properties, "properties");
         CheckArg.isNotNull(workspaceName, "workspaceName");
         this.workspaceName = workspaceName;
         this.on = on;
-        this.properties = Collections.unmodifiableList(Arrays.asList(properties));
+        this.properties = Collections.unmodifiableMap(properties);
     }
 
     /**
-     * Create a request to update the properties on the node at the supplied location.
-     * 
-     * @param on the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param properties the new properties on the node
-     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
-     */
-    public UpdatePropertiesRequest( Location on,
-                                    String workspaceName,
-                                    Iterable<Property> properties ) {
-        CheckArg.isNotNull(on, "on");
-        CheckArg.isNotNull(properties, "properties");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.on = on;
-        List<Property> props = new LinkedList<Property>();
-        for (Property property : properties) {
-            if (property != null) props.add(property);
-        }
-        this.properties = Collections.unmodifiableList(props);
-        CheckArg.isNotEmpty(this.properties, "properties");
-    }
-
-    /**
-     * Create a request to update the properties on the node at the supplied location.
-     * 
-     * @param on the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param properties the new properties on the node
-     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
-     */
-    public UpdatePropertiesRequest( Location on,
-                                    String workspaceName,
-                                    Iterator<Property> properties ) {
-        CheckArg.isNotNull(on, "on");
-        CheckArg.isNotNull(properties, "properties");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.on = on;
-        List<Property> props = new LinkedList<Property>();
-        while (properties.hasNext()) {
-            Property property = properties.next();
-            if (property != null) props.add(property);
-        }
-        this.properties = Collections.unmodifiableList(props);
-        CheckArg.isNotEmpty(this.properties, "properties");
-    }
-
-    /**
-     * Create a request to update the properties on the node at the supplied location.
-     * 
-     * @param on the location of the node to be read
-     * @param workspaceName the name of the workspace containing the node
-     * @param properties the new properties on the node
-     * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update
-     */
-    private UpdatePropertiesRequest( Location on,
-                                     String workspaceName,
-                                     List<Property> properties ) {
-        CheckArg.isNotNull(on, "on");
-        CheckArg.isNotNull(properties, "properties");
-        CheckArg.isNotNull(workspaceName, "workspaceName");
-        this.workspaceName = workspaceName;
-        this.on = on;
-        this.properties = properties;
-        CheckArg.isNotEmpty(this.properties, "properties");
-    }
-
-    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.request.Request#isReadOnly()
@@ -169,20 +109,12 @@
     }
 
     /**
-     * {@inheritDoc}
+     * Get the map of properties for the node, keyed by property name. Any property to be removed will have a map entry with a
+     * null value.
      * 
-     * @see java.lang.Iterable#iterator()
+     * @return the properties being updated; never null and never empty
      */
-    public Iterator<Property> iterator() {
-        return this.properties.iterator();
-    }
-
-    /**
-     * Get the properties for the node.
-     * 
-     * @return the collection of properties; never null and never empty
-     */
-    public Collection<Property> properties() {
+    public Map<Name, Property> properties() {
         return properties;
     }
 
@@ -261,39 +193,4 @@
         return "update properties on " + on() + " in the \"" + workspaceName + "\" workspace to " + properties();
     }
 
-    /**
-     * Merge these updates with those in the supplied request, with the supplied changes overwriting any similar changes on this
-     * node.
-     * 
-     * @param other the other updates that are to be merged with these
-     * @return the merged request
-     */
-    public UpdatePropertiesRequest mergeWith( UpdatePropertiesRequest other ) {
-        if (other == null) return this;
-        if (other.properties().size() == 1) {
-            Property newProp = other.properties.get(0);
-            List<Property> newProps = new LinkedList<Property>();
-            for (Property prop : this.properties) {
-                if (!prop.getName().equals(newProp.getName())) {
-                    newProps.add(prop);
-                }
-            }
-            newProps.add(newProp);
-            return new UpdatePropertiesRequest(on, workspaceName, Collections.unmodifiableList(newProps));
-        }
-        Set<Name> otherNames = new HashSet<Name>();
-        for (Property prop : other.properties()) {
-            otherNames.add(prop.getName());
-        }
-        List<Property> newProps = new LinkedList<Property>();
-        for (Property prop : this.properties) {
-            if (!otherNames.contains(prop.getName())) {
-                newProps.add(prop);
-            }
-        }
-        newProps.addAll(other.properties);
-        return new UpdatePropertiesRequest(on, workspaceName, Collections.unmodifiableList(newProps));
-
-    }
-
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/package-info.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/package-info.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -148,8 +148,10 @@
  *   <li>{@link UpdatePropertiesRequest} - A request to update one or more properties on a node.  Any property
  *   with no values will be removed, while properties with one or more values will be set (replace any existing property
  *   with the same name, if they exist). </li>
- *   <li>{@link RemovePropertiesRequest} - A request to remove one or more properties on a node.  No error is reported
+ *   <li>{@link RemovePropertyRequest} - A request to remove one property from a node.  No error is reported
  *   if the node does not contain a property that is to be removed.</li>
+ *   <li>{@link SetPropertyRequest} - A request to set one property on a node.  No error is reported
+ *   if the node does not already have the property, since the property is just created.</li>
  *   <li>{@link DeleteBranchRequest} - A request to delete a node and all nodes located below it.</li>
  *   <li>{@link CompositeRequest} - A request that acts as a container for multiple other requests (of various kinds),
  *   allowing you to batch together multiple for processing.  Use the one of the {@link CompositeRequest#with(Request...) CompositeRequest.with(...)}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -42,10 +42,12 @@
 import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.ReadPropertyRequest;
-import org.jboss.dna.graph.request.RemovePropertiesRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 
 /**
@@ -185,6 +187,18 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+     */
+    @Override
+    public void process( VerifyNodeExistsRequest request ) {
+        logger.log(level, GraphI18n.executingRequest, request);
+        delegate.process(request);
+        logger.log(level, GraphI18n.executedRequest, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
      */
     @Override
@@ -293,10 +307,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertiesRequest)
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
      */
     @Override
-    public void process( RemovePropertiesRequest request ) {
+    public void process( SetPropertyRequest request ) {
         logger.log(level, GraphI18n.executingRequest, request);
         delegate.process(request);
         logger.log(level, GraphI18n.executedRequest, request);
@@ -305,6 +319,18 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertyRequest)
+     */
+    @Override
+    public void process( RemovePropertyRequest request ) {
+        logger.log(level, GraphI18n.executingRequest, request);
+        delegate.process(request);
+        logger.log(level, GraphI18n.executedRequest, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
      */
     @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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -23,10 +23,10 @@
  */
 package org.jboss.dna.graph.request.processor;
 
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Queue;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
@@ -40,7 +40,6 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.ReferentialIntegrityException;
-import org.jboss.dna.graph.property.basic.BasicEmptyProperty;
 import org.jboss.dna.graph.request.CacheableRequest;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
 import org.jboss.dna.graph.request.CompositeRequest;
@@ -59,9 +58,10 @@
 import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.ReadPropertyRequest;
-import org.jboss.dna.graph.request.RemovePropertiesRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
 import org.jboss.dna.graph.request.UnsupportedRequestException;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
@@ -190,8 +190,10 @@
             process((ReadAllPropertiesRequest)request);
         } else if (request instanceof ReadPropertyRequest) {
             process((ReadPropertyRequest)request);
-        } else if (request instanceof RemovePropertiesRequest) {
-            process((RemovePropertiesRequest)request);
+        } else if (request instanceof RemovePropertyRequest) {
+            process((RemovePropertyRequest)request);
+        } else if (request instanceof SetPropertyRequest) {
+            process((SetPropertyRequest)request);
         } else if (request instanceof RenameNodeRequest) {
             process((RenameNodeRequest)request);
         } else if (request instanceof UpdatePropertiesRequest) {
@@ -608,23 +610,18 @@
     }
 
     /**
-     * Process a request to remove the specified properties from a node.
+     * Process a request to remove the specified property from a node.
      * <p>
      * This method does nothing if the request is null. Unless overridden, this method converts this request into a
      * {@link UpdatePropertiesRequest}.
      * </p>
      * 
-     * @param request the request to remove the properties with certain names
+     * @param request the request to remove the property
      */
-    public void process( RemovePropertiesRequest request ) {
+    public void process( RemovePropertyRequest request ) {
         if (request == null) return;
-        Collection<Name> names = request.propertyNames();
-        if (names.isEmpty()) return;
-        List<Property> emptyProperties = new ArrayList<Property>(names.size());
-        for (Name propertyName : names) {
-            emptyProperties.add(new BasicEmptyProperty(propertyName));
-        }
-        UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.from(), request.inWorkspace(), emptyProperties);
+        Map<Name, Property> properties = Collections.singletonMap(request.propertyName(), null);
+        UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.from(), request.inWorkspace(), properties);
         process(update);
         if (update.hasError()) {
             request.setError(update.getError());
@@ -634,6 +631,29 @@
     }
 
     /**
+     * Process a request to set the specified property on a node.
+     * <p>
+     * This method does nothing if the request is null. Unless overridden, this method converts this request into a
+     * {@link UpdatePropertiesRequest}.
+     * </p>
+     * 
+     * @param request the request to set the property
+     */
+    public void process( SetPropertyRequest request ) {
+        if (request == null) return;
+        Property property = request.property();
+        Map<Name, Property> properties = Collections.singletonMap(property.getName(), property);
+        UpdatePropertiesRequest update = new UpdatePropertiesRequest(request.on(), request.inWorkspace(), properties);
+        process(update);
+        if (update.hasError()) {
+            request.setError(update.getError());
+        } else {
+            // Set the actual location ...
+            request.setActualLocationOfNode(update.getActualLocationOfNode());
+        }
+    }
+
+    /**
      * Process a request to remove the specified properties from a node.
      * <p>
      * This method does nothing if the request is null.

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -70,6 +70,7 @@
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.ReadPropertyRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
@@ -319,9 +320,18 @@
         assertThat(request, is(instanceOf(UpdatePropertiesRequest.class)));
         UpdatePropertiesRequest read = (UpdatePropertiesRequest)request;
         assertThat(read.on(), is(on));
-        assertThat(read.properties(), hasItems(properties));
+        assertThat(read.properties().values(), hasItems(properties));
     }
 
+    protected void assertNextRequestSetProperty( Location on,
+                                                 Property property ) {
+        Request request = executedRequests.poll();
+        assertThat(request, is(instanceOf(SetPropertyRequest.class)));
+        SetPropertyRequest read = (SetPropertyRequest)request;
+        assertThat(read.on(), is(on));
+        assertThat(read.property(), is(property));
+    }
+
     // ----------------------------------------------------------------------------------------------------------------
     // Immediate requests
     // ----------------------------------------------------------------------------------------------------------------
@@ -623,53 +633,80 @@
     }
 
     @Test
-    public void shouldSetPropertiesWithEitherOnOrToMethodsCalledFirst() {
+    public void shouldSetPropertyWithEitherOnOrToMethodsCalledFirst() {
         graph.set("propName").on(validPath).to(3.0f);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", 3.0f));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", 3.0f));
 
         graph.set("propName").to(3.0f).on(validPath);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", 3.0f));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", 3.0f));
     }
 
     @Test
     public void shouldSetPropertyValueToPrimitiveTypes() {
         graph.set("propName").on(validPath).to(3.0F);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", new Float(3.0f)));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", new Float(3.0f)));
 
         graph.set("propName").on(validPath).to(1.0D);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", new Double(1.0)));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", new Double(1.0)));
 
         graph.set("propName").on(validPath).to(false);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", Boolean.FALSE));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", Boolean.FALSE));
 
         graph.set("propName").on(validPath).to(3);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", new Integer(3)));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", new Integer(3)));
 
         graph.set("propName").on(validPath).to(5L);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", new Long(5)));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", new Long(5)));
 
         graph.set("propName").on(validPath).to(validPath);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", validPath));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", validPath));
 
         graph.set("propName").on(validPath).to(validPath.getLastSegment().getName());
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", validPath.getLastSegment()
-                                                                                                          .getName()));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", validPath.getLastSegment().getName()));
         Date now = new Date();
         graph.set("propName").on(validPath).to(now);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", now));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", now));
 
         DateTime dtNow = context.getValueFactories().getDateFactory().create(now);
         graph.set("propName").on(validPath).to(dtNow);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", dtNow));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", dtNow));
 
         Calendar calNow = Calendar.getInstance();
         calNow.setTime(now);
         graph.set("propName").on(validPath).to(calNow);
-        assertNextRequestUpdateProperties(Location.create(validPath), createProperty("propName", dtNow));
+        assertNextRequestSetProperty(Location.create(validPath), createProperty("propName", dtNow));
+    }
 
+    @Test
+    public void shouldSetMultiplePropertiesAtOnce() {
+        Property p1 = createProperty("propName1", new Float(3.0f));
+        Property p2 = createProperty("propName2", new Double(1.0));
+        Property p3 = createProperty("propName3", "String value");
+        graph.batch().set(p1, p2, p3).on(validPath).execute();
+        assertNextRequestUpdateProperties(Location.create(validPath), p1, p2, p3);
     }
 
     @Test
+    public void shouldCombineAdjacentSetPropertyCalls() {
+        Property p1 = createProperty("propName1", new Float(3.0f));
+        Property p2 = createProperty("propName2", new Double(1.0));
+        Property p3 = createProperty("propName3", "String value");
+        graph.batch().set(p1).on(validPath).and().set(p2).on(validPath).and().set(p3).on(validPath).execute();
+        assertNextRequestUpdateProperties(Location.create(validPath), p1, p2, p3);
+    }
+
+    @Test
+    public void shouldNotCombineNonAdjacentSetPropertyCalls() {
+        Property p1 = createProperty("propName1", new Float(3.0f));
+        Property p2 = createProperty("propName2", new Double(1.0));
+        Property p3 = createProperty("propName3", "String value");
+        graph.batch().set(p1).on(validPath).and().set(p2).on(validPath).and().set(p3).on(validUuid).execute();
+        extractRequestsFromComposite();
+        assertNextRequestUpdateProperties(Location.create(validPath), p1, p2);
+        assertNextRequestSetProperty(Location.create(validUuid), p3);
+    }
+
+    @Test
     public void shouldReadNode() {
         Location child1 = Location.create(createPath(validPath, "x"));
         Location child2 = Location.create(createPath(validPath, "y"));

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -494,7 +494,7 @@
         if (stopwatch != null) {
             stopwatch.stop();
             if (output != null) {
-                output.println("    " + getTotalAndAverageDuration(stopwatch));
+                output.println("    " + getTotalAndAverageDuration(stopwatch, totalNumberCreated));
             }
 
             // Perform second batch ...
@@ -504,20 +504,21 @@
             sw.start();
             batch.execute();
             sw.stop();
-            System.out.println("     final " + getTotalAndAverageDuration(stopwatch));
+            System.out.println("     final " + getTotalAndAverageDuration(sw, totalNumberCreated));
             assertThat(totalNumberCreated, is(totalNumber + calculateTotalNumberOfNodesInTree(2, 2, false)));
         }
         return (int)totalNumberCreated;
 
     }
 
-    protected String getTotalAndAverageDuration( Stopwatch stopwatch ) {
-        long totalDurationInMicroseconds = TimeUnit.NANOSECONDS.toMicros(stopwatch.getTotalDuration().longValue());
-        long totalNumber = stopwatch.getCount();
-        long avgDuration = totalDurationInMicroseconds / totalNumber / 1000L;
+    protected String getTotalAndAverageDuration( Stopwatch stopwatch,
+                                                 long numNodes ) {
+        long totalDurationInMilliseconds = TimeUnit.NANOSECONDS.toMillis(stopwatch.getTotalDuration().longValue());
+        long avgDuration = totalDurationInMilliseconds / numNodes;
         String units = " millisecond(s)";
-        if (avgDuration == 0L) {
-            avgDuration = totalDurationInMicroseconds / totalNumber;
+        if (avgDuration < 1L) {
+            long totalDurationInMicroseconds = TimeUnit.NANOSECONDS.toMicros(stopwatch.getTotalDuration().longValue());
+            avgDuration = totalDurationInMicroseconds / numNodes;
             units = " microsecond(s)";
         }
         return "total = " + stopwatch.getTotalDuration() + "; avg = " + avgDuration + units;

Deleted: trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/RemovePropertiesRequestTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/RemovePropertiesRequestTest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/RemovePropertiesRequestTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -1,190 +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.request;
-
-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.junit.matchers.JUnitMatchers.hasItems;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import org.jboss.dna.graph.property.Name;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * @author Randall Hauch
- */
-public class RemovePropertiesRequestTest extends AbstractRequestTest {
-
-    private RemovePropertiesRequest request;
-    private Name validPropertyName1;
-    private Name validPropertyName2;
-    private Name validPropertyName3;
-
-    @Override
-    @Before
-    public void beforeEach() {
-        super.beforeEach();
-        validPropertyName1 = createName("foo1");
-        validPropertyName2 = createName("foo2");
-        validPropertyName3 = createName("foo3");
-    }
-
-    @Override
-    protected Request createRequest() {
-        return new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullFromLocation() {
-        new RemovePropertiesRequest(null, workspace1, validPropertyName1);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullFromWorkspaceName() {
-        new RemovePropertiesRequest(validPathLocation1, null, validPropertyName1);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullPropertyName() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, (Name[])null);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameArray() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, new Name[] {});
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullPropertyNameIterator() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, (Iterator<Name>)null);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameIterator() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, new ArrayList<Name>().iterator());
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullPropertyNameIterable() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, (Iterable<Name>)null);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameIterable() {
-        new RemovePropertiesRequest(validPathLocation, workspace1, new ArrayList<Name>());
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndValidPropertyName() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1);
-        assertThat(request.from(), is(sameInstance(validPathLocation1)));
-        assertThat(request.inWorkspace(), is(sameInstance(workspace1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.propertyNames(), hasItems(validPropertyName1));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndValidPropertyNames() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1, validPropertyName2);
-        assertThat(request.from(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.propertyNames(), hasItems(validPropertyName1, validPropertyName2));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIteratorOverValidPropertyName() {
-        List<Name> names = new ArrayList<Name>();
-        names.add(validPropertyName1);
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, names);
-        assertThat(request.from(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.propertyNames(), hasItems(validPropertyName1));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIteratorOverValidPropertyNames() {
-        List<Name> names = new ArrayList<Name>();
-        names.add(validPropertyName1);
-        names.add(validPropertyName2);
-        names.add(validPropertyName3);
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, names.iterator());
-        assertThat(request.from(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.propertyNames(), hasItems(validPropertyName1, validPropertyName2, validPropertyName3));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIterableWithValidPropertyNames() {
-        List<Name> names = new ArrayList<Name>();
-        names.add(validPropertyName1);
-        names.add(validPropertyName2);
-        names.add(validPropertyName3);
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, names);
-        assertThat(request.from(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.propertyNames(), hasItems(validPropertyName1, validPropertyName2, validPropertyName3));
-    }
-
-    @Test
-    public void shouldConsiderEqualTwoRequestsWithSameLocationsAndSamePropertyNames() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1, validPropertyName2);
-        RemovePropertiesRequest request2 = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1,
-                                                                       validPropertyName2);
-        assertThat(request, is(request2));
-    }
-
-    @Test
-    public void shouldConsiderNotEqualTwoRequestsWithDifferentLocations() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1, validPropertyName2);
-        RemovePropertiesRequest request2 = new RemovePropertiesRequest(validPathLocation2, workspace1, validPropertyName1,
-                                                                       validPropertyName2);
-        assertThat(request.equals(request2), is(false));
-    }
-
-    @Test
-    public void shouldConsiderNotEqualTwoRequestsWithDifferentWorkspaceNames() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1, validPropertyName2);
-        RemovePropertiesRequest request2 = new RemovePropertiesRequest(validPathLocation1, workspace2, validPropertyName1,
-                                                                       validPropertyName2);
-        assertThat(request.equals(request2), is(false));
-    }
-
-    @Test
-    public void shouldConsiderNotEqualTwoRequestsWithSameLocationButDifferentPropertyNames() {
-        request = new RemovePropertiesRequest(validPathLocation1, workspace1, validPropertyName1, validPropertyName2);
-        RemovePropertiesRequest request2 = new RemovePropertiesRequest(validPathLocation2, workspace1, validPropertyName2,
-                                                                       validPropertyName3);
-        assertThat(request.equals(request2), is(false));
-    }
-
-}

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/UpdatePropertiesRequestTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/UpdatePropertiesRequestTest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/request/UpdatePropertiesRequestTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -28,9 +28,10 @@
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.hasItems;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Property;
 import org.junit.Before;
 import org.junit.Test;
@@ -41,151 +42,78 @@
 public class UpdatePropertiesRequestTest extends AbstractRequestTest {
 
     private UpdatePropertiesRequest request;
+    private Map<Name, Property> validProperties;
 
     @Override
     @Before
     public void beforeEach() {
         super.beforeEach();
+        validProperties = new HashMap<Name, Property>();
+        validProperties.put(validProperty1.getName(), validProperty1);
+        validProperties.put(validProperty2.getName(), validProperty2);
     }
 
     @Override
     protected Request createRequest() {
-        return new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1);
+        return new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowCreatingRequestWithNullFromLocation() {
-        new UpdatePropertiesRequest(null, workspace1, validProperty1);
+        new UpdatePropertiesRequest(null, workspace1, validProperties);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowCreatingRequestWithNullWorkspaceName() {
-        new UpdatePropertiesRequest(validPathLocation1, null, validProperty1);
+        new UpdatePropertiesRequest(validPathLocation1, null, validProperties);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowCreatingRequestWithNullPropertyName() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, (Property[])null);
+        new UpdatePropertiesRequest(validPathLocation, workspace1, null);
     }
 
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameArray() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, new Property[] {});
+    public void shouldNotAllowCreatingRequestWithEmptyPropertyMap() {
+        new UpdatePropertiesRequest(validPathLocation, workspace1, Collections.<Name, Property>emptyMap());
     }
 
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullPropertyNameIterator() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, (Iterator<Property>)null);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameIterator() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, new ArrayList<Property>().iterator());
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithNullPropertyNameIterable() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, (Iterable<Property>)null);
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowCreatingRequestWithEmptyPropertyNameIterable() {
-        new UpdatePropertiesRequest(validPathLocation, workspace1, new ArrayList<Property>());
-    }
-
     @Test
     public void shouldCreateValidRequestWithValidLocationAndValidProperty() {
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1);
+        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
         assertThat(request.on(), is(sameInstance(validPathLocation1)));
         assertThat(request.inWorkspace(), is(sameInstance(workspace1)));
         assertThat(request.hasError(), is(false));
         assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1));
+        assertThat(request.properties().values(), hasItems(validProperty1, validProperty2));
     }
 
     @Test
-    public void shouldCreateValidRequestWithValidLocationAndValidPropertyNames() {
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1, validProperty2);
-        assertThat(request.on(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1, validProperty2));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIteratorOverValidPropertyName() {
-        List<Property> properties = new ArrayList<Property>();
-        properties.add(validProperty1);
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, properties.iterator());
-        assertThat(request.on(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIteratorOverValidPropertyNames() {
-        List<Property> properties = new ArrayList<Property>();
-        properties.add(validProperty1);
-        properties.add(validProperty2);
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, properties.iterator());
-        assertThat(request.on(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1, validProperty2));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIterableWithValidPropertyName() {
-        List<Property> properties = new ArrayList<Property>();
-        properties.add(validProperty1);
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, properties);
-        assertThat(request.on(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1));
-    }
-
-    @Test
-    public void shouldCreateValidRequestWithValidLocationAndIterableWithValidPropertyNames() {
-        List<Property> properties = new ArrayList<Property>();
-        properties.add(validProperty1);
-        properties.add(validProperty2);
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, properties);
-        assertThat(request.on(), is(sameInstance(validPathLocation1)));
-        assertThat(request.hasError(), is(false));
-        assertThat(request.getError(), is(nullValue()));
-        assertThat(request.properties(), hasItems(validProperty1, validProperty2));
-    }
-
-    @Test
-    public void shouldConsiderEqualTwoRequestsWithSameLocationsAndSamePropertyNames() {
-        request = new UpdatePropertiesRequest(validPathLocation1, new String(workspace1), validProperty1, validProperty2);
-        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1,
-                                                                       validProperty2);
+    public void shouldConsiderEqualTwoRequestsWithSameLocationsAndSameProperties() {
+        request = new UpdatePropertiesRequest(validPathLocation1, new String(workspace1), validProperties);
+        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
         assertThat(request, is(request2));
     }
 
     @Test
     public void shouldConsiderNotEqualTwoRequestsWithDifferentLocations() {
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1, validProperty2);
-        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation2, workspace1, validProperty1,
-                                                                       validProperty2);
+        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
+        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation2, workspace1, validProperties);
         assertThat(request.equals(request2), is(false));
     }
 
     @Test
     public void shouldConsiderNotEqualTwoRequestsWithDifferentWorkspaceNames() {
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1, validProperty2);
-        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation1, workspace2, validProperty1,
-                                                                       validProperty2);
+        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
+        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation1, workspace2, validProperties);
         assertThat(request.equals(request2), is(false));
     }
 
     @Test
-    public void shouldConsiderNotEqualTwoRequestsWithSameLocationButDifferentPropertyNames() {
-        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperty1, validProperty2);
-        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation2, workspace1, validProperty1);
+    public void shouldConsiderNotEqualTwoRequestsWithSameLocationButDifferentProperties() {
+        request = new UpdatePropertiesRequest(validPathLocation1, workspace1, validProperties);
+        Map<Name, Property> otherValidProperties = Collections.singletonMap(validProperty1.getName(), validProperty1);
+        UpdatePropertiesRequest request2 = new UpdatePropertiesRequest(validPathLocation2, workspace1, otherValidProperties);
         assertThat(request.equals(request2), is(false));
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -66,6 +66,7 @@
 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.request.BatchRequestBuilder;
 import org.jboss.dna.graph.request.ChangeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.jcr.cache.ChangedNodeInfo;
@@ -148,6 +149,7 @@
     protected final HashMap<UUID, NodeInfo> deletedNodes;
 
     private LinkedList<Request> requests;
+    private BatchRequestBuilder requestBuilder;
     protected Graph.Batch operations;
 
     public SessionCache( JcrSession session,
@@ -183,6 +185,7 @@
 
         // Create the batch operations ...
         this.requests = new LinkedList<Request>();
+        this.requestBuilder = new BatchRequestBuilder(this.requests);
         this.operations = this.store.batch();
     }
 
@@ -233,7 +236,8 @@
             // Create a new batch for future operations ...
             // LinkedList<Request> oldRequests = this.requests;
             this.requests = new LinkedList<Request>();
-            operations = store.batch(this.requests);
+            this.requestBuilder = new BatchRequestBuilder(this.requests);
+            operations = store.batch(this.requestBuilder);
 
             // Remove all the cached items that have been changed or deleted ...
             for (UUID changedUuid : changedNodes.keySet()) {
@@ -273,6 +277,9 @@
             // Find the path of the given node ...
             Path path = getPathFor(nodeUuid);
 
+            // Make sure the builder has finished all the requests ...
+            this.requestBuilder.finishPendingRequest();
+
             // Remove all of the enqueued requests for this branch ...
             LinkedList<Request> branchRequests = new LinkedList<Request>();
             LinkedList<Request> nonBranchRequests = new LinkedList<Request>();
@@ -297,13 +304,14 @@
             }
 
             // Now execute the branch ...
-            Graph.Batch branchBatch = store.batch(branchRequests);
+            Graph.Batch branchBatch = store.batch(new BatchRequestBuilder(branchRequests));
             try {
                 branchBatch.execute();
 
                 // Still have non-branch related requests that we haven't executed ...
                 this.requests = nonBranchRequests;
-                this.operations = store.batch(nonBranchRequests);
+                this.requestBuilder = new BatchRequestBuilder(this.requests);
+                this.operations = store.batch(this.requestBuilder);
 
                 // Remove all the cached, changed or deleted items that were just saved ...
                 for (UUID changedUuid : branchUuids) {

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -59,7 +59,6 @@
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
-import org.jboss.dna.graph.request.RemovePropertiesRequest;
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
@@ -304,16 +303,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertiesRequest)
-     */
-    @Override
-    public void process( RemovePropertiesRequest request ) {
-        if (updatesAllowed(request)) super.process(request);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
      */
     @Override

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -190,16 +190,18 @@
         if (node == null) return;
 
         // Now set (or remove) the properties to the supplied node ...
-        for (Property property : request.properties()) {
-            Name propName = property.getName();
+        for (Map.Entry<Name, Property> entry : request.properties().entrySet()) {
+            Name propName = entry.getKey();
             // Don't allow the child list property to be removed or changed
             if (propName.equals(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST)) continue;
-            if (property.size() == 0) {
+
+            Property property = entry.getValue();
+            if (property == null) {
                 node.remove(propName);
                 continue;
             }
             Object value = null;
-            if (property.size() == 1) {
+            if (property.isSingle()) {
                 value = property.iterator().next();
             } else {
                 value = property.getValuesAsArray();
@@ -502,7 +504,8 @@
         assert original != null;
         assert newParent != null;
         // Get or create the new node ...
-        Path.Segment name = desiredName != null ? context.getValueFactories().getPathFactory().createSegment(desiredName) : (Path.Segment)original.getFqn().getLastElement();
+        Path.Segment name = desiredName != null ? context.getValueFactories().getPathFactory().createSegment(desiredName) : (Path.Segment)original.getFqn()
+                                                                                                                                                  .getLastElement();
 
         // Update the children to account for same-name siblings.
         // This not only updates the FQN of the child nodes, but it also sets the property that stores the

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -864,12 +864,12 @@
                 ObjectOutputStream oos = new ObjectOutputStream(os);
                 int numProps = 0;
                 LargeValueSerializer largeValues = null;
-                Collection<Property> props = request.properties();
+                Map<Name, Property> props = request.properties();
                 References refs = enforceReferentialIntegrity ? new References() : null;
                 if (originalData == null) {
                     largeValues = new LargeValueSerializer(entity);
                     numProps = props.size();
-                    serializer.serializeProperties(oos, numProps, props, largeValues, refs);
+                    serializer.serializeProperties(oos, numProps, props.values(), largeValues, refs);
                 } else {
                     boolean hadLargeValues = !entity.getLargeValues().isEmpty();
                     Set<String> largeValueHashesWritten = hadLargeValues ? new HashSet<String>() : null;
@@ -940,7 +940,7 @@
                 }
             } catch (NoResultException e) {
                 // there are no properties yet ...
-                createProperties(workspaceId, actual.uuid, request.properties());
+                createProperties(workspaceId, actual.uuid, request.properties().values());
             }
 
         } catch (Throwable e) { // Includes PathNotFoundException

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -361,7 +361,7 @@
      */
     public int reserializeProperties( ObjectInputStream input,
                                       ObjectOutputStream output,
-                                      Collection<Property> updatedProperties,
+                                      Map<Name, Property> updatedProperties,
                                       LargeValues largeValues,
                                       LargeValues removedLargeValues,
                                       ReferenceValues references ) throws IOException, ClassNotFoundException {
@@ -371,10 +371,6 @@
         assert largeValues != null;
         assert references != null;
         // Assemble a set of property names to skip deserializing
-        Set<Name> skipNames = new HashSet<Name>();
-        for (Property property : updatedProperties) {
-            skipNames.add(property.getName());
-        }
         Map<Name, Property> allProperties = new HashMap<Name, Property>();
 
         // Read the number of properties ...
@@ -385,7 +381,7 @@
             String nameStr = (String)input.readObject();
             Name name = valueFactories.getNameFactory().create(nameStr);
             assert name != null;
-            if (skipNames.contains(name)) {
+            if (updatedProperties.containsKey(name)) {
                 // Deserialized, but don't materialize ...
                 deserializePropertyValues(input, name, true, largeValues, removedLargeValues, references);
             } else {
@@ -399,9 +395,10 @@
         }
 
         // Add all the updated properties ...
-        for (Property updated : updatedProperties) {
-            if (updated.isEmpty()) {
-                allProperties.remove(updated.getName());
+        for (Map.Entry<Name, Property> entry : updatedProperties.entrySet()) {
+            Property updated = entry.getValue();
+            if (updated == null) {
+                allProperties.remove(entry.getKey());
             } else {
                 allProperties.put(updated.getName(), updated);
             }

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -24,6 +24,7 @@
 package org.jboss.dna.connector.store.jpa;
 
 import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.test.ReadableConnectorTest;
@@ -55,6 +56,17 @@
         source.setMaximumConnectionIdleTimeInSeconds(0);
         source.setLargeValueSizeInBytes(150);
 
+        // Create a graph and look up the root node. We do this to initialize the connection pool and
+        // force the database to be setup at this point. By doing it now, we don't include this overhead
+        // in our test timings.
+        try {
+            Graph graph = Graph.create(source, context);
+            graph.getNodeAt("/");
+        } catch (Throwable t) {
+            Logger.getLogger(getClass()).debug("Unable to read the root node while setting up the \"" + source.getName()
+                                               + "\" JPA source");
+        }
+
         return source;
     }
 

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-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -268,8 +268,9 @@
 
         Property[] initial = new Property[] {prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8};
         Property[] updated = new Property[] {prop2b, prop3b, prop6b};
+        Name[] deleted = new Name[] {};
         SkippedLargeValues removedLargeValues = new SkippedLargeValues();
-        assertReserializable(serializer, removedLargeValues, initial, updated);
+        assertReserializable(serializer, removedLargeValues, initial, updated, deleted);
 
         assertThat(largeValues.getCount(), is(3));
         assertThat(removedLargeValues.getCount(), is(2)); // p2's value and p6's original value
@@ -411,26 +412,29 @@
     protected void assertReserializable( Serializer serializer,
                                          Serializer.LargeValues removedLargeValues,
                                          Property[] originalProperties,
-                                         Property... updatedProperties ) throws IOException, ClassNotFoundException {
+                                         Property[] updatedProperties,
+                                         Name[] removedProperties ) throws IOException, ClassNotFoundException {
         Collection<Name> propertiesThatStay = new HashSet<Name>();
         Collection<Name> propertiesThatAreDeleted = new HashSet<Name>();
         for (Property prop : originalProperties) {
             propertiesThatStay.add(prop.getName());
         }
         for (Property prop : updatedProperties) {
-            if (prop.isEmpty()) {
-                propertiesThatAreDeleted.add(prop.getName());
-                propertiesThatStay.remove(prop.getName());
-            } else {
-                propertiesThatStay.add(prop.getName());
-            }
+            propertiesThatStay.add(prop.getName());
         }
+        for (Name removedPropertyName : removedProperties) {
+            propertiesThatAreDeleted.add(removedPropertyName);
+            propertiesThatStay.remove(removedPropertyName);
+        }
 
         // Serialize the properties one at a time ...
         byte[] bytes = serialize(serializer, originalProperties);
 
         // Now reserialize, updating the properties ...
-        Collection<Property> updatedProps = Arrays.asList(updatedProperties);
+        Map<Name, Property> updatedProps = new HashMap<Name, Property>();
+        for (Property property : updatedProperties) {
+            updatedProps.put(property.getName(), property);
+        }
         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
         ObjectInputStream ois = new ObjectInputStream(bais);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();

Modified: trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java	2009-04-06 15:55:23 UTC (rev 808)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java	2009-04-07 21:23:28 UTC (rev 809)
@@ -61,7 +61,6 @@
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
-import org.jboss.dna.graph.request.RemovePropertiesRequest;
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
@@ -347,18 +346,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertiesRequest)
-     */
-    @Override
-    public void process( RemovePropertiesRequest request ) {
-        logger.trace(request.toString());
-        verifyUpdatesAllowed();
-        super.process(request);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
      */
     @Override
@@ -727,6 +714,7 @@
 
     /**
      * Create a file.
+     * 
      * @param path
      * @param file
      * @param content
@@ -735,14 +723,17 @@
      */
     private void newFile( String path,
                           String file,
-                          byte[] content, String message ) throws SVNException {
+                          byte[] content,
+                          String message ) throws SVNException {
         SVNNodeKind childKind = repository.checkPath(file, -1);
         if (childKind == SVNNodeKind.NONE) {
             ScmAction addFileNodeAction = addFile(path, file, content);
             SVNActionExecutor executor = new SVNActionExecutor(repository);
-            executor.execute(addFileNodeAction,message);
+            executor.execute(addFileNodeAction, message);
         } else {
-            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Item with name '{0}' can't be created (already exist)", file);
+            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
+                                                         "Item with name '{0}' can't be created (already exist)",
+                                                         file);
             throw new SVNException(err);
         }
     }
@@ -764,7 +755,7 @@
      */
     public ScmAction addFile( String path,
                               String file,
-                              byte[] content) {
+                              byte[] content ) {
         return new AddFile(path, file, content);
     }
 
@@ -858,16 +849,16 @@
 
     }
 
-//    private Date getCreatedOn( Object[] objs ) {
-//        Date createdOn = null;
-//        for (Object object : objs) {
-//            if (object instanceof Date) {
-//                createdOn = (Date)object;
-//
-//            }
-//        }
-//        return createdOn;
-//    }
+    // private Date getCreatedOn( Object[] objs ) {
+    // Date createdOn = null;
+    // for (Object object : objs) {
+    // if (object instanceof Date) {
+    // createdOn = (Date)object;
+    //
+    // }
+    // }
+    // return createdOn;
+    // }
 
     private byte[] getContent( Object[] objs ) {
         byte[] content = null;



From dna-commits at lists.jboss.org  Tue Apr  7 18:21:03 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 07 Apr 2009 18:21:03 -0400
Subject: [dna-commits] DNA SVN: r810 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LrJfD-0008I7-JS@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-07 18:21:03 -0400 (Tue, 07 Apr 2009)
New Revision: 810

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-194 Implement update JCR capability

Uncommented more TCK unit tests for Level 2.


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-04-07 21:23:28 UTC (rev 809)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-07 22:21:03 UTC (rev 810)
@@ -727,6 +727,10 @@
     public final Property setProperty( String name,
                                        Calendar value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
 
     }
@@ -751,6 +755,10 @@
     public final Property setProperty( String name,
                                        InputStream value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
@@ -774,6 +782,10 @@
     public final Property setProperty( String name,
                                        Node value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
@@ -785,6 +797,10 @@
     public final Property setProperty( String name,
                                        String value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value)));
     }
 
@@ -797,6 +813,10 @@
                                        String value,
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(type, value)));
     }
 
@@ -831,9 +851,26 @@
     public final Property setProperty( String name,
                                        Value value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingSingleValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), (JcrValue)value));
     }
 
+    protected final Property removeExistingSingleValuedProperty( String name )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        PropertyId id = new PropertyId(nodeUuid, nameFrom(name));
+        AbstractJcrProperty property = cache.findJcrProperty(id);
+        if (property != null) {
+            assert !property.isMultiple();
+            property.remove();
+            return property;
+        }
+        // else the property doesn't exist ...
+        throw new RepositoryException(JcrI18n.propertyNotFoundOnNode.text(name, getPath(), cache.workspaceName()));
+    }
+
     /**
      * {@inheritDoc}
      * 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-07 21:23:28 UTC (rev 809)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-07 22:21:03 UTC (rev 810)
@@ -69,6 +69,7 @@
     public static I18n itemNotFoundWithUuid;
     public static I18n itemNotFoundAtPath;
     public static I18n itemNotFoundAtPathRelativeToReferenceNode;
+    public static I18n propertyNotFoundOnNode;
     public static I18n propertyNotFoundAtPathRelativeToReferenceNode;
     public static I18n nodeNotFoundAtPathRelativeToReferenceNode;
     public static I18n childNotFoundUnderNode;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-07 21:23:28 UTC (rev 809)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-07 22:21:03 UTC (rev 810)
@@ -50,6 +50,8 @@
 @NotThreadSafe
 final class JcrValue implements Value {
 
+    static final JcrValue[] EMPTY_ARRAY = new JcrValue[] {};
+
     private final SessionCache sessionCache;
     private final ValueFactories valueFactories;
     private final int type;

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-07 21:23:28 UTC (rev 809)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-07 22:21:03 UTC (rev 810)
@@ -59,6 +59,7 @@
 itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}": {2}
 itemNotFoundAtPath = An item at "{0}" could not be found in workspace "{1}"
 itemNotFoundAtPathRelativeToReferenceNode = An item at "{0}" relative to "{1}" could not be found in workspace "{2}"
+propertyNotFoundOnNode = Property "{0}" does not exist on node "{1}" in workspace "{2}"
 propertyNotFoundAtPathRelativeToReferenceNode = A property at "{0}" relative to "{1}" could not be found in workspace "{2}"
 nodeNotFoundAtPathRelativeToReferenceNode = A node at "{0}" relative to "{1}" could not be found in workspace "{2}"
 childNotFoundUnderNode = The child "{0}" could not be found under "{1}" in workspace "{2}"

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-07 21:23:28 UTC (rev 809)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-07 22:21:03 UTC (rev 810)
@@ -41,11 +41,20 @@
 import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
 import org.apache.jackrabbit.test.api.SessionUUIDTest;
+import org.apache.jackrabbit.test.api.SetPropertyBooleanTest;
+import org.apache.jackrabbit.test.api.SetPropertyCalendarTest;
+import org.apache.jackrabbit.test.api.SetPropertyConstraintViolationExceptionTest;
+import org.apache.jackrabbit.test.api.SetPropertyDoubleTest;
+import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
+import org.apache.jackrabbit.test.api.SetPropertyLongTest;
+import org.apache.jackrabbit.test.api.SetValueBinaryTest;
 import org.apache.jackrabbit.test.api.SetValueBooleanTest;
+import org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest;
 import org.apache.jackrabbit.test.api.SetValueDateTest;
 import org.apache.jackrabbit.test.api.SetValueDoubleTest;
 import org.apache.jackrabbit.test.api.SetValueLongTest;
 import org.apache.jackrabbit.test.api.SetValueReferenceTest;
+import org.apache.jackrabbit.test.api.SetValueVersionExceptionTest;
 import org.apache.jackrabbit.test.api.ValueFactoryTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
@@ -164,26 +173,26 @@
             // addTestSuite(NodeOrderableChildNodesTest.class);
             addTestSuite(PropertyTest.class);
             //
-            // addTestSuite(SetValueBinaryTest.class);
+            addTestSuite(SetValueBinaryTest.class);
             addTestSuite(SetValueBooleanTest.class);
             addTestSuite(SetValueDateTest.class);
             addTestSuite(SetValueDoubleTest.class);
             addTestSuite(SetValueLongTest.class);
             addTestSuite(SetValueReferenceTest.class);
             // addTestSuite(SetValueStringTest.class);
-            // addTestSuite(SetValueConstraintViolationExceptionTest.class);
+            addTestSuite(SetValueConstraintViolationExceptionTest.class);
             // addTestSuite(SetValueValueFormatExceptionTest.class);
-            // addTestSuite(SetValueVersionExceptionTest.class);
-            //
-            // addTestSuite(SetPropertyBooleanTest.class);
-            // addTestSuite(SetPropertyCalendarTest.class);
-            // addTestSuite(SetPropertyDoubleTest.class);
-            // addTestSuite(SetPropertyInputStreamTest.class);
-            // addTestSuite(SetPropertyLongTest.class);
+            addTestSuite(SetValueVersionExceptionTest.class);
+
+            addTestSuite(SetPropertyBooleanTest.class);
+            addTestSuite(SetPropertyCalendarTest.class);
+            addTestSuite(SetPropertyDoubleTest.class);
+            addTestSuite(SetPropertyInputStreamTest.class);
+            addTestSuite(SetPropertyLongTest.class);
             // addTestSuite(SetPropertyNodeTest.class);
             // addTestSuite(SetPropertyStringTest.class);
             // addTestSuite(SetPropertyValueTest.class);
-            // addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
+            addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
             // addTestSuite(SetPropertyAssumeTypeTest.class);
             //
             // addTestSuite(NodeItemIsModifiedTest.class);



From dna-commits at lists.jboss.org  Tue Apr  7 18:22:57 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 07 Apr 2009 18:22:57 -0400
Subject: [dna-commits] DNA SVN: r811 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LrJh3-0008Nw-0t@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-07 18:22:56 -0400 (Tue, 07 Apr 2009)
New Revision: 811

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
Log:
DNA-344 JcrSession.getValueFactory.create(String, int) Does Not Fail Fast as per Specification

Applied second patch to clean up some of the code that is no longer needed.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-07 22:21:03 UTC (rev 810)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-07 22:22:56 UTC (rev 811)
@@ -343,11 +343,7 @@
             case PropertyType.BOOLEAN:
                 // Make sure the existing value is valid per the current type.
                 // This is required if we rely upon the value factories to cast correctly.
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY) {
                     throw createValueFormatException(boolean.class);
                 }
                 try {
@@ -357,15 +353,8 @@
                 }
 
             case PropertyType.DATE:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else if (this.type == PropertyType.DOUBLE) {
-                    value = valueFactories.getDoubleFactory().create(this.value);
-                } else if (this.type == PropertyType.LONG) {
-                    value = valueFactories.getLongFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY && this.type != PropertyType.DOUBLE
+                    && this.type != PropertyType.LONG) {
                     throw createValueFormatException(Calendar.class);
                 }
                 try {
@@ -375,13 +364,7 @@
                 }
 
             case PropertyType.NAME:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else if (this.type == PropertyType.PATH) {
-                    value = valueFactories.getPathFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY && this.type != PropertyType.PATH) {
                     throw createValueFormatException(Name.class);
                 }
                 try {
@@ -391,13 +374,7 @@
                 }
 
             case PropertyType.PATH:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else if (this.type == PropertyType.NAME) {
-                    value = valueFactories.getNameFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY && this.type != PropertyType.NAME) {
                     throw createValueFormatException(Path.class);
                 }
                 try {
@@ -407,11 +384,7 @@
                 }
 
             case PropertyType.REFERENCE:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY) {
                     throw createValueFormatException(Node.class);
                 }
                 try {
@@ -420,15 +393,8 @@
                     throw createValueFormatException(vfe);
                 }
             case PropertyType.DOUBLE:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else if (this.type == PropertyType.LONG) {
-                    value = valueFactories.getLongFactory().create(this.value);
-                } else if (this.type == PropertyType.DATE) {
-                    value = valueFactories.getDateFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY && this.type != PropertyType.LONG
+                    && this.type != PropertyType.DATE) {
                     throw createValueFormatException(double.class);
                 }
                 try {
@@ -437,15 +403,8 @@
                     throw createValueFormatException(vfe);
                 }
             case PropertyType.LONG:
-                if (this.type == PropertyType.STRING) {
-                    value = valueFactories.getStringFactory().create(this.value);
-                } else if (this.type == PropertyType.BINARY) {
-                    value = valueFactories.getBinaryFactory().create(this.value);
-                } else if (this.type == PropertyType.DOUBLE) {
-                    value = valueFactories.getDoubleFactory().create(this.value);
-                } else if (this.type == PropertyType.DATE) {
-                    value = valueFactories.getDateFactory().create(this.value);
-                } else {
+                if (this.type != PropertyType.STRING && this.type != PropertyType.BINARY && this.type != PropertyType.DOUBLE
+                    && this.type != PropertyType.DATE) {
                     throw createValueFormatException(long.class);
                 }
                 try {



From dna-commits at lists.jboss.org  Tue Apr  7 18:28:54 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 07 Apr 2009 18:28:54 -0400
Subject: [dna-commits] DNA SVN: r812 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LrJmo-0001as-05@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-07 18:28:53 -0400 (Tue, 07 Apr 2009)
New Revision: 812

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-343 TCK Tests for Node Mixin Modification Methods Fail

Applied the patch that takes a stab the missing functionality. 

In addition to the usual rules required by the JCR 1.0 specification, the canAddMixin and addMixin methods enforce internal consistency by requiring that: 
     * No properties defined by the mixin type can have the same name as any property defined by the node's primary type or any of its existing mixin types. 
     * No child nodes defined by the mixin type can have the same name as any child node defined by the node's primary type or any of its existing mixin types. 
     * If the node has a current residual definition for child nodes and/or properties, all nodes and properties that share a name with a child node definition or property definition from the new mixin type must be compatible with the definition provided by the new mixin type. 

The removeMixin imposes the following rule in addition to those required by the JCR 1.0 specification: 
     * A mixin type can be removed if and only if all of the node's existing child nodes and properties would still have a valid definition from the node's primary type or other mixin types. In practice, this means that either the node must have a residual definition compatible with any of the remaining child nodes or properties that currently use a definition from the to-be-removed mixin type or all of the child nodes and properties that use a definition from the to-be-removed mixin type must be removed prior to calling this method. 

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-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -27,9 +27,12 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.regex.Pattern;
 import javax.jcr.InvalidItemStateException;
@@ -49,6 +52,7 @@
 import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
@@ -224,7 +228,7 @@
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getPrimaryNodeType()
      */
-    public NodeType getPrimaryNodeType() throws RepositoryException {
+    public JcrNodeType getPrimaryNodeType() throws RepositoryException {
         Name primaryTypeName = nodeInfo().getPrimaryTypeName();
         return session().nodeTypeManager().getNodeType(primaryTypeName);
     }
@@ -251,26 +255,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#canAddMixin(java.lang.String)
-     */
-    public final boolean canAddMixin( String mixinName ) {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#removeMixin(java.lang.String)
-     */
-    public final void removeMixin( String mixinName ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see javax.jcr.Node#getPrimaryItem()
      */
     public final Item getPrimaryItem() throws RepositoryException {
@@ -604,17 +588,300 @@
 
     /**
      * {@inheritDoc}
+     * <p>
+     * <b>DNA Implementation Notes</b>
+     * </p>
+     * <p>
+     * DNA imposes the following additional restrictions on the addition of mixin types in addition to the restrictions provided
+     * by the JCR 1.0 specification:
+     * <ol>
+     * <li>No properties defined by the mixin type can have the same name as any property defined by the node's primary type or
+     * any of its existing mixin types.</li>
+     * <li>No child nodes defined by the mixin type can have the same name as any child node defined by the node's primary type or
+     * any of its existing mixin types.</li>
+     * <li>If the node has a current residual definition for child nodes and/or properties, all nodes and properties that share a
+     * name with a child node definition or property definition from the new mixin type must be compatible with the definition
+     * provided by the new mixin type.</li>
+     * </ol>
+     * </p>
      * 
-     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#canAddMixin(java.lang.String)
+     */
+    public final boolean canAddMixin( String mixinName ) throws NoSuchNodeTypeException, RepositoryException {
+        CheckArg.isNotNull(mixinName, "mixinName");
+        CheckArg.isNotZeroLength(mixinName, "mixinName");
+
+        JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
+
+        if (this.isLocked()) {
+            return false;
+        }
+
+        if (this.getDefinition().isProtected()) {
+            return false;
+        }
+
+        // TODO: Check access control when that support is added
+        // TODO: Throw VersionException if this node is versionable and checked in or unversionable and the nearest versionable
+        // ancestor is checked in
+
+        NodeType primaryType = this.getPrimaryNodeType();
+        NodeType[] mixinTypes = this.getMixinNodeTypes();
+
+        if (!mixinCandidateType.isMixin()) {
+            return false;
+        }
+
+        if (mixinCandidateType.conflictsWith(primaryType, mixinTypes)) {
+            return false;
+        }
+
+        // ------------------------------------------------------------------------------
+        // Check for any existing properties based on residual definitions that conflict
+        // ------------------------------------------------------------------------------
+        for (JcrPropertyDefinition propertyDefinition : mixinCandidateType.propertyDefinitions()) {
+            AbstractJcrProperty existingProp = cache.findJcrProperty(new PropertyId(nodeUuid,
+                                                                                    propertyDefinition.getInternalName()));
+            if (existingProp != null) {
+                if (propertyDefinition.isMultiple()) {
+                    if (!propertyDefinition.canCastToTypeAndSatisfyConstraints(existingProp.getValues())) {
+                        return false;
+                    }
+                } else {
+                    if (!propertyDefinition.canCastToTypeAndSatisfyConstraints(existingProp.getValue())) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        // ------------------------------------------------------------------------------
+        // Check for any existing child nodes based on residual definitions that conflict
+        // ------------------------------------------------------------------------------
+        Set<Name> mixinChildNodeNames = new HashSet<Name>();
+        for (JcrNodeDefinition nodeDefinition : mixinCandidateType.childNodeDefinitions()) {
+            mixinChildNodeNames.add(nodeDefinition.getInternalName());
+        }
+
+        for (Name nodeName : mixinChildNodeNames) {
+            // Need to figure out if the child node requires an SNS definition
+            int snsCount = nodeInfo().getChildren().getCountOfSameNameSiblingsWithName(nodeName);
+
+            for (Iterator<ChildNode> iter = nodeInfo().getChildren().getChildren(nodeName); iter.hasNext();) {
+                AbstractJcrNode childNode = cache.findJcrNode(iter.next().getUuid());
+                JcrNodeDefinition match = this.cache.nodeTypes().findChildNodeDefinition(mixinCandidateType.getInternalName(),
+                                                                                         Collections.<Name>emptyList(),
+                                                                                         nodeName,
+                                                                                         childNode.getPrimaryNodeType().getInternalName(),
+                                                                                         snsCount,
+                                                                                         false);
+
+                if (match == null) {
+                    return false;
+                }
+            }
+
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * <b>DNA Implementation Notes</b>
+     * </p>
+     * <p>
+     * The criteria noted in {@link #canAddMixin(String)} must be satisifed in addition to the criteria defined in the JCR 1.0
+     * specification.
+     * </p>
+     * 
      * @see javax.jcr.Node#addMixin(java.lang.String)
      */
-    public final void addMixin( String mixinName ) {
-        throw new UnsupportedOperationException();
+    public final void addMixin( String mixinName ) throws RepositoryException {
+        CheckArg.isNotNull(mixinName, "mixinName");
+        CheckArg.isNotZeroLength(mixinName, "mixinName");
+
+        JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
+
+        // Check this separately since it throws a different type of exception
+        if (this.isLocked()) {
+            throw new LockException();
+        }
+
+        if (!canAddMixin(mixinName)) {
+            throw new ConstraintViolationException();
+        }
+
+        Property existingMixinProperty = getProperty(JcrLexicon.MIXIN_TYPES);
+        Value[] existingMixinValues;
+        if (existingMixinProperty != null) {
+            existingMixinValues = existingMixinProperty.getValues();
+        } else {
+            existingMixinValues = new Value[0];
+        }
+
+        Value[] newMixinValues = new Value[existingMixinValues.length + 1];
+        System.arraycopy(existingMixinValues, 0, newMixinValues, 0, existingMixinValues.length);
+        newMixinValues[newMixinValues.length - 1] = valueFrom(PropertyType.NAME, mixinCandidateType.getInternalName());
+
+        cache.findJcrProperty(editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, false));
+
+        // ------------------------------------------------------------------------------
+        // Create any auto-created properties/nodes from new type
+        // ------------------------------------------------------------------------------
+
+        for (JcrPropertyDefinition propertyDefinition : mixinCandidateType.propertyDefinitions()) {
+            if (propertyDefinition.isAutoCreated() && !propertyDefinition.isProtected()) {
+                if (null == cache.findJcrProperty(new PropertyId(nodeUuid, propertyDefinition.getInternalName()))) {
+                    assert propertyDefinition.getDefaultValues() != null;
+                    if (propertyDefinition.isMultiple()) {
+                        setProperty(propertyDefinition.getName(), propertyDefinition.getDefaultValues());
+                    } else {
+                        assert propertyDefinition.getDefaultValues().length == 1;
+                        setProperty(propertyDefinition.getName(), propertyDefinition.getDefaultValues()[0]);
+                    }
+                }
+            }
+        }
+
+        for (JcrNodeDefinition nodeDefinition : mixinCandidateType.childNodeDefinitions()) {
+            if (nodeDefinition.isAutoCreated() && !nodeDefinition.isProtected()) {
+                Name nodeName = nodeDefinition.getInternalName();
+                if (!nodeInfo().getChildren().getChildren(nodeName).hasNext()) {
+                    assert nodeDefinition.getDefaultPrimaryType() != null;
+                    editor().createChild(nodeName,
+                                         (UUID)null,
+                                         ((JcrNodeType)nodeDefinition.getDefaultPrimaryType()).getInternalName());
+                }
+            }
+        }
+
     }
 
     /**
      * {@inheritDoc}
+     * <p>
+     * <b>DNA Implementation Notes</b>
+     * </p>
+     * <p>
+     * DNA allows the removal of a mixin type if and only if all of the node's existing child nodes and properties would still
+     * have a valid definition from the node's primary type or other mixin types. In practice, this means that either the node
+     * must have a residual definition compatible with any of the remaining child nodes or properties that currently use a
+     * definition from the to-be-removed mixin type or all of the child nodes and properties that use a definition from the
+     * to-be-removed mixin type must be removed prior to calling this method.
+     * </p>
+     * *
      * 
+     * @see javax.jcr.Node#removeMixin(java.lang.String)
+     */
+    public final void removeMixin( String mixinName ) throws RepositoryException {
+
+        if (this.isLocked()) {
+            throw new LockException();
+        }
+
+        // TODO: Check access control when that support is added
+        // TODO: Throw VersionException if this node is versionable and checked in or unversionable and the nearest versionable
+        // ancestor is checked in
+
+        Property existingMixinProperty = getProperty(JcrLexicon.MIXIN_TYPES);
+
+        if (existingMixinProperty == null) {
+            throw new NoSuchNodeTypeException();
+        }
+
+        Value[] existingMixinValues = existingMixinProperty.getValues();
+
+        if (existingMixinValues.length == 0) {
+            throw new NoSuchNodeTypeException();
+        }
+
+        // ------------------------------------------------------------------------------
+        // Build the new list of mixin types
+        // ------------------------------------------------------------------------------
+
+        int newMixinValuesCount = existingMixinValues.length - 1;
+        Value[] newMixinValues = new Value[newMixinValuesCount];
+        List<Name> newMixinNames = new ArrayList<Name>(newMixinValuesCount);
+        Name primaryTypeName = getPrimaryNodeType().getInternalName();
+
+        int j = 0;
+        for (int i = 0; i < existingMixinValues.length; i++) {
+            if (!existingMixinValues[i].getString().equals(mixinName)) {
+                if (j < newMixinValuesCount) {
+                    newMixinValues[j++] = existingMixinValues[i];
+                    newMixinNames.add(cache.nameFactory.create(existingMixinValues[i].getString()));
+                } else {
+                    throw new NoSuchNodeTypeException();
+                }
+            }
+        }
+
+        // ------------------------------------------------------------------------------
+        // Check that any remaining properties that use the mixin type to be removed
+        // match the residual definition for the node.
+        // ------------------------------------------------------------------------------
+
+        for (PropertyIterator iter = getProperties(); iter.hasNext();) {
+            Property property = iter.nextProperty();
+            if (mixinName.equals(property.getDefinition().getDeclaringNodeType().getName())) {
+                JcrPropertyDefinition match;
+
+                // Only the residual definition would work - if there were any other definition for this name,
+                // the mixin type would not have been added due to the conflict
+                if (property.getDefinition().isMultiple()) {
+                    match = cache.nodeTypes().findPropertyDefinition(primaryTypeName,
+                                                                     newMixinNames,
+                                                                     JcrNodeType.RESIDUAL_NAME,
+                                                                     property.getValues(),
+                                                                     true);
+                } else {
+                    match = cache.nodeTypes().findPropertyDefinition(primaryTypeName,
+                                                                     newMixinNames,
+                                                                     JcrNodeType.RESIDUAL_NAME,
+                                                                     property.getValue(),
+                                                                     true,
+                                                                     true);
+                }
+
+                if (match == null) {
+                    throw new ConstraintViolationException();
+                }
+            }
+        }
+
+        // ------------------------------------------------------------------------------
+        // Check that any remaining child nodes that use the mixin type to be removed
+        // match the residual definition for the node.
+        // ------------------------------------------------------------------------------
+        for (NodeIterator iter = getNodes(); iter.hasNext();) {
+            AbstractJcrNode node = (AbstractJcrNode)iter.nextNode();
+            Name childNodeName = cache.nameFactory.create(node.getName());
+            int snsCount = node.nodeInfo().getChildren().getCountOfSameNameSiblingsWithName(childNodeName);
+            if (mixinName.equals(node.getDefinition().getDeclaringNodeType().getName())) {
+                // Only the residual definition would work - if there were any other definition for this name,
+                // the mixin type would not have been added due to the conflict
+                JcrNodeDefinition match = cache.nodeTypes().findChildNodeDefinition(primaryTypeName,
+                                                                                    newMixinNames,
+                                                                                    JcrNodeType.RESIDUAL_NAME,
+                                                                                    node.getPrimaryNodeType().getInternalName(),
+                                                                                    snsCount,
+                                                                                    true);
+
+                if (match == null) {
+                    throw new ConstraintViolationException();
+                }
+            }
+        }
+
+        cache.findJcrProperty(editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, false));
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see javax.jcr.Node#addNode(java.lang.String)
      */
     public final Node addNode( String relPath )

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -26,9 +26,11 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+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 javax.jcr.PropertyType;
 import javax.jcr.Value;
@@ -488,4 +490,128 @@
     final RepositoryNodeTypeManager nodeTypeManager() {
         return nodeTypeManager;
     }
+
+    /**
+     * Returns whether this node type is in conflict with the provided primary node type or mixin types.
+     * <p>
+     * A node type is in conflict with another set of node types if either of the following is true:
+     * <ol>
+     * <li>This node type has the same name as any of the other node types</li>
+     * <li>This node type defines a property (or inherits the definition of a property) with the same name as a property defined
+     * in any of the other node types <i>unless</i> this node type and the other node type both inherited the property definition
+     * from the same ancestor node type</li>
+     * <li>This node type defines a child node (or inherits the definition of a child node) with the same name as a child node
+     * defined in any of the other node types <i>unless</i> this node type and the other node type both inherited the child node
+     * definition from the same ancestor node type</li>
+     * </ol>
+     * </p>
+     * 
+     * @param primaryNodeType the primary node type to check
+     * @param mixinNodeTypes the mixin node types to check
+     * @return true if this node type conflicts with the provided primary or mixin node types as defined below
+     */
+    final boolean conflictsWith( NodeType primaryNodeType,
+                                 NodeType[] mixinNodeTypes ) {
+        Map<PropertyDefinitionId, JcrPropertyDefinition> props = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
+        /*
+         * Need to have the same parent name for all PropertyDefinitionIds and NodeDefinitionIds as we're checking for conflicts on the definition name
+         */
+        final Name DEFAULT_NAME = this.name;
+
+        for (JcrPropertyDefinition property : propertyDefinitions()) {
+            /*
+             * I'm trying really hard to reuse existing code, but it's a stretch in this case.  I don't care about the property
+             * types or where they were declared... if more than one definition with the given name exists (not counting definitions
+             * inherited from the same root definition), then there is a conflict.
+             */
+            PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
+                                                                property.isMultiple());
+            props.put(pid, property);
+        }
+
+        /*
+         * The specification does not mandate whether this should or should not be consider a conflict.  However, the Apache
+         * TCK canRemoveMixin test cases assume that this will generate a conflict.
+         */
+        if (primaryNodeType.getName().equals(getName())) {
+            // This node type has already been applied to the node
+            return true;
+        }
+
+        for (JcrPropertyDefinition property : ((JcrNodeType)primaryNodeType).propertyDefinitions()) {
+            PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
+                                                                property.isMultiple());
+            JcrPropertyDefinition oldProp = props.put(pid, property);
+            if (oldProp != null) {
+                String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
+                String propTypeName = property.getDeclaringNodeType().getName();
+                if (!oldPropTypeName.equals(propTypeName)) {
+                    // The two types conflict as both separately declare a property with the same name
+                    return true;
+                }
+            }
+        }
+
+        for (NodeType mixinNodeType : mixinNodeTypes) {
+            /*
+             * The specification does not mandate whether this should or should not be consider a conflict.  However, the Apache
+             * TCK canRemoveMixin test cases assume that this will generate a conflict.
+             */
+            if (mixinNodeType.getName().equals(getName())) {
+                // This node type has already been applied to the node
+                return true;
+            }
+
+            for (JcrPropertyDefinition property : ((JcrNodeType)mixinNodeType).propertyDefinitions()) {
+                PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
+                                                                    property.isMultiple());
+                JcrPropertyDefinition oldProp = props.put(pid, property);
+                if (oldProp != null) {
+                    String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
+                    String propTypeName = property.getDeclaringNodeType().getName();
+                    if (!oldPropTypeName.equals(propTypeName)) {
+                        // The two types conflict as both separately declare a property with the same name
+                        return true;
+                    }
+                }
+            }
+        }
+
+        Map<NodeDefinitionId, JcrNodeDefinition> childNodes = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
+
+        for (JcrNodeDefinition childNode : childNodeDefinitions()) {
+            NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
+            childNodes.put(nid, childNode);
+        }
+
+        for (JcrNodeDefinition childNode : ((JcrNodeType)primaryNodeType).childNodeDefinitions()) {
+            NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
+            JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
+            if (oldNode != null) {
+                String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
+                String childNodeTypeName = childNode.getDeclaringNodeType().getName();
+                if (!oldNodeTypeName.equals(childNodeTypeName)) {
+                    // The two types conflict as both separately declare a child node with the same name
+                    return true;
+                }
+            }
+        }
+
+        for (NodeType mixinNodeType : mixinNodeTypes) {
+            for (JcrNodeDefinition childNode : ((JcrNodeType)mixinNodeType).childNodeDefinitions()) {
+                NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
+                JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
+                if (oldNode != null) {
+                    String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
+                    String childNodeTypeName = childNode.getDeclaringNodeType().getName();
+                    if (!oldNodeTypeName.equals(childNodeTypeName)) {
+                        // The two types conflict as both separately declare a child node with the same name
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -90,7 +90,7 @@
      * 
      * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
      */
-    public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
+    public JcrNodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
         Name ntName = context.getValueFactories().getNameFactory().create(nodeTypeName);
         JcrNodeType type = repositoryTypeManager.getNodeType(ntName);
         if (type != null) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -43,6 +43,7 @@
      */
     static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.Property property ) {
         Object value = property.getFirstValue();
+        if (value == null) return PropertyType.UNDEFINED;
 
         // Get the DNA property type for this ...
         switch (org.jboss.dna.graph.property.PropertyType.discoverType(value)) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -539,6 +539,12 @@
          */
         public PropertyId setProperty( Name name,
                                        JcrValue value ) throws ConstraintViolationException {
+            return setProperty(name, value, true);
+        }
+
+        public PropertyId setProperty( Name name,
+                                       JcrValue value,
+                                       boolean skipProtected ) throws ConstraintViolationException {
             assert name != null;
             assert value != null;
             JcrPropertyDefinition definition = null;
@@ -576,7 +582,7 @@
                                                                 name,
                                                                 value,
                                                                 true,
-                                                                true);
+                                                                skipProtected);
                 if (definition == null) {
                     throw new ConstraintViolationException();
                 }
@@ -607,6 +613,9 @@
         /**
          * Set the values for the property. If the property does not exist, it will be added. If the property does exist, the
          * existing values will be replaced with those that are supplied.
+         * <p>
+         * This method will not set protected property definitions and should be used in almost all cases.
+         * </p>
          * 
          * @param name the property name; may not be null
          * @param values new property values, all of which must have the same {@link Value#getType() property type}; may not be
@@ -617,6 +626,25 @@
          */
         public PropertyId setProperty( Name name,
                                        Value[] values ) throws ConstraintViolationException {
+            return setProperty(name, values, true);
+        }
+
+        /**
+         * Set the values for the property. If the property does not exist, it will be added. If the property does exist, the
+         * existing values will be replaced with those that are supplied.
+         * 
+         * @param name the property name; may not be null
+         * @param values new property values, all of which must have the same {@link Value#getType() property type}; may not be
+         *        null but may be empty
+         * @param skipProtected if true, attempts to set protected properties will fail. If false, attempts to set protected
+         *        properties will be allowed.
+         * @return the identifier for the property; never null
+         * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
+         *         definition constraint
+         */
+        public PropertyId setProperty( Name name,
+                                       Value[] values,
+                                       boolean skipProtected ) throws ConstraintViolationException {
             assert name != null;
             assert values != null;
             int numValues = values.length;
@@ -660,7 +688,7 @@
                                                                 node.getMixinTypeNames(),
                                                                 name,
                                                                 values,
-                                                                true);
+                                                                skipProtected);
                 if (definition == null) {
                     throw new ConstraintViolationException();
                 }
@@ -757,8 +785,8 @@
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
-                                                                                                                 .getString());
+                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING,
+                                                  definition.getId().getString());
                     setProperty(DnaLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
@@ -887,10 +915,7 @@
             // ---------------------------------------
             // Now record the changes to the store ...
             // ---------------------------------------
-            Graph.Create<Graph.Batch> create = operations.createUnder(currentLocation)
-                                                         .nodeNamed(name)
-                                                         .with(desiredUuid)
-                                                         .with(primaryTypeProp);
+            Graph.Create<Graph.Batch> create = operations.createUnder(currentLocation).nodeNamed(name).with(desiredUuid).with(primaryTypeProp);
             if (nodeDefnDefn != null) {
                 create = create.with(nodeDefinitionProp);
             }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -229,16 +229,11 @@
         node.getProperty("../good/path");
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shoudNotAllowAddMixin() throws Exception {
-        node.addMixin(null);
-    }
+    /*
+     * More comprehensive tests of addMixin, removeMixin, and canAddMixin require additional setup 
+     * and are in MixinTest
+     */
 
-    @Test
-    public void shoudNotAllowCanAddMixin() throws Exception {
-        assertThat(node.canAddMixin(null), is(false));
-    }
-
     @Test( expected = UnsupportedOperationException.class )
     public void shoudNotAllowCancelMerge() throws Exception {
         node.cancelMerge(null);
@@ -702,11 +697,6 @@
         node.orderBefore(null, null);
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowRemoveMixin() throws Exception {
-        node.removeMixin(null);
-    }
-
     @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotAllowRestoreVersionName() throws Exception {
         node.restore((String)null, false);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-07 22:22:56 UTC (rev 811)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -38,6 +38,9 @@
 import org.apache.jackrabbit.test.RepositoryStub;
 import org.apache.jackrabbit.test.api.AddNodeTest;
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
+import org.apache.jackrabbit.test.api.NodeAddMixinTest;
+import org.apache.jackrabbit.test.api.NodeCanAddMixinTest;
+import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
 import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
 import org.apache.jackrabbit.test.api.SessionUUIDTest;
@@ -200,9 +203,9 @@
             // addTestSuite(PropertyItemIsModifiedTest.class);
             // addTestSuite(PropertyItemIsNewTest.class);
             //
-            // addTestSuite(NodeAddMixinTest.class);
-            // addTestSuite(NodeCanAddMixinTest.class);
-            // addTestSuite(NodeRemoveMixinTest.class);
+            addTestSuite(NodeAddMixinTest.class);
+            addTestSuite(NodeCanAddMixinTest.class);
+            addTestSuite(NodeRemoveMixinTest.class);
             //
             // addTestSuite(WorkspaceCloneReferenceableTest.class);
             // addTestSuite(WorkspaceCloneSameNameSibsTest.class);

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java	2009-04-07 22:28:53 UTC (rev 812)
@@ -0,0 +1,675 @@
+/*
+ * 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;
+
+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.stub;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.OnParentVersionAction;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+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.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.basic.BasicName;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+public class MixinTest {
+
+    /*
+     * Declares the following node types:
+     * mixinTypeA with child node nodeA and property propertyA
+     * mixinTypeB with child node nodeB and property propertyB
+     * mixinTypeC with child node nodeA and property propertyB
+     * mixinTypeWithAutoCreatedProperty with auto-created property propertyB
+     * mixinTypeWithAutoCreatedChildNode with auto-created child node nodeB
+     * mixinTypeC with child node nodeA and property propertyB
+     * primaryTypeA with child node nodeA and property propertyA
+     */
+    static final Name MIXIN_TYPE_A = new BasicName("", "mixinTypeA");
+    static final Name MIXIN_TYPE_B = new BasicName("", "mixinTypeB");
+    static final Name MIXIN_TYPE_C = new BasicName("", "mixinTypeC");
+    static final Name MIXIN_TYPE_WITH_AUTO_PROP = new BasicName("", "mixinTypeWithAutoCreatedProperty");
+    static final Name MIXIN_TYPE_WITH_AUTO_CHILD = new BasicName("", "mixinTypeWithAutoCreatedChildNode");
+    static final Name PRIMARY_TYPE_A = new BasicName("", "mixinTypeA");
+
+    static final Name PROPERTY_A = new BasicName("", "propertyA");
+    static final Name PROPERTY_B = new BasicName("", "propertyB");
+
+    static final Name CHILD_NODE_A = new BasicName("", "nodeA");
+    static final Name CHILD_NODE_B = new BasicName("", "nodeB");
+
+    private String workspaceName;
+    private ExecutionContext context;
+    private InMemoryRepositorySource source;
+    private JcrWorkspace workspace;
+    private JcrSession session;
+    private Graph graph;
+    private RepositoryConnectionFactory connectionFactory;
+    private RepositoryNodeTypeManager repoTypeManager;
+    private Map<String, Object> sessionAttributes;
+    private Map<JcrRepository.Options, String> options;
+    private NamespaceRegistry registry;
+    @Mock
+    private JcrRepository repository;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        workspaceName = "workspace1";
+        final String repositorySourceName = "repository";
+
+        // Set up the source ...
+        source = new InMemoryRepositorySource();
+        source.setName(workspaceName);
+        source.setDefaultWorkspaceName(workspaceName);
+
+        // Set up the execution context ...
+        context = new ExecutionContext();
+        // Register the test namespace
+        context.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
+
+        // Set up the initial content ...
+        graph = Graph.create(source, context);
+
+        // Make sure the path to the namespaces exists ...
+        graph.create("/jcr:system"); // .and().create("/jcr:system/dna:namespaces");
+        graph.set("jcr:primaryType").on("/jcr:system").to(DnaLexicon.SYSTEM);
+
+        // Stub out the connection factory ...
+        connectionFactory = new RepositoryConnectionFactory() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
+             */
+            @SuppressWarnings( "synthetic-access" )
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                return repositorySourceName.equals(sourceName) ? source.getConnection() : null;
+            }
+        };
+
+        // Stub out the repository, since we only need a few methods ...
+        JcrNodeTypeSource source = null;
+        source = new JcrBuiltinNodeTypeSource(this.context, source);
+        source = new DnaBuiltinNodeTypeSource(this.context, source);
+        source = new TestNodeTypeSource(this.context, source);
+        repoTypeManager = new RepositoryNodeTypeManager(context, source);
+        stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
+        stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
+        stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+
+        // Stub out the repository options ...
+        options = new EnumMap<JcrRepository.Options, String>(JcrRepository.Options.class);
+        options.put(JcrRepository.Options.PROJECT_NODE_TYPES, Boolean.FALSE.toString());
+        stub(repository.getOptions()).toReturn(options);
+
+        // Set up the session attributes ...
+        sessionAttributes = new HashMap<String, Object>();
+
+        // Now create the workspace ...
+        workspace = new JcrWorkspace(repository, workspaceName, context, sessionAttributes);
+
+        // Create the session and log in ...
+        session = (JcrSession)workspace.getSession();
+        registry = session.getExecutionContext().getNamespaceRegistry();
+    }
+
+    @After
+    public void after() throws Exception {
+        if (session != null && session.isLive()) {
+            session.logout();
+        }
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowNullMixinTypeName() throws Exception {
+        graph.create("/a");
+        graph.set("jcr:primaryType").on("/a").to(PRIMARY_TYPE_A);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        nodeA.canAddMixin(null);
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowEmptyMixinTypeName() throws Exception {
+        graph.create("/a");
+        graph.set("jcr:primaryType").on("/a").to(PRIMARY_TYPE_A);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        nodeA.canAddMixin("");
+    }
+
+    @Test( expected = NoSuchNodeTypeException.class )
+    public void shouldNotAllowInvalidMixinTypeName() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        nodeA.canAddMixin("foo");
+    }
+
+    @Test
+    public void shouldNotAllowAddingMixinToProtectedNodes() throws Exception {
+        Node rootNode = session.getRootNode();
+        Node systemNode = rootNode.getNode(JcrLexicon.SYSTEM.getString(registry));
+
+        assertThat(systemNode.getDefinition().isProtected(), is(true));
+        assertThat(systemNode.canAddMixin(JcrMixLexicon.VERSIONABLE.getString(registry)), is(false));
+    }
+
+    @Test
+    public void shouldAllowAddingMixinIfNoConflict() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(JcrMixLexicon.REFERENCEABLE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_B.getString(registry)), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowAddingMixinIfPrimaryTypeConflicts() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(JcrMixLexicon.REFERENCEABLE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_C.getString(registry)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowAddingMixinIfMixinTypeConflicts() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.BASE.getString(registry));
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_C.getString(registry)), is(false));
+    }
+
+    @Test
+    public void shouldAutoCreateAutoCreatedPropertiesOnAddition() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.BASE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry));
+        Property prop = nodeA.getProperty(PROPERTY_B.getString(registry));
+
+        assertThat(prop, is(notNullValue()));
+        assertThat(prop.getLong(), is(10L));
+    }
+
+    @Test
+    public void shouldAutoCreateAutoCreatedChildNodesOnAddition() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.BASE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry));
+        Node childNode = nodeA.getNode(CHILD_NODE_B.getString(registry));
+
+        assertThat(childNode, is(notNullValue()));
+        assertThat(childNode.getPrimaryNodeType().getName(), is(JcrNtLexicon.UNSTRUCTURED.getString(registry)));
+    }
+
+    @Test( expected = ConstraintViolationException.class )
+    public void shouldNotAllowAdditionIfResidualPropertyConflicts() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.setProperty(PROPERTY_B.getString(registry), "Not a boolean");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry)), is(false));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry));
+    }
+
+    @Test
+    public void shouldAllowAdditionIfResidualPropertyDoesNotConflict() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.setProperty(PROPERTY_B.getString(registry), 10L);
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_PROP.getString(registry));
+    }
+
+    @Test( expected = ConstraintViolationException.class )
+    public void shouldNotAllowAdditionIfResidualChildNodeConflicts() throws Exception {
+        graph.create("/a").and().create("/a/" + CHILD_NODE_B);
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a/" + CHILD_NODE_B).to(JcrNtLexicon.BASE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry)), is(false));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry));
+    }
+
+    @Test
+    public void shouldAllowAdditionIfResidualChildNodeDoesNotConflict() throws Exception {
+        graph.create("/a").and().create("/a/" + CHILD_NODE_B);
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a/" + CHILD_NODE_B).to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_WITH_AUTO_CHILD.getString(registry));
+        nodeA.save();
+
+        Node newRootNode = session.getRootNode();
+        Node newNodeA = newRootNode.getNode("a");
+
+        Node newNodeB = newNodeA.getNode(CHILD_NODE_B.getString(registry));
+
+        assertThat(newNodeA.isNodeType(MIXIN_TYPE_WITH_AUTO_CHILD.getLocalName()), is(true));
+        assertThat(newNodeB.isNodeType("nt:unstructured"), is(true));
+        assertThat(newNodeB, is(notNullValue()));
+
+        // Uncomment this to see the problem with the session cache not refreshing the immutable info for /a after a save
+        // assertThat(newNodeB.getDefinition().getDeclaringNodeType().getName(), is(MIXIN_TYPE_B.getString(registry)));
+
+    }
+
+    @Test
+    public void shouldAllowSettingNewPropertyAfterAddingMixin() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(JcrMixLexicon.REFERENCEABLE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_B.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_B.getString(registry));
+
+        nodeA.setProperty(PROPERTY_B.getString(registry), "some string");
+        nodeA.save();
+
+        rootNode = session.getRootNode();
+        nodeA = rootNode.getNode("a");
+        Property propB = nodeA.getProperty(PROPERTY_B.getString(registry));
+
+        assertThat(propB, is(notNullValue()));
+        assertThat(propB.getValue().getString(), is("some string"));
+    }
+
+    @Test
+    public void shouldAllowAddingNewChildNodeAfterAddingMixin() throws Exception {
+        graph.create("/a");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(JcrMixLexicon.REFERENCEABLE.getString(registry));
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.canAddMixin(MIXIN_TYPE_B.getString(registry)), is(true));
+        nodeA.addMixin(MIXIN_TYPE_B.getString(registry));
+
+        nodeA.addNode(CHILD_NODE_B.getString(registry));
+        nodeA.save();
+
+        Node newRootNode = session.getRootNode();
+        Node newNodeA = newRootNode.getNode("a");
+        Node newNodeB = newNodeA.getNode(CHILD_NODE_B.getString(registry));
+
+        assertThat(newNodeB, is(notNullValue()));
+        assertThat(newNodeB.getDefinition().getDeclaringNodeType().getName(), is(MIXIN_TYPE_B.getString(registry)));
+    }
+
+    @Test
+    public void shouldAllowRemovalIfNoConflict() throws Exception {
+        graph.create("/a").and().create("/a/nodeB");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.removeMixin(MIXIN_TYPE_B.getString(registry));
+        nodeA.save();
+
+        rootNode = session.getRootNode();
+        nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.getMixinNodeTypes().length, is(0));
+    }
+
+    @Test
+    public void shouldAllowRemovalIfExistingPropertyWouldHaveDefinition() throws Exception {
+        graph.create("/a").and().create("/a");
+        graph.set(PROPERTY_B).on("/a").to("true");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.removeMixin(MIXIN_TYPE_B.getString(registry));
+        nodeA.save();
+
+        rootNode = session.getRootNode();
+        nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.getMixinNodeTypes().length, is(0));
+    }
+
+    @Test( expected = ConstraintViolationException.class )
+    public void shouldNotAllowRemovalIfExistingPropertyWouldHaveNoDefinition() throws Exception {
+        graph.create("/a").and().create("/a");
+        graph.set(PROPERTY_B).on("/a").to("true");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.removeMixin(MIXIN_TYPE_B.getString(registry));
+        nodeA.save();
+
+        rootNode = session.getRootNode();
+        nodeA = rootNode.getNode("a");
+
+        assertThat(nodeA.getMixinNodeTypes().length, is(0));
+    }
+
+    @Test
+    public void shouldAllowRemovalIfExistingChildNodeWouldHaveDefinition() throws Exception {
+        graph.create("/a").and().create("/a/nodeB");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.removeMixin(MIXIN_TYPE_B.getString(registry));
+    }
+
+    @Test( expected = ConstraintViolationException.class )
+    public void shouldNotAllowRemovalIfExistingChildNodeWouldHaveNoDefinition() throws Exception {
+        graph.create("/a").and().create("/a/nodeB");
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(PRIMARY_TYPE_A);
+        graph.set(JcrLexicon.MIXIN_TYPES.getString(registry)).on("/a").to(MIXIN_TYPE_B);
+
+        Node rootNode = session.getRootNode();
+        Node nodeA = rootNode.getNode("a");
+        nodeA.removeMixin(MIXIN_TYPE_B.getString(registry));
+    }
+
+    class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+        /** The list of primary node types. */
+        private final List<JcrNodeType> nodeTypes;
+
+        TestNodeTypeSource( ExecutionContext context,
+                            JcrNodeTypeSource predecessor ) {
+            super(predecessor);
+
+            nodeTypes = new ArrayList<JcrNodeType>();
+
+            JcrNodeType base = findType(JcrNtLexicon.BASE);
+
+            if (base == null) {
+                String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
+
+            if (unstructured == null) {
+                String baseTypeName = JcrNtLexicon.UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            Value tenValue = new JcrValue(context.getValueFactories(), null, PropertyType.LONG, 10);
+
+            // Stubbing in child node and property definitions for now
+            JcrNodeType mixinTypeA = new JcrNodeType(
+                                                     context,
+                                                     NO_NODE_TYPE_MANAGER,
+                                                     MIXIN_TYPE_A,
+                                                     Arrays.asList(new JcrNodeType[] {base}),
+                                                     NO_PRIMARY_ITEM_NAME,
+                                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                  context,
+                                                                                                                  null,
+                                                                                                                  CHILD_NODE_A,
+                                                                                                                  OnParentVersionAction.IGNORE,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  JcrNtLexicon.BASE,
+                                                                                                                  new JcrNodeType[] {base}),}),
+                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                          context,
+                                                                                                                          null,
+                                                                                                                          PROPERTY_A,
+                                                                                                                          OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          NO_DEFAULT_VALUES,
+                                                                                                                          PropertyType.STRING,
+                                                                                                                          NO_CONSTRAINTS,
+                                                                                                                          false),}),
+                                                     IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType mixinTypeB = new JcrNodeType(
+                                                     context,
+                                                     NO_NODE_TYPE_MANAGER,
+                                                     MIXIN_TYPE_B,
+                                                     Arrays.asList(new JcrNodeType[] {base}),
+                                                     NO_PRIMARY_ITEM_NAME,
+                                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                  context,
+                                                                                                                  null,
+                                                                                                                  CHILD_NODE_B,
+                                                                                                                  OnParentVersionAction.IGNORE,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  JcrNtLexicon.BASE,
+                                                                                                                  new JcrNodeType[] {base}),}),
+                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                          context,
+                                                                                                                          null,
+                                                                                                                          PROPERTY_B,
+                                                                                                                          OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          NO_DEFAULT_VALUES,
+                                                                                                                          PropertyType.BINARY,
+                                                                                                                          NO_CONSTRAINTS,
+                                                                                                                          false),}),
+                                                     IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType mixinTypeC = new JcrNodeType(
+                                                     context,
+                                                     NO_NODE_TYPE_MANAGER,
+                                                     MIXIN_TYPE_C,
+                                                     Arrays.asList(new JcrNodeType[] {base}),
+                                                     NO_PRIMARY_ITEM_NAME,
+                                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                  context,
+                                                                                                                  null,
+                                                                                                                  CHILD_NODE_A,
+                                                                                                                  OnParentVersionAction.IGNORE,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  false,
+                                                                                                                  JcrNtLexicon.BASE,
+                                                                                                                  new JcrNodeType[] {base}),}),
+                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                          context,
+                                                                                                                          null,
+                                                                                                                          PROPERTY_B,
+                                                                                                                          OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          false,
+                                                                                                                          NO_DEFAULT_VALUES,
+                                                                                                                          PropertyType.STRING,
+                                                                                                                          NO_CONSTRAINTS,
+                                                                                                                          false),}),
+                                                     IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType mixinTypeWithAutoChild = new JcrNodeType(
+                                                                 context,
+                                                                 NO_NODE_TYPE_MANAGER,
+                                                                 MIXIN_TYPE_WITH_AUTO_CHILD,
+                                                                 Arrays.asList(new JcrNodeType[] {base}),
+                                                                 NO_PRIMARY_ITEM_NAME,
+                                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                              context,
+                                                                                                                              null,
+                                                                                                                              CHILD_NODE_B,
+                                                                                                                              OnParentVersionAction.IGNORE,
+                                                                                                                              true,
+                                                                                                                              true,
+                                                                                                                              false,
+                                                                                                                              false,
+                                                                                                                              JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                              new JcrNodeType[] {unstructured}),}),
+                                                                 NO_PROPERTIES, IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType mixinTypeWithAutoProperty = new JcrNodeType(
+                                                                    context,
+                                                                    NO_NODE_TYPE_MANAGER,
+                                                                    MIXIN_TYPE_WITH_AUTO_PROP,
+                                                                    Arrays.asList(new JcrNodeType[] {base}),
+                                                                    NO_PRIMARY_ITEM_NAME,
+                                                                    NO_CHILD_NODES,
+                                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                                         context,
+                                                                                                                                         null,
+                                                                                                                                         PROPERTY_B,
+                                                                                                                                         OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                                         true,
+                                                                                                                                         true,
+                                                                                                                                         false,
+                                                                                                                                         new Value[] {tenValue},
+                                                                                                                                         PropertyType.LONG,
+                                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                                         false),}),
+                                                                    IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType primaryTypeA = new JcrNodeType(
+                                                       context,
+                                                       NO_NODE_TYPE_MANAGER,
+                                                       PRIMARY_TYPE_A,
+                                                       Arrays.asList(new JcrNodeType[] {base}),
+                                                       NO_PRIMARY_ITEM_NAME,
+                                                       Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                    context,
+                                                                                                                    null,
+                                                                                                                    CHILD_NODE_A,
+                                                                                                                    OnParentVersionAction.IGNORE,
+                                                                                                                    false,
+                                                                                                                    false,
+                                                                                                                    false,
+                                                                                                                    false,
+                                                                                                                    JcrNtLexicon.BASE,
+                                                                                                                    new JcrNodeType[] {base}),}),
+                                                       Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                            context,
+                                                                                                                            null,
+                                                                                                                            PROPERTY_A,
+                                                                                                                            OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                            false,
+                                                                                                                            false,
+                                                                                                                            false,
+                                                                                                                            NO_DEFAULT_VALUES,
+                                                                                                                            PropertyType.STRING,
+                                                                                                                            NO_CONSTRAINTS,
+                                                                                                                            false),}),
+                                                       NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {mixinTypeA, mixinTypeB, mixinTypeC, mixinTypeWithAutoChild,
+                mixinTypeWithAutoProperty, primaryTypeA,}));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
+        }
+
+    }
+
+}
\ No newline at end of file


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



From dna-commits at lists.jboss.org  Tue Apr  7 23:06:01 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 07 Apr 2009 23:06:01 -0400
Subject: [dna-commits] DNA SVN: r813 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LrO6z-0007Dd-S2@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-07 23:06:01 -0400 (Tue, 07 Apr 2009)
New Revision: 813

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-345 JcrMultiValueProperty.setValues(null) Doesn't Remove Property

Applied the patch, and also uncommented a few more TCK unit tests after additional corrections to the AbstractJcrNode.setProperty implementations.

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-04-07 22:28:53 UTC (rev 812)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-08 03:06:01 UTC (rev 813)
@@ -672,7 +672,8 @@
                 JcrNodeDefinition match = this.cache.nodeTypes().findChildNodeDefinition(mixinCandidateType.getInternalName(),
                                                                                          Collections.<Name>emptyList(),
                                                                                          nodeName,
-                                                                                         childNode.getPrimaryNodeType().getInternalName(),
+                                                                                         childNode.getPrimaryNodeType()
+                                                                                                  .getInternalName(),
                                                                                          snsCount,
                                                                                          false);
 
@@ -996,7 +997,7 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
 
@@ -1024,7 +1025,7 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
@@ -1051,7 +1052,7 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
@@ -1066,7 +1067,7 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value)));
     }
@@ -1082,7 +1083,7 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(type, value)));
     }
@@ -1095,6 +1096,10 @@
     public final Property setProperty( String name,
                                        String[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (values == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values)));
     }
 
@@ -1107,6 +1112,10 @@
                                        String[] values,
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (values == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(type, values)));
     }
 
@@ -1120,17 +1129,16 @@
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
             // If there is an existing property, then remove it ...
-            return removeExistingSingleValuedProperty(name);
+            return removeExistingValuedProperty(name);
         }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), (JcrValue)value));
     }
 
-    protected final Property removeExistingSingleValuedProperty( String name )
+    protected final Property removeExistingValuedProperty( String name )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         PropertyId id = new PropertyId(nodeUuid, nameFrom(name));
         AbstractJcrProperty property = cache.findJcrProperty(id);
         if (property != null) {
-            assert !property.isMultiple();
             property.remove();
             return property;
         }
@@ -1147,6 +1155,10 @@
                                        Value value,
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingValuedProperty(name);
+        }
         return cache.findJcrProperty(editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type)));
     }
 
@@ -1158,7 +1170,29 @@
     public final Property setProperty( String name,
                                        Value[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), values));
+        if (values == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingValuedProperty(name);
+        }
+        int len = values.length;
+        Value[] newValues = null;
+        if (len == 0) {
+            newValues = JcrMultiValueProperty.EMPTY_VALUES;
+        } else {
+            List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
+            for (int i = 0; i != len; ++i) {
+                Value value = values[i];
+                if (value == null) continue;
+                valuesWithDesiredType.add(value);
+            }
+            if (valuesWithDesiredType.isEmpty()) {
+                newValues = JcrMultiValueProperty.EMPTY_VALUES;
+            } else {
+                newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
+            }
+        }
+        // Set the value, perhaps to an empty array ...
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), newValues));
     }
 
     /**
@@ -1170,6 +1204,10 @@
                                        Value[] values,
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (values == null) {
+            // If there is an existing property, then remove it ...
+            return removeExistingValuedProperty(name);
+        }
         int len = values.length;
         Value[] newValues = null;
         if (len == 0) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-07 22:28:53 UTC (rev 812)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-08 03:06:01 UTC (rev 813)
@@ -184,8 +184,14 @@
      */
     public final void setValue( Value[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+
+        if (values == null) {
+            this.remove();
+            return;
+        }
+
         Value[] newValues = null;
-        if (values != null && values.length != 0) {
+        if (values.length != 0) {
             int numValues = values.length;
             List<Value> valuesList = new ArrayList<Value>(numValues);
             ValueFactory<?> factory = null;
@@ -257,8 +263,13 @@
      */
     public final void setValue( String[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        if (values == null) {
+            this.remove();
+            return;
+        }
+
         Value[] jcrValues = null;
-        if (values != null && values.length != 0) {
+        if (values.length != 0) {
             int numValues = values.length;
             List<Value> valuesList = new ArrayList<Value>(numValues);
             jcrValues = new JcrValue[numValues];

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-07 22:28:53 UTC (rev 812)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-08 03:06:01 UTC (rev 813)
@@ -57,8 +57,16 @@
 import org.apache.jackrabbit.test.api.SetValueDoubleTest;
 import org.apache.jackrabbit.test.api.SetValueLongTest;
 import org.apache.jackrabbit.test.api.SetValueReferenceTest;
+import org.apache.jackrabbit.test.api.SetValueStringTest;
 import org.apache.jackrabbit.test.api.SetValueVersionExceptionTest;
 import org.apache.jackrabbit.test.api.ValueFactoryTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -182,7 +190,7 @@
             addTestSuite(SetValueDoubleTest.class);
             addTestSuite(SetValueLongTest.class);
             addTestSuite(SetValueReferenceTest.class);
-            // addTestSuite(SetValueStringTest.class);
+            addTestSuite(SetValueStringTest.class);
             addTestSuite(SetValueConstraintViolationExceptionTest.class);
             // addTestSuite(SetValueValueFormatExceptionTest.class);
             addTestSuite(SetValueVersionExceptionTest.class);
@@ -207,13 +215,13 @@
             addTestSuite(NodeCanAddMixinTest.class);
             addTestSuite(NodeRemoveMixinTest.class);
             //
-            // addTestSuite(WorkspaceCloneReferenceableTest.class);
-            // addTestSuite(WorkspaceCloneSameNameSibsTest.class);
-            // addTestSuite(WorkspaceCloneTest.class);
-            // addTestSuite(WorkspaceCloneVersionableTest.class);
-            // addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
-            // addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
-            // addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
+            addTestSuite(WorkspaceCloneReferenceableTest.class);
+            addTestSuite(WorkspaceCloneSameNameSibsTest.class);
+            addTestSuite(WorkspaceCloneTest.class);
+            addTestSuite(WorkspaceCloneVersionableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
             // addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
             // addTestSuite(WorkspaceCopyReferenceableTest.class);
             // addTestSuite(WorkspaceCopySameNameSibsTest.class);



From dna-commits at lists.jboss.org  Thu Apr  9 16:07:42 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 09 Apr 2009 16:07:42 -0400
Subject: [dna-commits] DNA SVN: r814 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic.
Message-ID: <E1Ls0XG-0007MF-FE@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-09 16:07:42 -0400 (Thu, 09 Apr 2009)
New Revision: 814

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractBinary.java
Log:
Minor change to the 'toString()' method of AbstractBinary, to no longer include in the resulting string the value of the binary (which could be very large).  We're relying upon 'toString()' for a number of error messages (especially in 'dna-jcr').

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractBinary.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractBinary.java	2009-04-08 03:06:01 UTC (rev 813)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractBinary.java	2009-04-09 20:07:42 UTC (rev 814)
@@ -131,8 +131,10 @@
         try {
             acquire();
             StringBuilder sb = new StringBuilder(super.toString());
-            sb.append(" len=").append(getSize()).append("; [");
-            sb.append(Base64.encodeBytes(getBytes()));
+            sb.append("binary[");
+            sb.append(getSize());
+            sb.append("] with hash ");
+            sb.append(Base64.encodeBytes(getHash()));
             return sb.toString();
         } finally {
             release();



From dna-commits at lists.jboss.org  Mon Apr 13 15:13:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 13 Apr 2009 15:13:43 -0400
Subject: [dna-commits] DNA SVN: r815 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LtRbD-0007tl-Nh@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-13 15:13:43 -0400 (Mon, 13 Apr 2009)
New Revision: 815

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-346 JR TCK Tests for SetPropertyStringTest and SetPropertyValueTest Fail

Applied the patch, which fixed a couple of minor defects that were keeping the tests from passing as per Javadoc in section 7.1.5 of the 1.0.1 spec:
- Added code to "compact" value arrays, that is, Node.setProperty(String, Value[]) and Node.setProperty(String, Value[], int) should ignore any null values in the value array. 
- Added code to throw a ValueFormatException if the values in the value arrays in Node.setProperty(String, Value[]) and Node.setProperty(String, Value[], int) are not all of the same type. The spec permits coercing the values into the type expected by the property, but it does not permit different types of values in the array. 
- Added code (AbstractJcrNode.checkCardinalityOfExistingProperty) to throw a ValueFormatException if one attempts to set a multi-valued value onto an existing single-valued array (or vice versa) directly through the node.

A few minor changes were made to the patch, including supplying useful error messages for exceptions, and some minor refactoring of some code to compute the Name object only once.

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-04-09 20:07:42 UTC (rev 814)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-13 19:13:43 UTC (rev 815)
@@ -49,6 +49,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
 import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
@@ -68,11 +69,11 @@
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 
 /**
  * An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
@@ -151,13 +152,16 @@
 
     final JcrValue[] valuesFrom( int propertyType,
                                  Object[] values ) {
+        /*
+         * Null values in the array are "compacted" (read: ignored) as per section 7.1.6 in the JCR 1.0.1 specification. 
+         */
         int len = values.length;
         ValueFactories factories = cache.factories();
-        JcrValue[] results = new JcrValue[values.length];
+        List<JcrValue> results = new ArrayList<JcrValue>(len);
         for (int i = 0; i != len; ++i) {
-            results[i] = new JcrValue(factories, cache, propertyType, values[i]);
+            if (values[i] != null) results.add(new JcrValue(factories, cache, propertyType, values[i]));
         }
-        return results;
+        return results.toArray(new JcrValue[results.size()]);
     }
 
     @Override
@@ -905,7 +909,7 @@
         Path path = null;
         try {
             path = cache.pathFactory().create(relPath);
-        } catch (ValueFormatException e) {
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
         }
         if (path.size() == 0) {
@@ -954,7 +958,7 @@
         if (primaryNodeTypeName != null) {
             try {
                 childPrimaryTypeName = cache.nameFactory().create(primaryNodeTypeName);
-            } catch (ValueFormatException e) {
+            } catch (org.jboss.dna.graph.property.ValueFormatException e) {
                 throw new RepositoryException(JcrI18n.invalidNodeTypeNameParameter.text(primaryNodeTypeName,
                                                                                         "primaryNodeTypeName"));
             }
@@ -976,6 +980,33 @@
     }
 
     /**
+     * Checks whether there is an existing property with this name that does not match the given cardinality. If such a property
+     * exists, a {@code javax.jcr.ValueFormatException} is thrown, as per section 7.1.5 of the JCR 1.0.1 specification.
+     * 
+     * @param propertyName the name of the property
+     * @param isMultiple whether the property must have multiple values
+     * @throws javax.jcr.ValueFormatException if the property exists but has the opposite cardinality
+     * @throws RepositoryException if any other error occurs
+     */
+    private void checkCardinalityOfExistingProperty( Name propertyName,
+                                                     boolean isMultiple )
+        throws javax.jcr.ValueFormatException, RepositoryException {
+        // Check for existing single-valued property - can't set multiple values on single-valued property
+        PropertyInfo propInfo = this.nodeInfo().getProperty(propertyName);
+        if (propInfo != null && propInfo.isMultiValued() != isMultiple) {
+            if (isMultiple) {
+                I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
+                throw new ValueFormatException(msg.text(getPath(),
+                                                        propertyName.getString(cache.namespaces),
+                                                        cache.workspaceName()));
+            }
+            I18n msg = JcrI18n.unableToSetMultiValuedPropertyUsingSingleValue;
+            throw new ValueFormatException(msg.text(getPath(), propertyName, cache.workspaceName()));
+        }
+
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Node#setProperty(java.lang.String, boolean)
@@ -983,7 +1014,9 @@
     public final Property setProperty( String name,
                                        boolean value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.BOOLEAN, value)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.BOOLEAN, value)));
 
     }
 
@@ -999,8 +1032,11 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
 
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
+
     }
 
     /**
@@ -1011,7 +1047,9 @@
     public final Property setProperty( String name,
                                        double value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.DOUBLE, value)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.DOUBLE, value)));
 
     }
 
@@ -1027,7 +1065,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
     }
 
     /**
@@ -1038,8 +1078,9 @@
     public final Property setProperty( String name,
                                        long value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.LONG, value)));
-
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.LONG, value)));
     }
 
     /**
@@ -1054,7 +1095,10 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
+
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
     }
 
     /**
@@ -1069,7 +1113,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.STRING, value)));
     }
 
     /**
@@ -1085,7 +1131,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(type, value)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(type, value)));
     }
 
     /**
@@ -1100,7 +1148,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, true);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valuesFrom(PropertyType.STRING, values)));
     }
 
     /**
@@ -1116,7 +1166,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(type, values)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, true);
+        return cache.findJcrProperty(editor().setProperty(propertyName, valuesFrom(type, values)));
     }
 
     /**
@@ -1131,7 +1183,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), (JcrValue)value));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, (JcrValue)value));
     }
 
     protected final Property removeExistingValuedProperty( String name )
@@ -1159,7 +1213,9 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type)));
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, false);
+        return cache.findJcrProperty(editor().setProperty(propertyName, ((JcrValue)value).asType(type)));
     }
 
     /**
@@ -1180,9 +1236,25 @@
             newValues = JcrMultiValueProperty.EMPTY_VALUES;
         } else {
             List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
+            int expectedType = -1;
             for (int i = 0; i != len; ++i) {
                 Value value = values[i];
                 if (value == null) continue;
+                if (expectedType == -1) {
+                    expectedType = value.getType();
+                } else if (value.getType() != expectedType) {
+                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
+                    StringBuilder sb = new StringBuilder();
+                    sb.append('[');
+                    for (int j = 0; j != values.length; ++j) {
+                        if (j != 0) sb.append(",");
+                        sb.append(values[j].toString());
+                    }
+                    sb.append(']');
+                    String propType = PropertyType.nameFromValue(expectedType);
+                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
+                    throw new ValueFormatException(msg.text(name, values, propType, getPath(), cache.workspaceName()));
+                }
                 valuesWithDesiredType.add(value);
             }
             if (valuesWithDesiredType.isEmpty()) {
@@ -1191,8 +1263,11 @@
                 newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
             }
         }
+
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, true);
         // Set the value, perhaps to an empty array ...
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), newValues));
+        return cache.findJcrProperty(editor().setProperty(propertyName, newValues));
     }
 
     /**
@@ -1208,15 +1283,33 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
+
         int len = values.length;
         Value[] newValues = null;
         if (len == 0) {
             newValues = JcrMultiValueProperty.EMPTY_VALUES;
         } else {
             List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
+            int expectedType = -1;
             for (int i = 0; i != len; ++i) {
                 Value value = values[i];
+
                 if (value == null) continue;
+                if (expectedType == -1) {
+                    expectedType = value.getType();
+                } else if (value.getType() != expectedType) {
+                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
+                    StringBuilder sb = new StringBuilder();
+                    sb.append('[');
+                    for (int j = 0; j != values.length; ++j) {
+                        if (j != 0) sb.append(",");
+                        sb.append(values[j].toString());
+                    }
+                    sb.append(']');
+                    String propType = PropertyType.nameFromValue(expectedType);
+                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
+                    throw new ValueFormatException(msg.text(name, values, propType, getPath(), cache.workspaceName()));
+                }
                 if (value.getType() != type) {
                     value = ((JcrValue)value).asType(type);
                 }
@@ -1228,8 +1321,11 @@
                 newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
             }
         }
+
+        Name propertyName = nameFrom(name);
+        checkCardinalityOfExistingProperty(propertyName, true);
         // Set the value, perhaps to an empty array ...
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), newValues));
+        return cache.findJcrProperty(editor().setProperty(propertyName, newValues));
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-09 20:07:42 UTC (rev 814)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-13 19:13:43 UTC (rev 815)
@@ -80,6 +80,9 @@
     public static I18n unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
     public static I18n unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
     public static I18n unableToSaveNodeThatWasCreatedSincePreviousSave;
+    public static I18n unableToSetMultiValuedPropertyUsingSingleValue;
+    public static I18n unableToSetSingleValuedPropertyUsingMultipleValues;
+    public static I18n allPropertyValuesMustHaveSameType;
 
     public static I18n unableToRemoveRootNode;
     public static I18n unableToMoveNodeToBeChildOfDecendent;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-09 20:07:42 UTC (rev 814)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-13 19:13:43 UTC (rev 815)
@@ -37,6 +37,7 @@
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.version.VersionException;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.ValueFactory;
 
@@ -193,6 +194,7 @@
         Value[] newValues = null;
         if (values.length != 0) {
             int numValues = values.length;
+            int valueType = -1;
             List<Value> valuesList = new ArrayList<Value>(numValues);
             ValueFactory<?> factory = null;
             for (int i = 0; i != numValues; ++i) {
@@ -200,7 +202,24 @@
                 if (value == null) {
                     // skip null values ...
                     continue;
-                } else if (value instanceof JcrValue) {
+                }
+                if (valueType == -1) {
+                    valueType = value.getType();
+                } else if (value.getType() != valueType) {
+                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
+                    StringBuilder sb = new StringBuilder();
+                    sb.append('[');
+                    for (int j = 0; j != values.length; ++j) {
+                        if (j != 0) sb.append(",");
+                        sb.append(values[j].toString());
+                    }
+                    sb.append(']');
+                    String propType = PropertyType.nameFromValue(valueType);
+                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
+                    throw new ValueFormatException(msg.text(getName(), values, propType, getPath(), cache.workspaceName()));
+                }
+
+                if (value instanceof JcrValue) {
                     // just use the value ...
                     valuesList.add(value);
                 } else {

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-09 20:07:42 UTC (rev 814)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-13 19:13:43 UTC (rev 815)
@@ -70,6 +70,9 @@
 unableToCreateNodeWithPrimaryTypeThatDoesNotExist = Unable to create child "{1}" in workspace "{2}" because the node type "{0}" does not exist
 unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition = Unable to create child "{2}" in workspace "{3}" because the node definition "{0}" on the "{1}" node type has no default primary type 
 unableToSaveNodeThatWasCreatedSincePreviousSave = Unable to save node "{0}" in workspace "{1}" because it was created since the last save
+unableToSetMultiValuedPropertyUsingSingleValue = Unable to set existing multi-valued property "{0}" on node "{1}" in workspace "{2}" using single-value setter methods
+unableToSetSingleValuedPropertyUsingMultipleValues = Unable to set existing single-valued property "{0}" on node "{1}" in workspace "{2}" using multi-value setter methods
+allPropertyValuesMustHaveSameType = All values of property "{0}" on node "{3}" in workspace "{4}" must all be {2} values (values were: {1})
 
 unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
 unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}" cannot be moved under a decendant node ("{1}") 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-09 20:07:42 UTC (rev 814)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-13 19:13:43 UTC (rev 815)
@@ -50,6 +50,8 @@
 import org.apache.jackrabbit.test.api.SetPropertyDoubleTest;
 import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
 import org.apache.jackrabbit.test.api.SetPropertyLongTest;
+import org.apache.jackrabbit.test.api.SetPropertyStringTest;
+import org.apache.jackrabbit.test.api.SetPropertyValueTest;
 import org.apache.jackrabbit.test.api.SetValueBinaryTest;
 import org.apache.jackrabbit.test.api.SetValueBooleanTest;
 import org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest;
@@ -183,7 +185,7 @@
             // addTestSuite(NodeUUIDTest.class);
             // addTestSuite(NodeOrderableChildNodesTest.class);
             addTestSuite(PropertyTest.class);
-            //
+
             addTestSuite(SetValueBinaryTest.class);
             addTestSuite(SetValueBooleanTest.class);
             addTestSuite(SetValueDateTest.class);
@@ -201,8 +203,8 @@
             addTestSuite(SetPropertyInputStreamTest.class);
             addTestSuite(SetPropertyLongTest.class);
             // addTestSuite(SetPropertyNodeTest.class);
-            // addTestSuite(SetPropertyStringTest.class);
-            // addTestSuite(SetPropertyValueTest.class);
+            addTestSuite(SetPropertyStringTest.class);
+            addTestSuite(SetPropertyValueTest.class);
             addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
             // addTestSuite(SetPropertyAssumeTypeTest.class);
             //
@@ -210,11 +212,11 @@
             // addTestSuite(NodeItemIsNewTest.class);
             // addTestSuite(PropertyItemIsModifiedTest.class);
             // addTestSuite(PropertyItemIsNewTest.class);
-            //
+
             addTestSuite(NodeAddMixinTest.class);
             addTestSuite(NodeCanAddMixinTest.class);
             addTestSuite(NodeRemoveMixinTest.class);
-            //
+
             addTestSuite(WorkspaceCloneReferenceableTest.class);
             addTestSuite(WorkspaceCloneSameNameSibsTest.class);
             addTestSuite(WorkspaceCloneTest.class);



From dna-commits at lists.jboss.org  Mon Apr 13 15:20:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 13 Apr 2009 15:20:51 -0400
Subject: [dna-commits] DNA SVN: r816 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LtRi7-0002O7-Hn@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-13 15:20:51 -0400 (Mon, 13 Apr 2009)
New Revision: 816

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
Log:
DNA-347 JcrQuery.storeAsNode Is Not Implemented

Applied the patch, which implements the JcrQuery.storeAsNode(...) method.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java	2009-04-13 19:13:43 UTC (rev 815)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java	2009-04-13 19:20:51 UTC (rev 816)
@@ -26,8 +26,9 @@
 import java.util.Arrays;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
-import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryManager;
@@ -64,11 +65,11 @@
      * {@code InvalidQueryException} is thrown. The language must be a string from among those returned by {@code
      * QueryManager#getSupportedQueryLanguages()}; if it is not, then an {@code InvalidQueryException} is thrown.
      * 
-     * @param statement 
+     * @param statement
      * @param language
      * @param storedNode
      * @return A {@code Query} object
-     * @throws InvalidQueryException if statement is invalid or language is unsupported. 
+     * @throws InvalidQueryException if statement is invalid or language is unsupported.
      * @see javax.jcr.query.QueryManager#createQuery(java.lang.String, java.lang.String)
      */
     private Query createQuery( String statement,
@@ -179,8 +180,21 @@
          * 
          * @see javax.jcr.query.Query#storeAsNode(java.lang.String)
          */
-        public Node storeAsNode( java.lang.String absPath ) throws UnsupportedRepositoryOperationException {
-            throw new UnsupportedRepositoryOperationException();
+        public Node storeAsNode( java.lang.String absPath )
+            throws PathNotFoundException, ConstraintViolationException, RepositoryException {
+            NamespaceRegistry namespaces = this.session.namespaces();
+            
+            Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(absPath);
+            Path parentPath = path.getParent();
+
+            Node parentNode = session.getNode(parentPath);
+            Node queryNode = parentNode.addNode(path.relativeTo(parentPath).getString(namespaces),
+                               JcrNtLexicon.QUERY.getString(namespaces));
+            
+            queryNode.setProperty(JcrLexicon.LANGUAGE.getString(namespaces), this.language);
+            queryNode.setProperty(JcrLexicon.STATEMENT.getString(namespaces), this.statement);
+            
+            return queryNode;
         }
 
     }

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-04-13 19:13:43 UTC (rev 815)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-13 19:20:51 UTC (rev 816)
@@ -406,6 +406,7 @@
      * @throws RepositoryException if there is a problem
      */
     Node getNode( Path path ) throws RepositoryException, PathNotFoundException {
+        if (path.isRoot()) return cache.findJcrRootNode();
         return cache.findJcrNode(null, path.relativeTo(rootPath));
     }
 
@@ -452,9 +453,7 @@
         return new ValueFactory() {
 
             public Value createValue( String value,
-                                      int propertyType ) 
-                throws ValueFormatException
-            {
+                                      int propertyType ) throws ValueFormatException {
                 return new JcrValue(valueFactories, sessionCache, propertyType, convertValueToType(value, propertyType));
             }
 
@@ -488,8 +487,9 @@
             public Value createValue( String value ) {
                 return new JcrValue(valueFactories, sessionCache, PropertyType.STRING, value);
             }
-            
-            Object convertValueToType(Object value, int toType) throws ValueFormatException { 
+
+            Object convertValueToType( Object value,
+                                       int toType ) throws ValueFormatException {
                 switch (toType) {
                     case PropertyType.BOOLEAN:
                         try {

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-04-13 19:13:43 UTC (rev 815)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-04-13 19:20:51 UTC (rev 816)
@@ -30,6 +30,9 @@
 import java.util.HashMap;
 import java.util.Map;
 import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.JcrLexicon;
@@ -56,7 +59,7 @@
     @Mock
     private JcrRepository repository;
     private RepositoryNodeTypeManager repoManager;
-    
+
     @Before
     public void beforeEach() throws Exception {
         final String repositorySourceName = "repository";
@@ -94,12 +97,12 @@
 
         // Stub out the repository, since we only need a few methods ...
         MockitoAnnotations.initMocks(this);
-        
+
         JcrNodeTypeSource source = null;
         source = new JcrBuiltinNodeTypeSource(context, source);
         source = new DnaBuiltinNodeTypeSource(context, source);
         repoManager = new RepositoryNodeTypeManager(context, source);
-        
+
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
         stub(repository.getRepositoryTypeManager()).toReturn(repoManager);
         stub(repository.getConnectionFactory()).toReturn(connectionFactory);
@@ -174,6 +177,49 @@
     }
 
     @Test
+    public void shouldCreateQuery() throws Exception {
+        String statement = "Some query syntax";
+
+        QueryManager queryManager = workspace.getQueryManager();
+        Query query = queryManager.createQuery(statement, Query.XPATH);
+
+        assertThat(query, is(notNullValue()));
+        assertThat(query.getLanguage(), is(Query.XPATH));
+        assertThat(query.getStatement(), is(statement));
+    }
+
+    @Test
+    public void shouldStoreQueryAsNode() throws Exception {
+        String statement = "Some query syntax";
+
+        QueryManager queryManager = workspace.getQueryManager();
+        Query query = queryManager.createQuery(statement, Query.XPATH);
+
+        Node node = query.storeAsNode("/storedQuery");
+        assertThat(node, is(notNullValue()));
+        assertThat(node.getPrimaryNodeType().getName(), is("nt:query"));
+        assertThat(node.getProperty("jcr:language").getString(), is(Query.XPATH));
+        assertThat(node.getProperty("jcr:statement").getString(), is(statement));
+    }
+
+    @Test
+    public void shouldLoadStoredQuery() throws Exception {
+        String statement = "Some query syntax";
+
+        QueryManager queryManager = workspace.getQueryManager();
+        Query query = queryManager.createQuery(statement, Query.XPATH);
+
+        Node node = query.storeAsNode("/storedQuery");
+
+        Query loaded = queryManager.getQuery(node);
+
+        assertThat(loaded, is(notNullValue()));
+        assertThat(loaded.getLanguage(), is(Query.XPATH));
+        assertThat(loaded.getStatement(), is(statement));
+        assertThat(loaded.getStoredQueryPath(), is(node.getPath()));
+    }
+
+    @Test
     public void shouldProvideSession() throws Exception {
         assertThat(workspace.getSession(), is(notNullValue()));
     }



From dna-commits at lists.jboss.org  Mon Apr 13 15:23:32 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 13 Apr 2009 15:23:32 -0400
Subject: [dna-commits] DNA SVN: r817 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LtRki-0002WL-2F@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-13 15:23:31 -0400 (Mon, 13 Apr 2009)
New Revision: 817

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
Log:
DNA-348 AbstractJcrNode.checkin and .checkout Throws the Wrong Exception Type

Applied the patch as is, which corrects the exception that is thrown from the methods.

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-04-13 19:20:51 UTC (rev 816)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-13 19:23:31 UTC (rev 817)
@@ -1341,21 +1341,21 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
+     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#checkin()
      */
-    public final Version checkin() {
-        throw new UnsupportedOperationException();
+    public final Version checkin() throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
+     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#checkout()
      */
-    public final void checkout() {
-        throw new UnsupportedOperationException();
+    public final void checkout() throws UnsupportedRepositoryOperationException  {
+        throw new UnsupportedRepositoryOperationException();
     }
 
     /**

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-13 19:20:51 UTC (rev 816)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-13 19:23:31 UTC (rev 817)
@@ -239,12 +239,12 @@
         node.cancelMerge(null);
     }
 
-    @Test( expected = UnsupportedOperationException.class )
+    @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shoudNotAllowCheckin() throws Exception {
         node.checkin();
     }
 
-    @Test( expected = UnsupportedOperationException.class )
+    @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shoudNotAllowCheckout() throws Exception {
         node.checkout();
     }



From dna-commits at lists.jboss.org  Mon Apr 13 15:27:37 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 13 Apr 2009 15:27:37 -0400
Subject: [dna-commits] DNA SVN: r818 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LtRof-00045j-J4@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-13 15:27:37 -0400 (Mon, 13 Apr 2009)
New Revision: 818

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-349 AbstractJcrNode.addNode Does Not Add jcr:uuid Property if Primary Type Extends mix:referenceable

Applied the patch, whic correctly adds the "jcr:uuid" property now.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-13 19:23:31 UTC (rev 817)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-13 19:27:37 UTC (rev 818)
@@ -881,6 +881,27 @@
             Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
             Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
 
+            // Now add the "jcr:uuid" property if and only if referenceable ...
+            if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) {
+                if (desiredUuid == null) {
+                    desiredUuid = UUID.randomUUID();
+                }
+                
+                // We know that this property is single-valued
+                JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, desiredUuid.toString());
+                PropertyDefinition propertyDefinition = nodeTypes().findPropertyDefinition(primaryTypeName,
+                                                                                           Collections.<Name>emptyList(),
+                                                                                           JcrLexicon.UUID,
+                                                                                           value,
+                                                                                           false,
+                                                                                           false);
+                PropertyId propId = new PropertyId(desiredUuid, JcrLexicon.UUID);
+                JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+                org.jboss.dna.graph.property.Property uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
+                PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
+                properties.put(JcrLexicon.UUID, propInfo);
+            }
+
             // Create the property info for the "jcr:primaryType" child property ...
             JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(node.getPrimaryTypeName(),
                                                                               node.getMixinTypeNames(),



From dna-commits at lists.jboss.org  Mon Apr 13 15:29:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 13 Apr 2009 15:29:46 -0400
Subject: [dna-commits] DNA SVN: r819 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LtRqk-0004Cu-QI@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-13 15:29:46 -0400 (Mon, 13 Apr 2009)
New Revision: 819

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
Log:
DNA-350 RepoistoryNodeTypeManager.findBest*Definition Returns Wrong Definition for Protected Items if skipProtected is True.

Applied the patch.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-13 19:27:37 UTC (rev 818)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-13 19:29:46 UTC (rev 819)
@@ -186,7 +186,7 @@
         if (primaryType != null) {
             for (JcrPropertyDefinition definition : primaryType.allSingleValuePropertyDefinitions(propertyName)) {
                 // See if the definition allows the value ...
-                if (skipProtected && definition.isProtected()) continue;
+                if (skipProtected && definition.isProtected()) return null;
                 if (setToEmpty) {
                     if (!definition.isMandatory()) return definition;
                     // Otherwise this definition doesn't work, so continue with the next ...
@@ -209,7 +209,7 @@
                 mixinTypes.add(mixinType);
                 for (JcrPropertyDefinition definition : mixinType.allSingleValuePropertyDefinitions(propertyName)) {
                     // See if the definition allows the value ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     if (setToEmpty) {
                         if (!definition.isMandatory()) return definition;
                         // Otherwise this definition doesn't work, so continue with the next ...
@@ -228,7 +228,7 @@
             if (primaryType != null) {
                 for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                     // See if the definition allows the value ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     if (setToEmpty) {
                         if (!definition.isMandatory()) return definition;
                         // Otherwise this definition doesn't work, so continue with the next ...
@@ -246,7 +246,7 @@
                 for (JcrNodeType mixinType : mixinTypes) {
                     for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                         // See if the definition allows the value ...
-                        if (skipProtected && definition.isProtected()) continue;
+                        if (skipProtected && definition.isProtected()) return null;
                         if (setToEmpty) {
                             if (!definition.isMandatory()) return definition;
                             // Otherwise this definition doesn't work, so continue with the next ...
@@ -269,7 +269,7 @@
             if (primaryType != null) {
                 for (JcrPropertyDefinition definition : primaryType.allSingleValuePropertyDefinitions(propertyName)) {
                     // See if the definition allows the value ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     assert definition.getRequiredType() != PropertyType.UNDEFINED;
                     if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
                 }
@@ -280,7 +280,7 @@
                 for (JcrNodeType mixinType : mixinTypes) {
                     for (JcrPropertyDefinition definition : mixinType.allSingleValuePropertyDefinitions(propertyName)) {
                         // See if the definition allows the value ...
-                        if (skipProtected && definition.isProtected()) continue;
+                        if (skipProtected && definition.isProtected()) return null;
                         assert definition.getRequiredType() != PropertyType.UNDEFINED;
                         if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
                     }
@@ -292,7 +292,7 @@
                 if (primaryType != null) {
                     for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                         // See if the definition allows the value ...
-                        if (skipProtected && definition.isProtected()) continue;
+                        if (skipProtected && definition.isProtected()) return null;
                         assert definition.getRequiredType() != PropertyType.UNDEFINED;
                         if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
                     }
@@ -303,7 +303,7 @@
                     for (JcrNodeType mixinType : mixinTypes) {
                         for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                             // See if the definition allows the value ...
-                            if (skipProtected && definition.isProtected()) continue;
+                            if (skipProtected && definition.isProtected()) return null;
                             assert definition.getRequiredType() != PropertyType.UNDEFINED;
                             if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
                         }
@@ -378,7 +378,7 @@
         if (primaryType != null) {
             for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                 // See if the definition allows the value ...
-                if (skipProtected && definition.isProtected()) continue;
+                if (skipProtected && definition.isProtected()) return null;
                 if (setToEmpty) {
                     if (!definition.isMandatory()) return definition;
                     // Otherwise this definition doesn't work, so continue with the next ...
@@ -402,7 +402,7 @@
                 mixinTypes.add(mixinType);
                 for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                     // See if the definition allows the value ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     if (setToEmpty) {
                         if (!definition.isMandatory()) return definition;
                         // Otherwise this definition doesn't work, so continue with the next ...
@@ -425,7 +425,7 @@
             if (primaryType != null) {
                 for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                     // See if the definition allows the value ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     assert definition.getRequiredType() != PropertyType.UNDEFINED;
                     if (definition.canCastToTypeAndSatisfyConstraints(values)) return definition;
                 }
@@ -436,7 +436,7 @@
                 for (JcrNodeType mixinType : mixinTypes) {
                     for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                         // See if the definition allows the value ...
-                        if (skipProtected && definition.isProtected()) continue;
+                        if (skipProtected && definition.isProtected()) return null;
                         assert definition.getRequiredType() != PropertyType.UNDEFINED;
                         if (definition.canCastToTypeAndSatisfyConstraints(values)) return definition;
                     }
@@ -535,7 +535,7 @@
         if (primaryType != null) {
             for (JcrNodeDefinition definition : primaryType.allChildNodeDefinitions(childName, requireSns)) {
                 // Skip protected definitions ...
-                if (skipProtected && definition.isProtected()) continue;
+                if (skipProtected && definition.isProtected()) return null;
                 // See if the definition allows a child with the supplied primary type ...
                 if (definition.allowsChildWithType(childType)) return definition;
             }
@@ -548,7 +548,7 @@
                 if (mixinType == null) continue;
                 for (JcrNodeDefinition definition : mixinType.allChildNodeDefinitions(childName, requireSns)) {
                     // Skip protected definitions ...
-                    if (skipProtected && definition.isProtected()) continue;
+                    if (skipProtected && definition.isProtected()) return null;
                     // See if the definition allows a child with the supplied primary type ...
                     if (definition.allowsChildWithType(childType)) return definition;
                 }



From dna-commits at lists.jboss.org  Tue Apr 14 12:13:03 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 14 Apr 2009 12:13:03 -0400
Subject: [dna-commits] DNA SVN: r820 - in trunk/dna-jcr/src/main:
	resources/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LtlFv-00050M-Jg@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-14 12:13:03 -0400 (Tue, 14 Apr 2009)
New Revision: 820

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.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/NodeDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
Log:
DNA-339 JCR Type Registration and Unregistration

Applying the patch, with some very minor modifications (particularly, some additional JavaDoc in NodeDefinitionId and PropertyDefinitionId).  The Key classes in JcrPropertyDefinition and JcrNodeDefinition should be replaced with PropertyDefinitionId and NodeDefinitionId, respectively, but this can be done in another change set.  The current approach works, but may actually not match the specification.  (The JavaDoc added to the *DefinitionId classes helps to explain what information exactly is used to distinguish the definitions, and the Key classes use a different set of information.)

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -76,6 +76,7 @@
     public static I18n errorWhileFindingNodeWithUuid;
     public static I18n errorWhileFindingNodeWithPath;
     public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
+    public static I18n noSnsDefinitionForNode;
     public static I18n missingNodeTypeForExistingNode;
     public static I18n unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
     public static I18n unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
@@ -109,6 +110,23 @@
     public static I18n notStoredQuery;
     public static I18n invalidQueryLanguage;
 
+    // Type registration messages
+    public static I18n invalidNodeTypeName;
+    public static I18n nodeTypeAlreadyExists;
+    public static I18n invalidPrimaryTypeName;
+    public static I18n invalidSupertypeName;
+    public static I18n supertypesConflict;
+    public static I18n ambiguousPrimaryItemName;
+    public static I18n invalidPrimaryItemName;
+    public static I18n autocreatedNodesNeedDefaults;
+    public static I18n residualDefinitionsCannotBeMandatory;
+    public static I18n cannotOverrideProtectedDefinition;
+    public static I18n cannotMakeMandatoryDefinitionOptional;
+    public static I18n constraintsChangedInSubtype;
+    public static I18n cannotRedefineProperty;
+    public static I18n autocreatedPropertyNeedsDefault;
+    public static I18n singleValuedPropertyNeedsSingleValuedDefault;
+    
     static {
         try {
             I18n.initialize(JcrI18n.class);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -27,11 +27,15 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
 
 /**
  * DNA implementation of the {@link NodeDefinition} class.
@@ -218,4 +222,73 @@
         return getId().toString();
     }
 
+    Key getKey(boolean includeTypes) {
+        return new Key(this, includeTypes);
+    }
+
+    /**
+     * Internal class that encapsulates composite unique identifier for child node definitions
+     */
+    class Key {
+        String keyString;
+
+        Key( Node node,
+             NamespaceRegistry registry ) throws Exception {
+            StringBuffer buff = new StringBuffer();
+
+            try {
+                String nodeName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+                buff.append(nodeName);
+            } catch (PathNotFoundException pnfe) {
+                // Ignorable, means name is not set.
+                buff.append(JcrNodeType.RESIDUAL_ITEM_NAME);
+            }
+
+            try {
+                Value[] requiredTypes = node.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES.getString(registry)).getValues();
+
+                for (int i = 0; i < requiredTypes.length; i++) {
+                    buff.append('_').append(requiredTypes[i].getString());
+                }
+            } catch (PathNotFoundException pnfe) {
+                // No required types. Weird, but not debilitating.
+            }
+
+            buff.append('_').append(node.getProperty(JcrLexicon.SAME_NAME_SIBLINGS.getString(registry)).getBoolean());
+
+            this.keyString = buff.toString();
+        }
+
+        Key( NodeDefinition def,
+             boolean includeTypes ) {
+            StringBuffer buff = new StringBuffer();
+            buff.append(def.getName());
+
+            if (includeTypes) {
+                NodeType[] requiredTypes = def.getRequiredPrimaryTypes();
+                for (int i = 0; i < requiredTypes.length; i++) {
+                    buff.append('_').append(requiredTypes[i].getName());
+                }
+            }
+
+            buff.append('_').append(def.allowsSameNameSiblings());
+
+            this.keyString = buff.toString();
+        }
+
+        @Override
+        public boolean equals( Object ob ) {
+            if (!(ob instanceof Key)) {
+                return false;
+            }
+
+            return keyString.equals(((Key)ob).keyString);
+        }
+
+        @Override
+        public int hashCode() {
+            return keyString.hashCode();
+        }
+    }
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -34,7 +34,6 @@
 import java.util.Set;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
-import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.Immutable;
@@ -334,7 +333,7 @@
      * 
      * @see javax.jcr.nodetype.NodeType#getDeclaredChildNodeDefinitions()
      */
-    public NodeDefinition[] getDeclaredChildNodeDefinitions() {
+    public JcrNodeDefinition[] getDeclaredChildNodeDefinitions() {
         // Always have to make a copy to prevent changes ...
         return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
     }
@@ -344,10 +343,10 @@
      * 
      * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions()
      */
-    public NodeDefinition[] getChildNodeDefinitions() {
+    public JcrNodeDefinition[] getChildNodeDefinitions() {
         // Always have to make a copy to prevent changes ...
         Collection<JcrNodeDefinition> definitions = this.allDefinitions.allChildNodeDefinitions();
-        return definitions.toArray(new NodeDefinition[definitions.size()]);
+        return definitions.toArray(new JcrNodeDefinition[definitions.size()]);
     }
 
     /**
@@ -355,10 +354,10 @@
      * 
      * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions()
      */
-    public PropertyDefinition[] getPropertyDefinitions() {
+    public JcrPropertyDefinition[] getPropertyDefinitions() {
         // Always have to make a copy to prevent changes ...
         Collection<JcrPropertyDefinition> definitions = this.allDefinitions.allPropertyDefinitions();
-        return definitions.toArray(new PropertyDefinition[definitions.size()]);
+        return definitions.toArray(new JcrPropertyDefinition[definitions.size()]);
     }
 
     /**
@@ -366,9 +365,9 @@
      * 
      * @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes()
      */
-    public NodeType[] getDeclaredSupertypes() {
+    public JcrNodeType[] getDeclaredSupertypes() {
         // Always have to make a copy to prevent changes ...
-        return declaredSupertypes.toArray(new NodeType[declaredSupertypes.size()]);
+        return declaredSupertypes.toArray(new JcrNodeType[declaredSupertypes.size()]);
     }
 
     /**
@@ -382,16 +381,26 @@
     }
 
     /**
-     * Returns the internal {@link Name} object for the note type. This method exists outside the JCR API and should not be
+     * Returns the internal {@link Name} object for the node type. This method exists outside the JCR API and should not be
      * exposed outside of the package.
      * 
-     * @return the internal {@link Name} object for the note type.
+     * @return the internal {@link Name} object for the node type.
      */
     Name getInternalName() {
         return name;
     }
 
     /**
+     * Returns the internal {@link Name} object for the primary item of this node type. This method exists outside the JCR API and
+     * should not be exposed outside of the package.
+     * 
+     * @return the internal {@link Name} object for the primary item of this node type.
+     */
+    Name getInternalPrimaryItemName() {
+        return primaryItemName;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#getPrimaryItemName()
@@ -410,7 +419,7 @@
      * 
      * @see javax.jcr.nodetype.NodeType#getDeclaredPropertyDefinitions()
      */
-    public PropertyDefinition[] getDeclaredPropertyDefinitions() {
+    public JcrPropertyDefinition[] getDeclaredPropertyDefinitions() {
         return propertyDefinitions.toArray(new JcrPropertyDefinition[propertyDefinitions.size()]);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -26,6 +26,7 @@
 import java.util.UUID;
 import java.util.regex.Pattern;
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
@@ -36,6 +37,7 @@
 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.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactories;
@@ -139,6 +141,20 @@
     }
 
     /**
+     * Creates a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+     * <code>context</code>. Provided to support immutable pattern for this class.
+     * 
+     * @param context the {@link ExecutionContext} for the new <code>JcrPropertyDefinition</code>
+     * @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+     *         <code>context</code>.
+     */
+    JcrPropertyDefinition with( ExecutionContext context) {
+        return new JcrPropertyDefinition(context, this.declaringNodeType, this.name, this.getOnParentVersion(),
+                                         this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
+                                         this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Object#toString()
@@ -673,4 +689,51 @@
             return false;
         }
     }
+
+    Key getKey(boolean includeType) {
+        return new Key(this, includeType);
+    }
+
+    /**
+     * Internal class that encapsulates composite unique identifier for property definitions
+     */
+    class Key {
+        String keyString;
+
+        Key( Node node,
+             NamespaceRegistry registry ) throws Exception {
+            String propertyName = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+            try {
+                propertyName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+            } catch (PathNotFoundException pnfe) {
+                // Ignorable, means name is not set.
+            }
+            String requiredType = node.getProperty(JcrLexicon.REQUIRED_TYPE.getString(registry)).getString();
+            boolean allowsMultiple = node.getProperty(JcrLexicon.MULTIPLE.getString(registry)).getBoolean();
+
+            this.keyString = propertyName + "-" + requiredType + "-" + allowsMultiple;
+        }
+
+        Key( PropertyDefinition def, boolean includeType ) {
+            String requiredType = PropertyType.nameFromValue(def.getRequiredType());
+
+            this.keyString = def.getName() + (includeType ? "-" + requiredType : "")  + "-" + def.isMultiple();
+        }
+
+        @Override
+        public boolean equals( Object ob ) {
+            if (!(ob instanceof Key)) {
+                return false;
+            }
+
+            return keyString.equals(((Key)ob).keyString);
+        }
+
+        @Override
+        public int hashCode() {
+            return keyString.hashCode();
+        }
+    }
+
 }

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-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -166,6 +166,10 @@
         return this.executionContext.getNamespaceRegistry();
     }
 
+    JcrWorkspace workspace() {
+        return this.workspace;
+    }
+
     /**
      * {@inheritDoc}
      * 

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-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -175,6 +175,10 @@
         return this.nodeTypeManager;
     }
 
+    final ExecutionContext context() {
+        return this.context;
+    }
+
     /**
      * {@inheritDoc}
      */

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -24,6 +24,7 @@
 package org.jboss.dna.jcr;
 
 import java.io.Serializable;
+import java.util.HashMap;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
@@ -34,6 +35,19 @@
  * within the graph as {@link #getString() string values} on a property. These string values can later be
  * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
  * use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"Similarly, a node type may have two or more child node definitions with identical name attributes as long as they are
+ * distinguishable by the required primary types attribute (the value returned by
+ * NodeDefinition.getRequiredPrimaryTypes)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
  */
 @Immutable
 public final class NodeDefinitionId implements Serializable {
@@ -51,7 +65,10 @@
     private final Name nodeTypeName;
     private final Name childDefinitionName;
     private final Name[] requiredPrimaryTypes;
-    private final String stringVersion;
+    /**
+     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+     */
+    private final String stringRepresentation;
 
     /**
      * Create an identifier for a node definition.
@@ -75,7 +92,7 @@
             sb.append('/');
             sb.append(requiredPrimaryType.getString());
         }
-        this.stringVersion = sb.toString();
+        this.stringRepresentation = sb.toString();
     }
 
     /**
@@ -121,7 +138,7 @@
      * @return the string form
      */
     public String getString() {
-        return this.stringVersion;
+        return this.stringRepresentation;
     }
 
     /**
@@ -153,7 +170,7 @@
      */
     @Override
     public int hashCode() {
-        return stringVersion.hashCode();
+        return stringRepresentation.hashCode();
     }
 
     /**
@@ -166,7 +183,7 @@
         if (obj == this) return true;
         if (obj instanceof NodeDefinitionId) {
             NodeDefinitionId that = (NodeDefinitionId)obj;
-            return this.stringVersion.equals(that.stringVersion);
+            return this.stringRepresentation.equals(that.stringRepresentation);
         }
         return false;
     }
@@ -178,7 +195,7 @@
      */
     @Override
     public String toString() {
-        return this.stringVersion;
+        return this.stringRepresentation;
     }
 
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -24,6 +24,7 @@
 package org.jboss.dna.jcr;
 
 import java.io.Serializable;
+import java.util.HashMap;
 import javax.jcr.PropertyType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.property.Name;
@@ -35,6 +36,20 @@
  * stored within the graph as {@link #getString() string values} on a property. These string values can later be
  * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
  * use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
+ * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
+ * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
+ * PropertyDefinition.isMultiple)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
  */
 @Immutable
 public final class PropertyDefinitionId implements Serializable {
@@ -53,7 +68,10 @@
     private final Name propertyDefinitionName;
     private final int propertyType;
     private final boolean allowsMultiple;
-    private final String stringVersion;
+    /**
+     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+     */
+    private final String stringRepresentation;
 
     /**
      * Create a new identifier for a propety definition.
@@ -73,8 +91,8 @@
         this.propertyDefinitionName = propertyDefinitionName;
         this.propertyType = propertyType;
         this.allowsMultiple = allowsMultiple;
-        this.stringVersion = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
-                             + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
+        this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
+                                    + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
     }
 
     /**
@@ -129,7 +147,7 @@
      * @return the string form
      */
     public String getString() {
-        return this.stringVersion;
+        return this.stringRepresentation;
     }
 
     /**
@@ -167,7 +185,7 @@
      */
     @Override
     public int hashCode() {
-        return this.stringVersion.hashCode();
+        return this.stringRepresentation.hashCode();
     }
 
     /**
@@ -180,7 +198,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyDefinitionId) {
             PropertyDefinitionId that = (PropertyDefinitionId)obj;
-            return this.stringVersion.equals(that.stringVersion);
+            return this.stringRepresentation.equals(that.stringRepresentation);
         }
         return false;
     }
@@ -192,7 +210,7 @@
      */
     @Override
     public String toString() {
-        return this.stringVersion;
+        return this.stringRepresentation;
     }
 
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-14 16:13:03 UTC (rev 820)
@@ -24,7 +24,9 @@
 package org.jboss.dna.jcr;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -32,6 +34,7 @@
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
@@ -47,6 +50,7 @@
 import org.jboss.dna.graph.property.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.jcr.JcrNodeTypeSource;
 
 /**
  * The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
@@ -70,6 +74,27 @@
     private final PropertyFactory propertyFactory;
     private final PathFactory pathFactory;
 
+    /**
+     * List of ways to filter the returned property definitions
+     * 
+     * @see RepositoryNodeTypeManager#findPropertyDefinitions(List, Name, PropertyCardinality, List)
+     */
+    private enum PropertyCardinality {
+        SINGLE_VALUED_ONLY,
+        MULTI_VALUED_ONLY,
+        ANY
+    }
+
+    /**
+     * List of ways to filter the returned node definitions
+     * 
+     * @see RepositoryNodeTypeManager#findChildNodeDefinitions(List, Name, NodeCardinality, List)
+     */
+    private enum NodeCardinality {
+        NO_SAME_NAME_SIBLINGS,
+        SAME_NAME_SIBLINGS
+    }
+
     RepositoryNodeTypeManager( ExecutionContext context,
                                JcrNodeTypeSource source ) {
         this.context = context;
@@ -454,6 +479,52 @@
     }
 
     /**
+     * Searches the supplied primary and mixin node types for all valid property definitions that match the given property name
+     * and cardinality.
+     * <p>
+     * If no satisfactory property definition could be found, this method returns an empty list.
+     * </p>
+     * 
+     * @param typeNamesToCheck the name of the types to check; may not be null
+     * @param propertyName the name of the property for which the definitions should be retrieved
+     * @param typeToCheck the type of definitions to consider (single-valued only, multi-valued only, or all)
+     * @param pendingTypes a list of types that have been created during type registration but not yet registered in the type map
+     * @return a list of all valid property definitions that match the given property name and cardinality
+     */
+    private List<JcrPropertyDefinition> findPropertyDefinitions( List<Name> typeNamesToCheck,
+                                                                 Name propertyName,
+                                                                 PropertyCardinality typeToCheck,
+                                                                 List<JcrNodeType> pendingTypes ) {
+        assert typeNamesToCheck != null;
+        Collection<JcrPropertyDefinition> propDefs = null;
+        List<JcrPropertyDefinition> matchingDefs = new ArrayList<JcrPropertyDefinition>();
+
+        // Look for a single-value property definition on the mixin types that matches by name and type ...
+        for (Name typeNameToCheck : typeNamesToCheck) {
+            JcrNodeType typeName = findTypeInMapOrList(typeNameToCheck, pendingTypes);
+            if (typeName == null) continue;
+
+            switch (typeToCheck) {
+                case SINGLE_VALUED_ONLY:
+                    propDefs = typeName.allSingleValuePropertyDefinitions(propertyName);
+                    break;
+                case MULTI_VALUED_ONLY:
+                    propDefs = typeName.allMultiValuePropertyDefinitions(propertyName);
+                    break;
+                case ANY:
+                    propDefs = typeName.allPropertyDefinitions(propertyName);
+                    break;
+                default:
+                    throw new IllegalStateException("Should be unreachable: " + typeToCheck);
+            }
+
+            matchingDefs.addAll(propDefs);
+        }
+
+        return matchingDefs;
+    }
+
+    /**
      * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied
      * name to be removed.
      * 
@@ -566,6 +637,50 @@
     }
 
     /**
+     * Searches the supplied primary and mixin node types for all valid child node definitions that match the given child node
+     * name and cardinality.
+     * <p>
+     * If no satisfactory child node definition could be found, this method returns an empty list.
+     * </p>
+     * 
+     * @param typeNamesToCheck the name of the types to check; may not be null
+     * @param childNodeName the name of the child node for which the definitions should be retrieved
+     * @param typesToCheck the type of definitions to consider (allows SNS or does not allow SNS)
+     * @param pendingTypes a list of types that have been created during type registration but not yet registered in the type map
+     * @return a list of all valid chlid node definitions that match the given child node name and cardinality
+     */
+    private List<JcrNodeDefinition> findChildNodeDefinitions( List<Name> typeNamesToCheck,
+                                                              Name childNodeName,
+                                                              NodeCardinality typesToCheck,
+                                                              List<JcrNodeType> pendingTypes ) {
+        assert typeNamesToCheck != null;
+        Collection<JcrNodeDefinition> nodeDefs = null;
+        List<JcrNodeDefinition> matchingDefs = new ArrayList<JcrNodeDefinition>();
+
+        for (Name typeNameToCheck : typeNamesToCheck) {
+            JcrNodeType typeName = findTypeInMapOrList(typeNameToCheck, pendingTypes);
+            if (typeName == null) continue;
+
+            switch (typesToCheck) {
+                case NO_SAME_NAME_SIBLINGS:
+                    nodeDefs = typeName.allChildNodeDefinitions(childNodeName);
+                    break;
+                case SAME_NAME_SIBLINGS:
+                    nodeDefs = typeName.allChildNodeDefinitions(childNodeName, true);
+                    break;
+            }
+
+            assert nodeDefs != null;
+            for (JcrNodeDefinition definition : nodeDefs) {
+                if (NodeCardinality.NO_SAME_NAME_SIBLINGS == typesToCheck && definition.allowsSameNameSiblings()) continue;
+                matchingDefs.add(definition);
+            }
+        }
+
+        return matchingDefs;
+    }
+
+    /**
      * Determine if the child node definitions of the supplied primary type and mixin types of a parent node allow all of the
      * children with the supplied name to be removed.
      * 
@@ -819,4 +934,531 @@
 
         batch.create(nodeDefPath).with(propsList).and();
     }
+
+    /**
+     * Registers the node types from the given {@link JcrNodeTypeSource}.
+     * <p>
+     * The effect of this method is &quot;all or nothing&quot;; if an error occurs, no node types are registered or updated.
+     * </p>
+     * <p>
+     * <b>DNA Implementation Notes</b>
+     * </p>
+     * <p>
+     * DNA currently supports registration of batches of types with some constraints. DNA will allow types to be registered if
+     * they meet the following criteria:
+     * <ol>
+     * <li>Existing types cannot be modified in-place - They must be unregistered and re-registered</li>
+     * <li>Types must have a non-null, non-empty name</li>
+     * <li>If a primary item name is specified for the node type, it must match the name of a property OR a child node, not both</li>
+     * <li>Each type must have a valid set of supertypes - that is, the type's supertypes must meet the following criteria:
+     * <ol>
+     * <li>The type must have at least one supertype (unless the type is {@code nt:base}.</li>
+     * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a property ({@code p1} and {@code p2}) with the
+     * same name and cardinality ({@code p1.isMultiple() == p2.isMultiple()}). Note that this does prohibit each {@code t1} and
+     * {@code t2} from having a common supertype (or super-supertype, etc.) that declares a property).</li>
+     * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a child node ({@code n1} and {@code n2}) with the
+     * same name and SNS status ({@code p1.allowsSameNameSiblings() == p2.allowsSameNameSiblings()}). Note that this does prohibit
+     * each {@code t1} and {@code t2} from having a common supertype (or super-supertype, etc.) that declares a child node).</li>
+     * </ol>
+     * </li>
+     * <li>Each type must have a valid set of properties - that is, the type's properties must meet the following criteria:
+     * <ol>
+     * <li>Residual property definitions cannot be mandatory</li>
+     * <li>If the property is auto-created, it must specify a default value</li>
+     * <li>If the property is single-valued, it can only specify a single default value</li>
+     * <li>If the property overrides an existing property definition from a supertype, the new definition must be mandatory if the
+     * old definition was mandatory</li>
+     * <li>The property cannot override an existing property definition from a supertype if the ancestor definition is protected</li>
+     * <li>If the property overrides an existing property definition from a supertype that specifies value constraints, the new
+     * definition must have the same value constraints as the old definition. <i>This requirement may be relaxed in a future
+     * version of DNA.</i></li>
+     * <li>If the property overrides an existing property definition from a supertype, the new definition must have the same
+     * required type as the old definition or a required type that can ALWAYS be cast to the required type of the ancestor (see
+     * section 6.2.6 of the JCR 1.0.1 specification)</li>
+     * </ol>
+     * Note that an empty set of properties would meet the above criteria.</li>
+     * <li>The type must have a valid set of child nodes - that is, the types's child nodes must meet the following criteria:
+     * <ol>
+     * <li>Residual child node definitions cannot be mandatory</li>
+     * <li>If the child node is auto-created, it must specify a default primary type name</li>
+     * <li>All required primary types must already be fully registered with the type manager or must have been defined earlier in
+     * the batch. <i>This requirement may be relaxed in a future version of DNA.</i></li>
+     * <li>If the child node overrides an existing child node definition from a supertype, the new definition must be mandatory if
+     * the old definition was mandatory</li>
+     * <li>The child node cannot override an existing child node definition from a supertype if the ancestor definition is
+     * protected</li>
+     * <li>If the child node overrides an existing child node definition from a supertype, the required primary types of the new
+     * definition must be more restrictive than the required primary types of the old definition - that is, the new primary types
+     * must defined such that any type that satisfies all of the required primary types for the new definition must also satisfy
+     * all of the required primary types for the old definition. This requirement is analogous to the requirement that overriding
+     * property definitions have a required type that is always convertible to the required type of the overridden definition.</li>
+     * </ol>
+     * Note that an empty set of child nodes would meet the above criteria.</li>
+     * </p>
+     * 
+     * @param nodeTypeSource the batch of {@link NodeType node types} to register
+     * @return the newly registered (or updated) {@link NodeType NodeTypes}
+     * @throws RepositoryException if any of the node types in the the {@link JcrNodeTypeSource} are invalid
+     * @throws RepositoryException if another error occurs
+     */
+    List<JcrNodeType> registerNodeTypes( JcrNodeTypeSource nodeTypeSource ) throws RepositoryException {
+
+        assert nodeTypeSource != null;
+        Collection<JcrNodeType> nodeTypeBatch = nodeTypeSource.getNodeTypes();
+
+        List<JcrNodeType> typesPendingRegistration = new ArrayList<JcrNodeType>(nodeTypeBatch.size());
+
+        for (JcrNodeType nodeType : nodeTypeBatch) {
+            if (nodeType.getInternalName() == null || nodeType.getName().length() == 0) {
+                throw new RepositoryException(JcrI18n.invalidNodeTypeName.text());
+            }
+
+            Name name = nodeType.getInternalName();
+
+            if (nodeTypes.containsKey(name)) {
+                throw new RepositoryException(JcrI18n.nodeTypeAlreadyExists.text(nodeType.getName()));
+            }
+
+            List<JcrNodeType> supertypes = supertypesFor(nodeType, typesPendingRegistration);
+
+            validate(nodeType, supertypes, typesPendingRegistration);
+
+            List<JcrPropertyDefinition> propertyDefs = new ArrayList<JcrPropertyDefinition>(
+                                                                                            nodeType.getDeclaredPropertyDefinitions().length);
+
+            for (JcrPropertyDefinition propertyDef : nodeType.getDeclaredPropertyDefinitions()) {
+                propertyDefs.add(propertyDef.with(this.context));
+            }
+
+            List<JcrNodeDefinition> nodeDefs = new ArrayList<JcrNodeDefinition>(nodeType.getDeclaredChildNodeDefinitions().length);
+
+            for (JcrNodeDefinition nodeDef : nodeType.getDeclaredChildNodeDefinitions()) {
+                JcrNodeType[] requiredPrimaryTypes = new JcrNodeType[nodeDef.getRequiredPrimaryTypeNames().size()];
+
+                int i = 0;
+                for (Name primaryTypeName : nodeDef.getRequiredPrimaryTypeNames()) {
+                    requiredPrimaryTypes[i] = findTypeInMapOrList(primaryTypeName, typesPendingRegistration);
+
+                    if (requiredPrimaryTypes[i] == null) {
+                        throw new RepositoryException(JcrI18n.invalidPrimaryTypeName.text(primaryTypeName, nodeType.getName()));
+                    }
+                    i++;
+                }
+
+                nodeDefs.add(nodeDef.with(this.context));
+            }
+
+            JcrNodeType newNodeType = new JcrNodeType(this.context, this, name, supertypes,
+                                                      nodeType.getInternalPrimaryItemName(), nodeDefs, propertyDefs,
+                                                      nodeType.isMixin(), nodeType.hasOrderableChildNodes());
+            typesPendingRegistration.add(newNodeType);
+        }
+
+        // Graph.Batch batch = graph.batch();
+        for (JcrNodeType nodeType : typesPendingRegistration) {
+            /*
+             * See comment in constructor.  Using a ConcurrentHashMap seems to be to weak of a
+             * solution (even it were also used for childNodeDefinitions and propertyDefinitions).
+             * Probably need to block all read access to these maps during this phase of registration.
+             */
+            nodeTypes.put(nodeType.getInternalName(), nodeType);
+            for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
+                childNodeDefinitions.put(childDefinition.getId(), childDefinition);
+            }
+            for (JcrPropertyDefinition propertyDefinition : nodeType.propertyDefinitions()) {
+                propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
+            }
+
+            // projectNodeTypeOnto(nodeType, parentOfTypeNodes, batch);
+        }
+
+        // batch.execute();
+
+        return typesPendingRegistration;
+    }
+
+    /**
+     * Finds the named type in the given list of types pending registration if it exists, else returns the type definition from
+     * the repository
+     * 
+     * @param typeName the name of the type to retrieve
+     * @param pendingList a list of types that have passed validation but have not yet been committed to the repository
+     * @return the node type with the given name from {@code pendingList} if it exists in the list or from the {@link #nodeTypes
+     *         registered types} if it exists there; may be null
+     */
+    private JcrNodeType findTypeInMapOrList( Name typeName,
+                                             List<JcrNodeType> pendingList ) {
+        for (JcrNodeType pendingNodeType : pendingList) {
+            if (pendingNodeType.getInternalName().equals(typeName)) {
+                return pendingNodeType;
+            }
+        }
+
+        return nodeTypes.get(typeName);
+    }
+
+    /**
+     * Returns the list of node types for the supertypes defined in the given node type.
+     * 
+     * @param nodeType a node type with a non-null array of supertypes
+     * @param pendingTypes the list of types that have been processed in this type batch but not yet committed to the repository's
+     *        set of types
+     * @return a list of node types where each element is the node type for the corresponding element of the array of supertype
+     *         names
+     * @throws RepositoryException if any of the names in the array of supertype names does not correspond to an
+     *         already-registered node type or a node type that is pending registration
+     */
+    private List<JcrNodeType> supertypesFor( JcrNodeType nodeType,
+                                             List<JcrNodeType> pendingTypes ) throws RepositoryException {
+        assert nodeType != null;
+
+        // If no supertypes are provided, assume nt:base as a supertype
+        if (nodeType.getDeclaredSupertypes() == null || nodeType.getDeclaredSupertypes().length == 0) {
+            return Collections.<JcrNodeType>singletonList(nodeTypes.get(JcrNtLexicon.BASE));
+        }
+
+        JcrNodeType[] supertypesArray = nodeType.getDeclaredSupertypes();
+        List<JcrNodeType> supertypes = new ArrayList<JcrNodeType>(supertypesArray.length);
+
+        for (int i = 0; i < supertypesArray.length; i++) {
+            supertypes.add(findTypeInMapOrList(supertypesArray[i].getInternalName(), pendingTypes));
+
+            if (supertypes.get(i) == null) {
+                throw new RepositoryException(JcrI18n.invalidSupertypeName.text(supertypesArray[i].getInternalName(),
+                                                                                nodeType.getName()));
+            }
+        }
+
+        return supertypes;
+    }
+
+    /**
+     * Validates that the supertypes are compatible under DNA restrictions.
+     * <p>
+     * DNA imposes the following rules on the supertypes of a type:
+     * <ol>
+     * <li>The type must have at least one supertype (unless the type is {@code nt:base}.</li>
+     * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a property ({@code p1} and {@code p2}) with the
+     * same name and cardinality ({@code p1.isMultiple() == p2.isMultiple()}). Note that this does prohibit each {@code t1} and
+     * {@code t2} from having a common supertype (or super-supertype, etc.) that declares a property).</li>
+     * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a child node ({@code n1} and {@code n2}) with the
+     * same name and SNS status ({@code p1.allowsSameNameSiblings() == p2.allowsSameNameSiblings()}). Note that this does prohibit
+     * each {@code t1} and {@code t2} from having a common supertype (or super-supertype, etc.) that declares a child node).</li>
+     * </ol>
+     * </p>
+     * <p>
+     * If any of these rules are violated, a {@link RepositoryException} is thrown.
+     * </p>
+     * 
+     * @param supertypes the supertypes of this node type
+     * @param nodeName the name of the node for which the supertypes are being validated.
+     * @throws RepositoryException if any of the rules described above are violated
+     */
+    private void validate( List<JcrNodeType> supertypes,
+                           String nodeName ) throws RepositoryException {
+        assert supertypes.size() > 0; // This is reasonable now that we default to having a supertype of nt:base
+
+        Map<JcrPropertyDefinition.Key, JcrPropertyDefinition> props = new HashMap<JcrPropertyDefinition.Key, JcrPropertyDefinition>();
+
+        for (JcrNodeType supertype : supertypes) {
+            for (JcrPropertyDefinition property : supertype.propertyDefinitions()) {
+                JcrPropertyDefinition oldProp = props.put(property.getKey(false), property);
+                if (oldProp != null) {
+                    String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
+                    String propTypeName = property.getDeclaringNodeType().getName();
+                    if (!oldPropTypeName.equals(propTypeName)) {
+                        throw new RepositoryException(JcrI18n.supertypesConflict.text(oldPropTypeName,
+                                                                                      propTypeName,
+                                                                                      "property",
+                                                                                      property.getName()));
+                    }
+                }
+            }
+        }
+
+        Map<JcrNodeDefinition.Key, JcrNodeDefinition> childNodes = new HashMap<JcrNodeDefinition.Key, JcrNodeDefinition>();
+
+        for (JcrNodeType supertype : supertypes) {
+            for (JcrNodeDefinition childNode : supertype.childNodeDefinitions()) {
+                JcrNodeDefinition oldNode = childNodes.put(childNode.getKey(false), childNode);
+                if (oldNode != null) {
+                    String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
+                    String childNodeTypeName = childNode.getDeclaringNodeType().getName();
+                    if (!oldNodeTypeName.equals(childNodeTypeName)) {
+                        throw new RepositoryException(JcrI18n.supertypesConflict.text(oldNodeTypeName,
+                                                                                      childNodeTypeName,
+                                                                                      "child node",
+                                                                                      childNode.getName()));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Validates that the given node type definition is valid under the DNA and JCR type rules within the given context.
+     * <p>
+     * See {@link #registerNodeTypes(JcrNodeTypeSource)} for the list of criteria that determine whether a node type definition is
+     * valid.
+     * </p>
+     * 
+     * @param nodeType the node type to attempt to validate
+     * @param supertypes the names of the supertypes of the node type to which this child node belongs
+     * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+     * @throws RepositoryException if the given node type template is not valid
+     */
+    private void validate( JcrNodeType nodeType,
+                           List<JcrNodeType> supertypes,
+                           List<JcrNodeType> pendingTypes ) throws RepositoryException {
+        validate(supertypes, nodeType.getName());
+
+        List<Name> supertypeNames = new ArrayList<Name>(supertypes.size());
+        for (JcrNodeType supertype : supertypes)
+            supertypeNames.add(supertype.getInternalName());
+
+        boolean found = false;
+        String primaryItemName = nodeType.getPrimaryItemName();
+        for (JcrNodeDefinition node : nodeType.getDeclaredChildNodeDefinitions()) {
+            validate(node, supertypeNames, pendingTypes);
+
+            if (primaryItemName != null && primaryItemName.equals(node.getName())) {
+                found = true;
+            }
+        }
+
+        for (JcrPropertyDefinition prop : nodeType.getDeclaredPropertyDefinitions()) {
+            validate(prop, supertypeNames, pendingTypes);
+            if (primaryItemName != null && primaryItemName.equals(prop.getName())) {
+                if (found) {
+                    throw new RepositoryException(JcrI18n.ambiguousPrimaryItemName.text(primaryItemName));
+                }
+                found = true;
+            }
+        }
+
+        if (primaryItemName != null && !found) {
+            throw new RepositoryException(JcrI18n.invalidPrimaryItemName.text(primaryItemName));
+        }
+    }
+
+    /**
+     * Validates that the given child node definition is valid under the DNA and JCR type rules within the given context.
+     * <p>
+     * DNA considers a child node definition valid if it meets these criteria:
+     * <ol>
+     * <li>Residual child node definitions cannot be mandatory</li>
+     * <li>If the child node is auto-created, it must specify a default primary type name</li>
+     * <li>All required primary types must already be fully registered with the type manager or must have been defined earlier in
+     * the batch. This requirement may be relaxed in a future version of DNA.</li>
+     * <li>If the child node overrides an existing child node definition from a supertype, the new definition must be mandatory if
+     * the old definition was mandatory</li>
+     * <li>The child node cannot override an existing child node definition from a supertype if the ancestor definition is
+     * protected</li>
+     * <li>If the child node overrides an existing child node definition from a supertype, the required primary types of the new
+     * definition must be more restrictive than the required primary types of the old definition - that is, the new primary types
+     * must defined such that any type that satisfies all of the required primary types for the new definition must also satisfy
+     * all of the required primary types for the old definition. This requirement is analogous to the requirement that overriding
+     * property definitions have a required type that is always convertible to the required type of the overridden definition.</li>
+     * </ol>
+     * </p>
+     * 
+     * @param node the child node definition to be validated
+     * @param supertypes the names of the supertypes of the node type to which this child node belongs
+     * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+     * @throws RepositoryException if the child node definition is not valid
+     */
+    private void validate( JcrNodeDefinition node,
+                           List<Name> supertypes,
+                           List<JcrNodeType> pendingTypes ) throws RepositoryException {
+        if (node.isAutoCreated() && node.getDefaultPrimaryType() == null) {
+            throw new RepositoryException(JcrI18n.autocreatedNodesNeedDefaults.text());
+        }
+        if (node.isMandatory() && node.getName() == null) {
+            throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("child nodes"));
+        }
+
+        Name nodeName = context.getValueFactories().getNameFactory().create(node.getName());
+        nodeName = nodeName == null ? JcrNodeType.RESIDUAL_NAME : nodeName;
+
+        List<JcrNodeDefinition> ancestors = findChildNodeDefinitions(supertypes,
+                                                                     nodeName,
+                                                                     node.allowsSameNameSiblings() ? NodeCardinality.SAME_NAME_SIBLINGS : NodeCardinality.NO_SAME_NAME_SIBLINGS,
+                                                                     pendingTypes);
+
+        for (JcrNodeDefinition ancestor : ancestors) {
+            if (ancestor.isProtected()) {
+                throw new RepositoryException(
+                                              JcrI18n.cannotOverrideProtectedDefinition.text(ancestor.getDeclaringNodeType().getName(),
+                                                                                             "child node"));
+            }
+
+            if (ancestor.isMandatory() && !node.isMandatory()) {
+                throw new RepositoryException(
+                                              JcrI18n.cannotMakeMandatoryDefinitionOptional.text(ancestor.getDeclaringNodeType().getName(),
+                                                                                                 "child node"));
+
+            }
+
+            for (int i = 0; i < ancestor.getRequiredPrimaryTypes().length; i++) {
+                NodeType apt = ancestor.getRequiredPrimaryTypes()[i];
+                boolean found = false;
+                for (Name name : node.getRequiredPrimaryTypeNames()) {
+                    JcrNodeType npt = findTypeInMapOrList(name, pendingTypes);
+
+                    if (npt.isNodeType(apt.getName())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    throw new RepositoryException(
+                                                  "Cannot redefine child node '"
+                                                  + nodeName
+                                                  + "' with required type '"
+                                                  + apt.getName()
+                                                  + "' with new child node that does not required that type or a subtype of that type.");
+
+                }
+            }
+        }
+    }
+
+    /**
+     * Validates that the given property definition is valid under the DNA and JCR type rules within the given context.
+     * <p>
+     * DNA considers a property definition valid if it meets these criteria:
+     * <ol>
+     * <li>Residual properties cannot be mandatory</li>
+     * <li>If the property is auto-created, it must specify a default value</li>
+     * <li>If the property is single-valued, it can only specify a single default value</li>
+     * <li>If the property overrides an existing property definition from a supertype, the new definition must be mandatory if the
+     * old definition was mandatory</li>
+     * <li>The property cannot override an existing property definition from a supertype if the ancestor definition is protected</li>
+     * <li>If the property overrides an existing property definition from a supertype, the new definition must have the same
+     * required type as the old definition or a required type that can ALWAYS be cast to the required type of the ancestor (see
+     * section 6.2.6 of the JCR 1.0.1 specification)</li>
+     * </ol>
+     * Note that an empty set of properties would meet the criteria above.
+     * </p>
+     * 
+     * @param prop the property definition to be validated
+     * @param supertypes the names of the supertypes of the node type to which this property belongs
+     * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+     * @throws RepositoryException if the property definition is not valid
+     */
+    private void validate( JcrPropertyDefinition prop,
+                           List<Name> supertypes,
+                           List<JcrNodeType> pendingTypes ) throws RepositoryException {
+        assert prop != null;
+        assert supertypes != null;
+        assert pendingTypes != null;
+
+        if (prop.isMandatory() && !prop.isProtected() && prop.getName() == null) {
+            throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("properties"));
+        }
+
+        Value[] defaultValues = prop.getDefaultValues();
+        if (prop.isAutoCreated() && !prop.isProtected() && (defaultValues == null || defaultValues.length == 0)) {
+            throw new RepositoryException(JcrI18n.autocreatedPropertyNeedsDefault.text(prop.getName(),
+                                                                                       prop.getDeclaringNodeType().getName()));
+        }
+
+        if (!prop.isMultiple() && (defaultValues != null && defaultValues.length > 1)) {
+            throw new RepositoryException(
+                                          JcrI18n.singleValuedPropertyNeedsSingleValuedDefault.text(prop.getName(),
+                                                                                                    prop.getDeclaringNodeType().getName()));
+        }
+
+        Name propName = context.getValueFactories().getNameFactory().create(prop.getName());
+        propName = propName == null ? JcrNodeType.RESIDUAL_NAME : propName;
+
+        List<JcrPropertyDefinition> ancestors = findPropertyDefinitions(supertypes,
+                                                                        propName,
+                                                                        prop.isMultiple() ? PropertyCardinality.MULTI_VALUED_ONLY : PropertyCardinality.SINGLE_VALUED_ONLY,
+                                                                        pendingTypes);
+
+        for (JcrPropertyDefinition ancestor : ancestors) {
+            if (ancestor.isProtected()) {
+                throw new RepositoryException(
+                                              JcrI18n.cannotOverrideProtectedDefinition.text(ancestor.getDeclaringNodeType().getName(),
+                                                                                             "property"));
+            }
+
+            if (ancestor.isMandatory() && !prop.isMandatory()) {
+                throw new RepositoryException(
+                                              JcrI18n.cannotMakeMandatoryDefinitionOptional.text(ancestor.getDeclaringNodeType().getName(),
+                                                                                                 "property"));
+
+            }
+
+            // TODO: It would be nice if we could allow modification of constraints if the new constraints were more strict than
+            // the old
+            if (ancestor.getValueConstraints() != null
+                && !Arrays.equals(ancestor.getValueConstraints(), prop.getValueConstraints())) {
+                throw new RepositoryException(JcrI18n.constraintsChangedInSubtype.text(propName,
+                                                                                       ancestor.getDeclaringNodeType().getName()));
+            }
+
+            if (!isAlwaysSafeConversion(prop.getRequiredType(), ancestor.getRequiredType())) {
+                throw new RepositoryException(
+                                              JcrI18n.cannotRedefineProperty.text(propName,
+                                                                                  PropertyType.nameFromValue(prop.getRequiredType()),
+                                                                                  ancestor.getDeclaringNodeType().getName(),
+                                                                                  PropertyType.nameFromValue(ancestor.getRequiredType())));
+
+            }
+        }
+    }
+
+    /**
+     * Returns whether it is always possible to convert a value with JCR property type {@code fromType} to {@code toType}.
+     * <p>
+     * This method is based on the conversions which can never throw an exception in the chart in section 6.2.6 of the JCR 1.0.1
+     * specification.
+     * </p>
+     * 
+     * @param fromType the type to be converted from
+     * @param toType the type to convert to
+     * @return true if any value with type {@code fromType} can be converted to a type of {@code toType} without a
+     *         {@link ValueFormatException} being thrown.
+     * @see PropertyType
+     */
+    private boolean isAlwaysSafeConversion( int fromType,
+                                            int toType ) {
+
+        if (fromType == toType) return true;
+
+        switch (toType) {
+            case PropertyType.BOOLEAN:
+                return fromType == PropertyType.BINARY || fromType == PropertyType.STRING;
+
+            case PropertyType.DATE:
+                return fromType == PropertyType.DOUBLE || fromType == PropertyType.LONG;
+
+            case PropertyType.DOUBLE:
+                // Conversion from DATE could result in out-of-range value
+                return fromType == PropertyType.LONG;
+            case PropertyType.LONG:
+                // Conversion from DATE could result in out-of-range value
+                return fromType == PropertyType.DOUBLE;
+
+            case PropertyType.PATH:
+                return fromType == PropertyType.NAME;
+
+                // Values of any type MAY fail when converting to these types
+            case PropertyType.NAME:
+            case PropertyType.REFERENCE:
+                return false;
+
+                // Any type can be converted to these types
+            case PropertyType.BINARY:
+            case PropertyType.STRING:
+            case PropertyType.UNDEFINED:
+                return true;
+
+            default:
+                throw new IllegalStateException("Unexpected state: " + toType);
+        }
+    }
+
 }

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-14 16:13:03 UTC (rev 820)
@@ -66,6 +66,7 @@
 errorWhileFindingNodeWithUuid = Error while finding the node with UUID "{0}" in workspace "{1}": {2}
 errorWhileFindingNodeWithPath = Error while finding the node "{0}" in workspace "{1}"
 nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
+noSnsDefinitionForNode = A node definition that allows same name siblings could not be found for the node "{0}" in workspace "{1}"
 missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
 unableToCreateNodeWithPrimaryTypeThatDoesNotExist = Unable to create child "{1}" in workspace "{2}" because the node type "{0}" does not exist
 unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition = Unable to create child "{2}" in workspace "{3}" because the node definition "{0}" on the "{1}" node type has no default primary type 
@@ -93,4 +94,20 @@
 tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this node
 
 notStoredQuery=This query has not been stored or loaded
-invalidQueryLanguage="{0}" is not a valid query langauge.  Supported languages are: {1}
\ No newline at end of file
+invalidQueryLanguage="{0}" is not a valid query langauge.  Supported languages are\: {1}
+
+invalidNodeTypeName=Node types cannot have a null or empty name
+nodeTypeAlreadyExists=Node type '{0}' already exists
+invalidPrimaryTypeName=Required primary type '{0}' in type '{1}' does not exist
+invalidSupertypeName=Supertype '{0}' from type '{1}' does not exist
+supertypesConflict=Types '{0}' and '{1}' cannot both be supertypes of the same type, as both separately declare {2} '{3}'
+ambiguousPrimaryItemName=Primary item name '{0}' matches the name of a child node and a property
+invalidPrimaryItemName=Primary item name '{0}' does not match the name of any child nodes or properties
+autocreatedNodesNeedDefaults=Autocreated child nodes must specify a default primary type
+residualDefinitionsCannotBeMandatory=Residual {0} cannot be mandatory
+cannotOverrideProtectedDefinition=Cannot override protected {1} definition from '{0}'
+cannotMakeMandatoryDefinitionOptional=Cannot override mandatory {1} definition from '{0}' with a non-mandatory definition.
+constraintsChangedInSubtype=Must use exact same constraints when overriding the property definition for {0} from type '{1}'
+cannotRedefineProperty=Cannot redefine property '{0}' with new type '{1}' when existing property with same name in type '{2}' has incompatible type '{3}'
+autocreatedPropertyNeedsDefault=Auto-created property '{0}' in type '{1}' must specify a default value
+singleValuedPropertyNeedsSingleValuedDefault=Single-valued property '{0}' in type '{1}' cannot have multiple default values



From dna-commits at lists.jboss.org  Tue Apr 14 12:29:42 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 14 Apr 2009 12:29:42 -0400
Subject: [dna-commits] DNA SVN: r821 -
	trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr.
Message-ID: <E1LtlW2-0001WK-VS@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-14 12:29:42 -0400 (Tue, 14 Apr 2009)
New Revision: 821

Modified:
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
Log:
Minor correction to one localized message.

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-14 16:13:03 UTC (rev 820)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-14 16:29:42 UTC (rev 821)
@@ -80,7 +80,7 @@
 nodeHasAlreadyBeenRemovedFromThisSession = Node "{0}" in workspace "{1} has already been removed from this session
 
 REP_NAME_DESC = DNA Repository
-REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC
+REP_VENDOR_DESC = JBoss - A division of Red Hat
 SPEC_NAME_DESC = Content Repository for Java Technology API
 
 errorObtainingWorkspaceNames = Error while obtaining the workspace names for the "{0}" repository: {1}



From dna-commits at lists.jboss.org  Tue Apr 14 12:41:54 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 14 Apr 2009 12:41:54 -0400
Subject: [dna-commits] DNA SVN: r822 -
	trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
Message-ID: <E1Ltlhq-0006HM-O1@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-14 12:41:54 -0400 (Tue, 14 Apr 2009)
New Revision: 822

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrReadingTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWritingTest.java
Log:
DNA-351 Performance Test for JCR Access

Applied the patch, which adds a number of new simple performance tests for measuring reads and writes through the JCR layer.

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java	2009-04-14 16:41:54 UTC (rev 822)
@@ -0,0 +1,271 @@
+/*
+ * 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;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.io.PrintStream;
+import java.util.concurrent.TimeUnit;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+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.PathFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Support class for performance testing of various operations over subtrees of the content graph
+ */
+
+public abstract class AbstractJcrAccessTest {
+
+    private InMemoryRepositorySource source;
+    private JcrSession session;
+    private JcrRepository repository;
+    
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        String workspaceName = "workspace1";
+
+        // Set up the source ...
+        source = new InMemoryRepositorySource();
+        source.setName(workspaceName);
+        source.setDefaultWorkspaceName(workspaceName);
+
+        // Set up the execution context ...
+        ExecutionContext context = new ExecutionContext();
+        // Register the test namespace
+        context.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
+
+        // Set up the initial content ...
+        Graph graph = Graph.create(source, context);
+
+        // Make sure the path to the namespaces exists ...
+        graph.create("/jcr:system"); // .and().create("/jcr:system/dna:namespaces");
+        graph.set("jcr:primaryType").on("/jcr:system").to(DnaLexicon.SYSTEM);
+
+        // Stub out the connection factory ...
+        RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
+             */
+            @SuppressWarnings( "synthetic-access" )
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                return source.getConnection();
+            }
+        };
+
+        repository = new JcrRepository(context, connectionFactory, "unused");
+
+        session = (JcrSession) repository.login();
+    }
+
+    @After
+    public void after() throws Exception {
+        if (session != null && session.isLive()) {
+            session.logout();
+        }
+    }
+    
+    protected JcrSession session() {
+        return this.session;
+    }
+    
+    private String getRandomString(int length) {
+        StringBuffer buff = new StringBuffer(length);
+        
+        for (int i = 0; i < length; i++) {
+            buff.append((char) ((Math.random() *26) + 'a'));
+        }
+        
+        return buff.toString();
+    }
+    
+    private int createChildren(Node parent, int numProperties, int width, int depth) 
+        throws Exception
+    {
+        if (depth < 1) {
+            return 0;
+            
+        }
+
+        int count = width;
+        
+        for (int i = 0; i < width; i++) {
+            Node newNode = parent.addNode(getRandomString(9), "nt:unstructured");
+            
+            for (int j = 0; j < numProperties; j++) {
+                newNode.setProperty(getRandomString(8), getRandomString(16));
+            }
+
+            count += createChildren(newNode, numProperties, width, depth - 1);
+        }
+        return count;
+    }
+    
+    protected int createSubgraph( JcrSession session,
+                                  String initialPath,
+                                  int depth,
+                                  int numberOfChildrenPerNode,
+                                  int numberOfPropertiesPerNode,
+                                  boolean oneBatch,
+                                  Stopwatch stopwatch,
+                                  PrintStream output,
+                                  String description )
+        throws Exception
+    {
+        // Calculate the number of nodes that we'll created, but subtract 1 since it doesn't create the root
+        long totalNumber = calculateTotalNumberOfNodesInTree(numberOfChildrenPerNode, depth, false);
+        if (initialPath == null) initialPath = "";
+        if (description == null) {
+            description = "" + numberOfChildrenPerNode + "x" + depth + " tree with " + numberOfPropertiesPerNode
+                          + " properties per node";
+        }
+
+        if (output != null) output.println(description + " (" + totalNumber + " nodes):");
+        long totalNumberCreated = 0;
+        
+        PathFactory pathFactory = session.getExecutionContext().getValueFactories().getPathFactory();
+        Node parentNode = session.getNode(pathFactory.create(initialPath));
+        
+        if (stopwatch != null) stopwatch.start();
+
+        totalNumberCreated += createChildren(parentNode,
+                                             numberOfPropertiesPerNode,
+                                             numberOfChildrenPerNode,
+                                             depth);
+
+        assertThat(totalNumberCreated, is(totalNumber));
+
+        session.save();
+
+        
+        if (stopwatch != null) {
+            stopwatch.stop();
+            if (output != null) {
+                output.println("    " + getTotalAndAverageDuration(stopwatch, totalNumberCreated));
+            }
+        }
+        return (int)totalNumberCreated;
+
+    }
+
+    protected int traverseSubgraph( JcrSession session,
+                                  String initialPath,
+                                  int depth,
+                                  int numberOfChildrenPerNode,
+                                  int numberOfPropertiesPerNode,
+                                  boolean oneBatch,
+                                  Stopwatch stopwatch,
+                                  PrintStream output,
+                                  String description )
+        throws Exception
+    {
+        // Calculate the number of nodes that we'll created, but subtract 1 since it doesn't create the root
+        long totalNumber = calculateTotalNumberOfNodesInTree(numberOfChildrenPerNode, depth, false);
+        if (initialPath == null) initialPath = "";
+        if (description == null) {
+            description = "" + numberOfChildrenPerNode + "x" + depth + " tree with " + numberOfPropertiesPerNode
+                          + " properties per node";
+        }
+
+        if (output != null) output.println(description + " (" + totalNumber + " nodes):");
+        long totalNumberTraversed = 0;
+        
+        PathFactory pathFactory = session.getExecutionContext().getValueFactories().getPathFactory();
+        Node parentNode = session.getNode(pathFactory.create(initialPath));
+        
+        if (stopwatch != null) stopwatch.start();
+
+        totalNumberTraversed += traverseChildren(parentNode);
+
+        assertThat(totalNumberTraversed, is(totalNumber));
+
+        session.save();
+
+        
+        if (stopwatch != null) {
+            stopwatch.stop();
+            if (output != null) {
+                output.println("    " + getTotalAndAverageDuration(stopwatch, totalNumberTraversed));
+            }
+        }
+        return (int)totalNumberTraversed;
+
+    }
+
+    protected int traverseChildren(Node parentNode) throws Exception {
+
+        int childCount = 0;
+        NodeIterator children = parentNode.getNodes();
+
+        while (children.hasNext()) {
+            childCount++;
+            
+            childCount += traverseChildren(children.nextNode());
+        }
+        
+        return childCount;
+    }
+    
+    protected String getTotalAndAverageDuration( Stopwatch stopwatch,
+                                                 long numNodes ) {
+        long totalDurationInMilliseconds = TimeUnit.NANOSECONDS.toMillis(stopwatch.getTotalDuration().longValue());
+        if (numNodes == 0) numNodes = 1;
+        long avgDuration = totalDurationInMilliseconds / numNodes;
+        String units = " millisecond(s)";
+        if (avgDuration < 1L) {
+            long totalDurationInMicroseconds = TimeUnit.NANOSECONDS.toMicros(stopwatch.getTotalDuration().longValue());
+            avgDuration = totalDurationInMicroseconds / numNodes;
+            units = " microsecond(s)";
+        }
+        return "total = " + stopwatch.getTotalDuration() + "; avg = " + avgDuration + units;
+    }
+
+    protected int calculateTotalNumberOfNodesInTree( int numberOfChildrenPerNode,
+                                                     int depth,
+                                                     boolean countRoot ) {
+        assert depth > 0;
+        assert numberOfChildrenPerNode > 0;
+        int totalNumber = 0;
+        for (int i = 0; i <= depth; ++i) {
+            totalNumber += (int)Math.pow(numberOfChildrenPerNode, i);
+        }
+        return countRoot ? totalNumber : totalNumber - 1;
+    }
+    
+    
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrReadingTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrReadingTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrReadingTest.java	2009-04-14 16:41:54 UTC (rev 822)
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.junit.Test;
+
+/**
+ * Test performance reading graph subtrees of various sizes with varying number of properties
+ */
+public class JcrReadingTest extends AbstractJcrAccessTest {
+
+    @Test
+    public void testReadingTrees() throws Exception {
+        int[] breadths = new int[] { 10, };
+        int[] depths = new int[] { 1, 2, 3, };
+        int[] properties = new int[] { 0, 7, 100 };
+        
+        for (int i = 0; i < breadths.length; i++) {
+            for (int j = 0; j < depths.length; j++) {
+                for (int k = 0; k < properties.length; k++) {
+                    String testName = "/" + breadths[i] + "x" + depths[j] + "x" + properties[k] + "test";
+                    session().getRootNode().addNode(testName, "nt:unstructured");
+                    createSubgraph(session(), testName, depths[j], breadths[i], properties[k], false, null, null, null);
+                    
+                    traverseSubgraph(session(), testName, depths[j], breadths[i], properties[k], false, new Stopwatch(), System.out, null);
+                }
+            }
+        }
+    }
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWritingTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWritingTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWritingTest.java	2009-04-14 16:41:54 UTC (rev 822)
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.junit.Test;
+
+
+/**
+ * Test performance writing graph subtrees of various sizes with varying number of properties
+ */
+public class JcrWritingTest extends AbstractJcrAccessTest {
+
+    @Test
+    public void testCreatingTrees() throws Exception {
+        int[] breadths = new int[] { 10, };
+        int[] depths = new int[] { 1, 2, 3, };
+        int[] properties = new int[] { 0, 7, 100 };
+        
+        for (int i = 0; i < breadths.length; i++) {
+            for (int j = 0; j < depths.length; j++) {
+                for (int k = 0; k < properties.length; k++) {
+                    String testName = "/" + breadths[i] + "x" + depths[j] + "x" + properties[k] + "test";
+                    session().getRootNode().addNode(testName, "nt:unstructured");
+                    createSubgraph(session(), testName, depths[j], breadths[i], properties[k], false, new Stopwatch(), System.out, null);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file


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



From dna-commits at lists.jboss.org  Tue Apr 14 16:06:33 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 14 Apr 2009 16:06:33 -0400
Subject: [dna-commits] DNA SVN: r823 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Ltott-0004uX-4D@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-14 16:06:32 -0400 (Tue, 14 Apr 2009)
New Revision: 823

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
Log:
DNA-339 JCR Type Registration and Unregistration

Applying the "no key" patch, which removes the Key nested class from JcrNodeDefinition and JcrPropertyDefinition and instead uses the (existing) NodeDefinitionId and PropertyDefinitionId.  The patch also adds test cases for type registration.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-14 16:41:54 UTC (rev 822)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-14 20:06:32 UTC (rev 823)
@@ -27,15 +27,11 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-import javax.jcr.Node;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NamespaceRegistry;
 
 /**
  * DNA implementation of the {@link NodeDefinition} class.
@@ -212,6 +208,13 @@
                                      defaultPrimaryTypeName, required);
     }
 
+    JcrNodeDefinition with( RepositoryNodeTypeManager nodeTypeManager ) {
+        JcrNodeType[] required = requiredPrimaryTypesByName.values().toArray(new JcrNodeType[requiredPrimaryTypesByName.size()]);
+        return new JcrNodeDefinition(this.context, nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
+                                     isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
+                                     defaultPrimaryTypeName, required);
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -221,74 +224,4 @@
     public String toString() {
         return getId().toString();
     }
-
-    Key getKey(boolean includeTypes) {
-        return new Key(this, includeTypes);
-    }
-
-    /**
-     * Internal class that encapsulates composite unique identifier for child node definitions
-     */
-    class Key {
-        String keyString;
-
-        Key( Node node,
-             NamespaceRegistry registry ) throws Exception {
-            StringBuffer buff = new StringBuffer();
-
-            try {
-                String nodeName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
-                buff.append(nodeName);
-            } catch (PathNotFoundException pnfe) {
-                // Ignorable, means name is not set.
-                buff.append(JcrNodeType.RESIDUAL_ITEM_NAME);
-            }
-
-            try {
-                Value[] requiredTypes = node.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES.getString(registry)).getValues();
-
-                for (int i = 0; i < requiredTypes.length; i++) {
-                    buff.append('_').append(requiredTypes[i].getString());
-                }
-            } catch (PathNotFoundException pnfe) {
-                // No required types. Weird, but not debilitating.
-            }
-
-            buff.append('_').append(node.getProperty(JcrLexicon.SAME_NAME_SIBLINGS.getString(registry)).getBoolean());
-
-            this.keyString = buff.toString();
-        }
-
-        Key( NodeDefinition def,
-             boolean includeTypes ) {
-            StringBuffer buff = new StringBuffer();
-            buff.append(def.getName());
-
-            if (includeTypes) {
-                NodeType[] requiredTypes = def.getRequiredPrimaryTypes();
-                for (int i = 0; i < requiredTypes.length; i++) {
-                    buff.append('_').append(requiredTypes[i].getName());
-                }
-            }
-
-            buff.append('_').append(def.allowsSameNameSiblings());
-
-            this.keyString = buff.toString();
-        }
-
-        @Override
-        public boolean equals( Object ob ) {
-            if (!(ob instanceof Key)) {
-                return false;
-            }
-
-            return keyString.equals(((Key)ob).keyString);
-        }
-
-        @Override
-        public int hashCode() {
-            return keyString.hashCode();
-        }
-    }
-
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-14 16:41:54 UTC (rev 822)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-04-14 20:06:32 UTC (rev 823)
@@ -112,6 +112,8 @@
                  Collection<JcrPropertyDefinition> propertyDefinitions,
                  boolean mixin,
                  boolean orderableChildNodes ) {
+        assert context != null;
+        
         this.context = context;
         this.nodeTypeManager = nodeTypeManager;
         this.name = name;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-14 16:41:54 UTC (rev 822)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-14 20:06:32 UTC (rev 823)
@@ -26,7 +26,6 @@
 import java.util.UUID;
 import java.util.regex.Pattern;
 import javax.jcr.Node;
-import javax.jcr.PathNotFoundException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
@@ -37,7 +36,6 @@
 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.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactories;
@@ -148,7 +146,7 @@
      * @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
      *         <code>context</code>.
      */
-    JcrPropertyDefinition with( ExecutionContext context) {
+    JcrPropertyDefinition with( ExecutionContext context ) {
         return new JcrPropertyDefinition(context, this.declaringNodeType, this.name, this.getOnParentVersion(),
                                          this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
                                          this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
@@ -592,12 +590,7 @@
 
             JcrValue jcrValue = (JcrValue)value;
             // Need to use the session execution context to handle the remaps
-            Name name = jcrValue.sessionCache()
-                                .session()
-                                .getExecutionContext()
-                                .getValueFactories()
-                                .getNameFactory()
-                                .create(jcrValue.value());
+            Name name = jcrValue.sessionCache().session().getExecutionContext().getValueFactories().getNameFactory().create(jcrValue.value());
 
             for (int i = 0; i < constraints.length; i++) {
                 if (constraints[i].equals(name)) {
@@ -689,51 +682,4 @@
             return false;
         }
     }
-
-    Key getKey(boolean includeType) {
-        return new Key(this, includeType);
-    }
-
-    /**
-     * Internal class that encapsulates composite unique identifier for property definitions
-     */
-    class Key {
-        String keyString;
-
-        Key( Node node,
-             NamespaceRegistry registry ) throws Exception {
-            String propertyName = JcrNodeType.RESIDUAL_ITEM_NAME;
-
-            try {
-                propertyName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
-            } catch (PathNotFoundException pnfe) {
-                // Ignorable, means name is not set.
-            }
-            String requiredType = node.getProperty(JcrLexicon.REQUIRED_TYPE.getString(registry)).getString();
-            boolean allowsMultiple = node.getProperty(JcrLexicon.MULTIPLE.getString(registry)).getBoolean();
-
-            this.keyString = propertyName + "-" + requiredType + "-" + allowsMultiple;
-        }
-
-        Key( PropertyDefinition def, boolean includeType ) {
-            String requiredType = PropertyType.nameFromValue(def.getRequiredType());
-
-            this.keyString = def.getName() + (includeType ? "-" + requiredType : "")  + "-" + def.isMultiple();
-        }
-
-        @Override
-        public boolean equals( Object ob ) {
-            if (!(ob instanceof Key)) {
-                return false;
-            }
-
-            return keyString.equals(((Key)ob).keyString);
-        }
-
-        @Override
-        public int hashCode() {
-            return keyString.hashCode();
-        }
-    }
-
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-14 16:41:54 UTC (rev 822)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-04-14 20:06:32 UTC (rev 823)
@@ -92,7 +92,8 @@
      */
     private enum NodeCardinality {
         NO_SAME_NAME_SIBLINGS,
-        SAME_NAME_SIBLINGS
+        SAME_NAME_SIBLINGS,
+        ANY
     }
 
     RepositoryNodeTypeManager( ExecutionContext context,
@@ -663,11 +664,14 @@
 
             switch (typesToCheck) {
                 case NO_SAME_NAME_SIBLINGS:
-                    nodeDefs = typeName.allChildNodeDefinitions(childNodeName);
+                    nodeDefs = typeName.allChildNodeDefinitions(childNodeName, false);
                     break;
                 case SAME_NAME_SIBLINGS:
                     nodeDefs = typeName.allChildNodeDefinitions(childNodeName, true);
                     break;
+                case ANY:
+                    nodeDefs = typeName.allChildNodeDefinitions(childNodeName);
+                    break;
             }
 
             assert nodeDefs != null;
@@ -1045,12 +1049,13 @@
                     i++;
                 }
 
-                nodeDefs.add(nodeDef.with(this.context));
+                nodeDefs.add(nodeDef.with(this.context).with(this));
             }
 
             JcrNodeType newNodeType = new JcrNodeType(this.context, this, name, supertypes,
                                                       nodeType.getInternalPrimaryItemName(), nodeDefs, propertyDefs,
                                                       nodeType.isMixin(), nodeType.hasOrderableChildNodes());
+
             typesPendingRegistration.add(newNodeType);
         }
 
@@ -1158,11 +1163,14 @@
                            String nodeName ) throws RepositoryException {
         assert supertypes.size() > 0; // This is reasonable now that we default to having a supertype of nt:base
 
-        Map<JcrPropertyDefinition.Key, JcrPropertyDefinition> props = new HashMap<JcrPropertyDefinition.Key, JcrPropertyDefinition>();
+        Map<PropertyDefinitionId, JcrPropertyDefinition> props = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
 
         for (JcrNodeType supertype : supertypes) {
             for (JcrPropertyDefinition property : supertype.propertyDefinitions()) {
-                JcrPropertyDefinition oldProp = props.put(property.getKey(false), property);
+                JcrPropertyDefinition oldProp = props.put(new PropertyDefinitionId(property.getInternalName(),
+                                                                                   property.getInternalName(),
+                                                                                   PropertyType.UNDEFINED, property.isMultiple()),
+                                                          property);
                 if (oldProp != null) {
                     String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
                     String propTypeName = property.getDeclaringNodeType().getName();
@@ -1176,11 +1184,13 @@
             }
         }
 
-        Map<JcrNodeDefinition.Key, JcrNodeDefinition> childNodes = new HashMap<JcrNodeDefinition.Key, JcrNodeDefinition>();
+        Map<NodeDefinitionId, JcrNodeDefinition> childNodes = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
 
         for (JcrNodeType supertype : supertypes) {
             for (JcrNodeDefinition childNode : supertype.childNodeDefinitions()) {
-                JcrNodeDefinition oldNode = childNodes.put(childNode.getKey(false), childNode);
+                JcrNodeDefinition oldNode = childNodes.put(new NodeDefinitionId(childNode.getInternalName(),
+                                                                                childNode.getInternalName(), new Name[0]),
+                                                           childNode);
                 if (oldNode != null) {
                     String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
                     String childNodeTypeName = childNode.getDeclaringNodeType().getName();
@@ -1273,17 +1283,14 @@
         if (node.isAutoCreated() && node.getDefaultPrimaryType() == null) {
             throw new RepositoryException(JcrI18n.autocreatedNodesNeedDefaults.text());
         }
-        if (node.isMandatory() && node.getName() == null) {
+        if (node.isMandatory() && JcrNodeType.RESIDUAL_ITEM_NAME.equals(node.getName())) {
             throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("child nodes"));
         }
 
         Name nodeName = context.getValueFactories().getNameFactory().create(node.getName());
         nodeName = nodeName == null ? JcrNodeType.RESIDUAL_NAME : nodeName;
 
-        List<JcrNodeDefinition> ancestors = findChildNodeDefinitions(supertypes,
-                                                                     nodeName,
-                                                                     node.allowsSameNameSiblings() ? NodeCardinality.SAME_NAME_SIBLINGS : NodeCardinality.NO_SAME_NAME_SIBLINGS,
-                                                                     pendingTypes);
+        List<JcrNodeDefinition> ancestors = findChildNodeDefinitions(supertypes, nodeName, NodeCardinality.ANY, pendingTypes);
 
         for (JcrNodeDefinition ancestor : ancestors) {
             if (ancestor.isProtected()) {
@@ -1353,7 +1360,7 @@
         assert supertypes != null;
         assert pendingTypes != null;
 
-        if (prop.isMandatory() && !prop.isProtected() && prop.getName() == null) {
+        if (prop.isMandatory() && !prop.isProtected() && JcrNodeType.RESIDUAL_ITEM_NAME.equals(prop.getName())) {
             throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("properties"));
         }
 

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java	2009-04-14 20:06:32 UTC (rev 823)
@@ -0,0 +1,1317 @@
+/*
+ * 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;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.PropertyDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+public class TypeRegistrationTest {
+
+    private static final String TEST_TYPE_NAME = "dna:testNode";
+    private static final String TEST_TYPE_NAME2 = "dna:testNode2";
+    private static final String TEST_PROPERTY_NAME = "dna:testProperty";
+    private static final String TEST_CHILD_NODE_NAME = "dna:testChildNode";
+
+    private ExecutionContext context;
+    private RepositoryNodeTypeManager repoTypeManager;
+    private JcrNodeType ntTemplate;
+    private NameFactory nameFactory;
+    private JcrNodeType base;
+    private JcrNodeType referenceable;
+    private JcrNodeType unstructured;
+    private JcrNodeType root;
+    private JcrNodeType versionHistory;
+    private JcrNodeType hierarchyNode;
+    private JcrNodeType file;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        context = new ExecutionContext();
+        nameFactory = context.getValueFactories().getNameFactory();
+
+        JcrNodeTypeSource source = null;
+        source = new JcrBuiltinNodeTypeSource(context, source);
+        source = new DnaBuiltinNodeTypeSource(this.context, source);
+        repoTypeManager = new RepositoryNodeTypeManager(context, source);
+
+        base = repoTypeManager.getNodeType(JcrNtLexicon.BASE);
+        referenceable = repoTypeManager.getNodeType(JcrMixLexicon.REFERENCEABLE);
+        unstructured = repoTypeManager.getNodeType(JcrNtLexicon.UNSTRUCTURED);
+        root = repoTypeManager.getNodeType(DnaLexicon.ROOT);
+        versionHistory = repoTypeManager.getNodeType(JcrNtLexicon.VERSION_HISTORY);
+        hierarchyNode = repoTypeManager.getNodeType(JcrNtLexicon.HIERARCHY_NODE);
+        file = repoTypeManager.getNodeType(JcrNtLexicon.FILE);
+    }
+
+    private Name nameFor( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    private JcrNodeTypeSource sourceFor( final JcrNodeType nodeType ) {
+        return new AbstractJcrNodeTypeSource() {
+            @Override
+            public List<JcrNodeType> getDeclaredNodeTypes() {
+                return Collections.singletonList(nodeType);
+            }
+        };
+    }
+
+    private JcrNodeTypeSource sourceFor( final JcrNodeType... nodeTypes ) {
+        return new AbstractJcrNodeTypeSource() {
+            @Override
+            public List<JcrNodeType> getDeclaredNodeTypes() {
+                return Arrays.asList(nodeTypes);
+            }
+        };
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowNullDefinition() throws Exception {
+        repoTypeManager.registerNodeTypes(null);
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowTemplateWithNullContext() throws Exception {
+        repoTypeManager.registerNodeTypes(sourceFor(new JcrNodeType(null, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                                    nameFor(TEST_TYPE_NAME), Arrays.asList(new JcrNodeType[] {}),
+                                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES,
+                                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowNodeTypeWithNoName() throws Exception {
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, null,
+                                     Arrays.asList(new JcrNodeType[] {}), AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES, AbstractJcrNodeTypeSource.NO_PROPERTIES,
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+        repoTypeManager.registerNodeTypes(sourceFor(ntTemplate));
+    }
+
+    @Test
+    public void shouldAllowNewDefinitionWithNoChildNodesOrProperties() throws Exception {
+        Name testTypeName = nameFactory.create(TEST_TYPE_NAME);
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, testTypeName,
+                                     Arrays.asList(new JcrNodeType[] {}), AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES, AbstractJcrNodeTypeSource.NO_PROPERTIES,
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> testNodeTypes = repoTypeManager.registerNodeTypes(sourceFor(ntTemplate));
+        JcrNodeType testNodeType = testNodeTypes.get(0);
+
+        assertThat(testNodeType.getName(), is(TEST_TYPE_NAME));
+        JcrNodeType nodeTypeFromRepo = repoTypeManager.getNodeType(testTypeName);
+        assertThat(nodeTypeFromRepo, is(notNullValue()));
+        assertThat(nodeTypeFromRepo.getName(), is(TEST_TYPE_NAME));
+    }
+
+    // @Test( expected = RepositoryException.class )
+    // public void shouldNotAllowModificationIfAllowUpdatesIsFalse() throws Exception {
+    // ntTemplate.setName("nt:base");
+    // repoTypeManager.registerNodeType(ntTemplate, false);
+    // }
+
+    // @Test( expected = RepositoryException.class )
+    // public void shouldNotAllowRedefinitionOfNewTypeIfAllowUpdatesIsFalse() throws Exception {
+    // Name testTypeName = nameFactory.create(TEST_TYPE_NAME);
+    // ntTemplate.setName(TEST_TYPE_NAME);
+    // ntTemplate.setDeclaredSupertypeNames(new String[] {"nt:base"});
+    //
+    // JcrNodeType testNodeType = repoTypeManager.registerNodeType(ntTemplate, false);
+    //
+    // assertThat(testNodeType.getName(), is(TEST_TYPE_NAME));
+    // JcrNodeType nodeTypeFromRepo = repoTypeManager.getNodeType(testTypeName);
+    // assertThat(nodeTypeFromRepo, is(notNullValue()));
+    // assertThat(nodeTypeFromRepo.getName(), is(TEST_TYPE_NAME));
+    //
+    // testNodeType = repoTypeManager.registerNodeType(ntTemplate, false);
+    // }
+
+    @Test
+    public void shouldAllowDefinitionWithExistingSupertypes() throws Exception {
+        Name testTypeName = nameFactory.create(TEST_TYPE_NAME);
+
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, testTypeName,
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> testNodeTypes = repoTypeManager.registerNodeTypes(sourceFor(ntTemplate));
+        JcrNodeType testNodeType = testNodeTypes.get(0);
+
+        assertThat(testNodeType.getName(), is(TEST_TYPE_NAME));
+        JcrNodeType nodeTypeFromRepo = repoTypeManager.getNodeType(testTypeName);
+        assertThat(nodeTypeFromRepo, is(notNullValue()));
+        assertThat(nodeTypeFromRepo.getName(), is(TEST_TYPE_NAME));
+
+    }
+
+    @Test
+    public void shouldAllowDefinitionWithSupertypesFromTypesRegisteredInSameCall() throws Exception {
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType ntTemplate2 = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                  nameFor(TEST_TYPE_NAME2), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                  AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                  AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                  AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                  AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, ntTemplate2});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, ntTemplate2)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowDefinitionWithSupertypesFromTypesRegisteredInSameCallInWrongOrder() throws Exception {
+        // Try to register the supertype AFTER the class that registers it
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType ntTemplate2 = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                  nameFor(TEST_TYPE_NAME2), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                  AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                  AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                  AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                  AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate2, ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate2, ntTemplate)));
+    }
+
+    @Test
+    public void shouldAllowDefinitionWithAProperty() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          null,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.LONG,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false)}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+    }
+
+    @Test
+    public void shouldAllowDefinitionWithProperties() throws Exception {
+
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {
+                                         new JcrPropertyDefinition(context, null, null,
+                                                                   OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                                   false, AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                   PropertyType.LONG, AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                   false),
+                                         new JcrPropertyDefinition(context, null, null,
+                                                                   OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                                   false, AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                   PropertyType.STRING, AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                   true),
+                                         new JcrPropertyDefinition(context, null, nameFor(TEST_PROPERTY_NAME),
+                                                                   OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                                   false, AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                   PropertyType.STRING, AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                   false)}), AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowAutocreatedPropertyWithNoDefault() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          nameFor(TEST_PROPERTY_NAME),
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          true,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.LONG,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowAutocreatedResidualProperty() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          null,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          true,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.UNDEFINED,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    // @Test
+    // public void shouldAllowAutocreatedNamedPropertyWithDefault() throws Exception {
+    // ntTemplate.setName(TEST_TYPE_NAME);
+    // ntTemplate.setDeclaredSupertypeNames(new String[] {"nt:base", "mix:referenceable"});
+    //
+    // JcrPropertyDefinitionTemplate prop = new JcrPropertyDefinitionTemplate(this.context);
+    // prop.setName(TEST_PROPERTY_NAME);
+    // prop.setRequiredType(PropertyType.UNDEFINED);
+    // prop.setAutoCreated(true);
+    // prop.setDefaultValues(new String[] {"<default>"});
+    // ntTemplate.getPropertyDefinitionTemplates().add(prop);
+    //
+    // List<NodeTypeDefinition> templates = Arrays.asList(new NodeTypeDefinition[] {ntTemplate});
+    // compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(templates, false));
+    // }
+    //
+    // @Test( expected = RepositoryException.class )
+    // public void shouldNotAllowSingleValuedPropertyWithMultipleDefaults() throws Exception {
+    // ntTemplate.setName(TEST_TYPE_NAME);
+    // ntTemplate.setDeclaredSupertypeNames(new String[] {"nt:base", "mix:referenceable"});
+    //
+    // JcrPropertyDefinitionTemplate prop = new JcrPropertyDefinitionTemplate(this.context);
+    // prop.setName(TEST_PROPERTY_NAME);
+    // prop.setRequiredType(PropertyType.UNDEFINED);
+    // prop.setAutoCreated(true);
+    // prop.setDefaultValues(new String[] {"<default>", "too many values"});
+    // ntTemplate.getPropertyDefinitionTemplates().add(prop);
+    //
+    // List<NodeTypeDefinition> templates = Arrays.asList(new NodeTypeDefinition[] {ntTemplate});
+    // compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(templates, false));
+    // }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowMandatoryResidualProperty() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          null,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          true,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.UNDEFINED,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test
+    public void shouldAllowTypeWithChildNode() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, false, false, true,
+                                                                                                  JcrNtLexicon.BASE,
+                                                                                                  new JcrNodeType[] {base})}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test
+    public void shouldAllowTypeWithMultipleChildNodes() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {
+                                         new JcrNodeDefinition(context, null, nameFor(TEST_CHILD_NODE_NAME),
+                                                               OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                               false, true, JcrNtLexicon.BASE, new JcrNodeType[] {base}),
+                                         new JcrNodeDefinition(context, null, nameFor(TEST_CHILD_NODE_NAME),
+                                                               OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                               false, false, JcrNtLexicon.BASE, new JcrNodeType[] {unstructured}),
+                                         new JcrNodeDefinition(context, null, nameFor(TEST_CHILD_NODE_NAME + "2"),
+                                                               OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                               false, true, JcrNtLexicon.BASE, new JcrNodeType[] {base}),
+
+                                     }), AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowAutocreatedChildNodeWithNoDefaultPrimaryType() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  true, false, false, false,
+                                                                                                  null, new JcrNodeType[] {base}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowMandatoryResidualChildNode() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  null,
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, true, false, false,
+                                                                                                  JcrNtLexicon.BASE,
+                                                                                                  new JcrNodeType[] {base}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingProtectedProperty() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          JcrLexicon.PRIMARY_TYPE,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.NAME,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingProtectedChildNode() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {root, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  JcrLexicon.SYSTEM,
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, false, false, false,
+                                                                                                  JcrNtLexicon.BASE,
+                                                                                                  new JcrNodeType[] {base}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingMandatoryChildNodeWithOptionalChildNode() throws Exception {
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {versionHistory}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  JcrLexicon.ROOT_VERSION,
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, false, false, false,
+                                                                                                  JcrNtLexicon.BASE,
+                                                                                                  new JcrNodeType[] {base}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate)));
+
+    }
+
+    @Test
+    public void shouldAllowOverridingPropertyFromCommonAncestor() throws Exception {
+        /*
+        * testNode declares prop testProperty
+        * testNodeB extends testNode
+        * testNodeC extends testNode
+        * testNodeD extends testNodeB and testNodeC and overrides testProperty --> LEGAL
+        */
+
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          nameFor(TEST_PROPERTY_NAME),
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.UNDEFINED,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeCTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "C"), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeDTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "D"),
+                                                    Arrays.asList(new JcrNodeType[] {nodeBTemplate, nodeCTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.STRING,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate, nodeCTemplate, nodeDTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate,
+                                                                                           nodeBTemplate,
+                                                                                           nodeCTemplate,
+                                                                                           nodeDTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingPropertyFromDifferentAncestors() throws Exception {
+        /*
+        * testNode
+        * testNodeB extends testNode and declares prop testProperty
+        * testNodeC extends testNode and declares prop testProperty
+        * testNodeD extends testNodeB and testNodeC and overrides testProperty --> ILLEGAL
+        */
+
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.UNDEFINED,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeCTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "C"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.UNDEFINED,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeDTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "D"),
+                                                    Arrays.asList(new JcrNodeType[] {nodeBTemplate, nodeCTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.STRING,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate, nodeCTemplate, nodeDTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate,
+                                                                                           nodeBTemplate,
+                                                                                           nodeCTemplate,
+                                                                                           nodeDTemplate)));
+    }
+
+    @Test
+    public void shouldAllowOverridingChildNodeFromCommonAncestor() throws Exception {
+        /*
+        * testNode declares node testChildNode
+        * testNodeB extends testNode
+        * testNodeC extends testNode
+        * testNodeD extends testNodeB and testNodeC and overrides testChildNode --> LEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  null,
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, false, false, false,
+                                                                                                  JcrNtLexicon.BASE,
+                                                                                                  new JcrNodeType[] {base}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeCTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "C"), Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeDTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "D"),
+                                                    Arrays.asList(new JcrNodeType[] {nodeBTemplate, nodeCTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 JcrLexicon.SYSTEM,
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                 new JcrNodeType[] {unstructured}),}),
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate, nodeCTemplate, nodeDTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate,
+                                                                                           nodeBTemplate,
+                                                                                           nodeCTemplate,
+                                                                                           nodeDTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingChildNodeFromDifferentAncestors() throws Exception {
+        /*
+        * testNode
+        * testNodeB extends testNode and declares node testChildNode
+        * testNodeC extends testNode and declares node testChildNode
+        * testNodeD extends testNodeB and testNodeC and overrides testChildNode --> ILLEGAL
+        */
+        ntTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER, nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME, AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 null,
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 JcrNtLexicon.BASE,
+                                                                                                                 new JcrNodeType[] {base}),}),
+
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeCTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "C"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 null,
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 JcrNtLexicon.BASE,
+                                                                                                                 new JcrNodeType[] {base}),}),
+
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeDTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "D"),
+                                                    Arrays.asList(new JcrNodeType[] {nodeBTemplate, nodeCTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 JcrLexicon.SYSTEM,
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                 new JcrNodeType[] {unstructured}),}),
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate, nodeCTemplate, nodeDTemplate});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate,
+                                                                                           nodeBTemplate,
+                                                                                           nodeCTemplate,
+                                                                                           nodeDTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowExtendingChildNodeIfSnsChanges() throws Exception {
+        /*
+        * testNode declares node testChildNode with no SNS
+        * testNodeB extends testNode with node testChildNode with SNS -> ILLEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false, false, false, false,
+                                                                                                  null, new JcrNodeType[] {root}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 true,
+                                                                                                                 JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                 new JcrNodeType[] {unstructured}),}),
+
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, nodeBTemplate)));
+
+    }
+
+    @Test
+    public void shouldAllowExtendingPropertyIfMultipleChanges() throws Exception {
+        /*
+        * testNode declares SV property testProperty
+        * testNodeB extends testNode with MV property testProperty with incompatible type -> LEGAL
+        * testNodeC extends testNode, testNodeB -> LEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          nameFor(TEST_PROPERTY_NAME),
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.LONG,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.DATE,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         true),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeCTemplate = new JcrNodeType(context, AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "C"), Arrays.asList(new JcrNodeType[] {ntTemplate,
+                                                        nodeBTemplate}), AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate, nodeCTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate,
+                                                                                           nodeBTemplate,
+                                                                                           nodeCTemplate)));
+    }
+
+    @Test
+    public void shouldAllowOverridingPropertyIfTypeNarrows() throws Exception {
+        /*
+        * testNode declares SV property testProperty of type UNDEFINED
+        * testNodeB extends testNode with SV property testProperty of type STRING -> LEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          nameFor(TEST_PROPERTY_NAME),
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.STRING,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.LONG,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, nodeBTemplate)));
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingPropertyIfTypeDoesNotNarrow() throws Exception {
+        /*
+        * testNode declares SV property testProperty of type DATE
+        * testNodeB extends testNode with SV property testProperty of type NAME -> ILLEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                          context,
+                                                                                                          null,
+                                                                                                          nameFor(TEST_PROPERTY_NAME),
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                          PropertyType.DATE,
+                                                                                                          AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                          false),}),
+                                     AbstractJcrNodeTypeSource.NOT_MIXIN, AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    AbstractJcrNodeTypeSource.NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         nameFor(TEST_PROPERTY_NAME),
+                                                                                                                         OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.NAME,
+                                                                                                                         AbstractJcrNodeTypeSource.NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, nodeBTemplate)));
+    }
+
+    @Test
+    public void shouldAllowOverridingChildNodeIfRequiredTypesNarrow() throws Exception {
+        /*
+        * testNode declares No-SNS childNode testChildNode requiring type nt:hierarchy
+        * testNodeB extends testNode with No-SNS childNode testChildNode requiring type nt:file -> LEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  null,
+                                                                                                  new JcrNodeType[] {hierarchyNode}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 true,
+                                                                                                                 null,
+                                                                                                                 new JcrNodeType[] {file}),}),
+
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, nodeBTemplate)));
+
+    }
+
+    @Test( expected = RepositoryException.class )
+    public void shouldNotAllowOverridingChildNodeIfRequiredTypesDoNotNarrow() throws Exception {
+        /*
+        * testNode declares No-SNS childNode testChildNode requiring type nt:hierarchy
+        * testNodeB extends testNode with No-SNS childNode testChildNode requiring type nt:base -> ILLEGAL
+        */
+        ntTemplate = new JcrNodeType(
+                                     context,
+                                     AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                     nameFor(TEST_TYPE_NAME),
+                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
+                                     AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                     Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                  context,
+                                                                                                  null,
+                                                                                                  nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                  OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  false,
+                                                                                                  null,
+                                                                                                  new JcrNodeType[] {hierarchyNode}),}),
+                                     AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                     AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeBTemplate = new JcrNodeType(
+                                                    context,
+                                                    AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
+                                                    nameFor(TEST_TYPE_NAME + "B"),
+                                                    Arrays.asList(new JcrNodeType[] {ntTemplate}),
+                                                    AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
+                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                 context,
+                                                                                                                 null,
+                                                                                                                 nameFor(TEST_CHILD_NODE_NAME),
+                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 false,
+                                                                                                                 true,
+                                                                                                                 null,
+                                                                                                                 new JcrNodeType[] {base}),}),
+
+                                                    AbstractJcrNodeTypeSource.NO_PROPERTIES, AbstractJcrNodeTypeSource.NOT_MIXIN,
+                                                    AbstractJcrNodeTypeSource.UNORDERABLE_CHILD_NODES);
+
+        List<JcrNodeType> templates = Arrays.asList(new JcrNodeType[] {ntTemplate, nodeBTemplate,});
+        compareTemplatesToNodeTypes(templates, repoTypeManager.registerNodeTypes(sourceFor(ntTemplate, nodeBTemplate)));
+
+    }
+
+    private void compareTemplatesToNodeTypes( List<JcrNodeType> templates,
+                                              List<JcrNodeType> nodeTypes ) {
+        assertThat(templates.size(), is(nodeTypes.size()));
+
+        for (int i = 0; i < nodeTypes.size(); i++) {
+            JcrNodeType jntt = templates.get(i);
+            compareTemplateToNodeType(jntt, null);
+            compareTemplateToNodeType(jntt, nodeTypes.get(i));
+        }
+    }
+
+    private void compareTemplateToNodeType( JcrNodeType template,
+                                            JcrNodeType nodeType ) {
+        Name nodeTypeName = nameFactory.create(template.getName());
+        if (nodeType == null) {
+            nodeType = repoTypeManager.getNodeType(nodeTypeName);
+            assertThat(nodeType.nodeTypeManager(), is(notNullValue()));
+        }
+
+        assertThat(nodeType, is(notNullValue()));
+        assertThat(nodeType.getName(), is(template.getName()));
+
+        assertThat(nodeType.getDeclaredSupertypes().length, is(template.getDeclaredSupertypes().length));
+        for (int i = 0; i < template.getDeclaredSupertypes().length; i++) {
+            assertThat(template.getDeclaredSupertypes()[i].getName(), is(nodeType.getDeclaredSupertypes()[i].getName()));
+        }
+        assertThat(template.isMixin(), is(nodeType.isMixin()));
+        assertThat(template.hasOrderableChildNodes(), is(nodeType.hasOrderableChildNodes()));
+
+        PropertyDefinition[] propertyDefs = nodeType.getDeclaredPropertyDefinitions();
+        List<JcrPropertyDefinition> propertyTemplates = Arrays.asList(template.getDeclaredPropertyDefinitions());
+
+        assertThat(propertyDefs.length, is(propertyTemplates.size()));
+        for (JcrPropertyDefinition pt : propertyTemplates) {
+            JcrPropertyDefinition propertyTemplate = pt;
+
+            PropertyDefinition matchingDefinition = null;
+            for (int i = 0; i < propertyDefs.length; i++) {
+                PropertyDefinition pd = propertyDefs[i];
+
+                String ptName = propertyTemplate.getName() == null ? JcrNodeType.RESIDUAL_ITEM_NAME : propertyTemplate.getName();
+                if (pd.getName().equals(ptName) && pd.getRequiredType() == propertyTemplate.getRequiredType()
+                    && pd.isMultiple() == propertyTemplate.isMultiple()) {
+                    matchingDefinition = pd;
+                    break;
+                }
+            }
+
+            comparePropertyTemplateToPropertyDefinition(propertyTemplate, (JcrPropertyDefinition)matchingDefinition);
+        }
+
+        NodeDefinition[] childNodeDefs = nodeType.getDeclaredChildNodeDefinitions();
+        List<JcrNodeDefinition> childNodeTemplates = Arrays.asList(template.getDeclaredChildNodeDefinitions());
+
+        assertThat(childNodeDefs.length, is(childNodeTemplates.size()));
+        for (JcrNodeDefinition nt : childNodeTemplates) {
+            JcrNodeDefinition childNodeTemplate = nt;
+
+            NodeDefinition matchingDefinition = null;
+            for (int i = 0; i < childNodeDefs.length; i++) {
+                JcrNodeDefinition nd = (JcrNodeDefinition)childNodeDefs[i];
+
+                String ntName = childNodeTemplate.getName() == null ? JcrNodeType.RESIDUAL_ITEM_NAME : childNodeTemplate.getName();
+                if (nd.getName().equals(ntName) && nd.allowsSameNameSiblings() == childNodeTemplate.allowsSameNameSiblings()) {
+
+                    boolean matchesOnRequiredTypes = childNodeTemplate.getRequiredPrimaryTypeNames()
+                                                                      .equals(nd.getRequiredPrimaryTypeNames());
+
+                    if (matchesOnRequiredTypes) {
+                        matchingDefinition = nd;
+                        break;
+                    }
+                }
+            }
+
+            compareNodeTemplateToNodeDefinition(childNodeTemplate, (JcrNodeDefinition)matchingDefinition);
+        }
+
+    }
+
+    private void comparePropertyTemplateToPropertyDefinition( JcrPropertyDefinition template,
+                                                              JcrPropertyDefinition definition ) {
+
+        assertThat(definition, is(notNullValue()));
+        assertThat(definition.getDeclaringNodeType(), is(notNullValue()));
+        // Had to match on name to even get to the definition
+        // assertThat(template.getName(), is(definition.getName()));
+
+        assertThat(template.getValueConstraints(), is(definition.getValueConstraints()));
+        assertThat(template.getOnParentVersion(), is(definition.getOnParentVersion()));
+        assertThat(template.getRequiredType(), is(definition.getRequiredType()));
+        assertThat(template.isAutoCreated(), is(definition.isAutoCreated()));
+        assertThat(template.isMandatory(), is(definition.isMandatory()));
+        assertThat(template.isMultiple(), is(definition.isMultiple()));
+        assertThat(template.isProtected(), is(definition.isProtected()));
+    }
+
+    private void compareNodeTemplateToNodeDefinition( JcrNodeDefinition template,
+                                                      JcrNodeDefinition definition ) {
+        assertThat(definition, is(notNullValue()));
+        assertThat(definition.getDeclaringNodeType(), is(notNullValue()));
+        // Had to match on name to even get to the definition
+        // assertThat(template.getName(), is(definition.getName()));
+
+        assertThat(template.getOnParentVersion(), is(definition.getOnParentVersion()));
+        assertThat(template.isAutoCreated(), is(definition.isAutoCreated()));
+        assertThat(template.isMandatory(), is(definition.isMandatory()));
+        assertThat(template.isProtected(), is(definition.isProtected()));
+
+        assertThat(template.with(repoTypeManager).getDefaultPrimaryType(), is(definition.getDefaultPrimaryType()));
+        assertThat(template.allowsSameNameSiblings(), is(definition.allowsSameNameSiblings()));
+
+        // assertThat(template.getRequiredPrimaryTypeNames(), is(definition.getRequiredPrimaryTypeNames()));
+
+    }
+}


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



From dna-commits at lists.jboss.org  Wed Apr 15 08:31:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 08:31:50 -0400
Subject: [dna-commits] DNA SVN: r824 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lu4HO-0003Ig-0c@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 08:31:49 -0400 (Wed, 15 Apr 2009)
New Revision: 824

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java
Log:
DNA-352 Version Tests in SerializationTest Fail Due to TCK Bug

Applied the patch that removes the version-related built-in node types, modifies the canAddMixin(...) method, modifies test that had coincidental dependency on one of the version built-in types.  This change was needed because of what appears to be incorrect behavior in the TCK unit tests.  For more details, see DNA-352.

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-04-14 20:06:32 UTC (rev 823)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-15 12:31:49 UTC (rev 824)
@@ -615,6 +615,17 @@
         CheckArg.isNotNull(mixinName, "mixinName");
         CheckArg.isNotZeroLength(mixinName, "mixinName");
 
+        /*
+         * Special workaround for SeralizationTest (and others) in JR TCK that incorrectly test whether a repository supports
+         * versioning by trying to add mix:versionable to a node.  The 1.0.1 says in section 4.11 that: 
+         * "A node is versionable if and only if it has been assigned the mixin type mix:versionable,
+         * otherwise it is nonversionable. Repositories that do not support versioning will simply not 
+         * provide this mixin type, whereas repositories that do support versioning must provide it."
+         */
+        if (JcrMixLexicon.VERSIONABLE.getString(namespaces()).equals(mixinName)) {
+            return false;
+        }
+
         JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
 
         if (this.isLocked()) {
@@ -1354,7 +1365,7 @@
      * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#checkout()
      */
-    public final void checkout() throws UnsupportedRepositoryOperationException  {
+    public final void checkout() throws UnsupportedRepositoryOperationException {
         throw new UnsupportedRepositoryOperationException();
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-14 20:06:32 UTC (rev 823)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-15 12:31:49 UTC (rev 824)
@@ -31,8 +31,6 @@
 import javax.jcr.Value;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.basic.BasicName;
 
 /**
  * {@link JcrNodeTypeSource} that provides built-in node types per the 1.0 specification.
@@ -56,7 +54,7 @@
         /*
          * These values get created without a session cache, as they aren't tied to any particular session.
          */
-        Value trueValue = new JcrValue(context.getValueFactories(), null, PropertyType.BOOLEAN, Boolean.TRUE);
+        // Value trueValue = new JcrValue(context.getValueFactories(), null, PropertyType.BOOLEAN, Boolean.TRUE);
         Value ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
 
         // Stubbing in child node and property definitions for now
@@ -458,118 +456,119 @@
                                                                                  PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
-        JcrNodeType version = new JcrNodeType(
-                                              context,
-                                              NO_NODE_TYPE_MANAGER,
-                                              JcrNtLexicon.VERSION,
-                                              Arrays.asList(new JcrNodeType[] {base, referenceable}),
-                                              NO_PRIMARY_ITEM_NAME,
-                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                           context,
-                                                                                                           null,
-                                                                                                           JcrLexicon.FROZEN_NODE,
-                                                                                                           OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                                                           false,
-                                                                                                           false,
-                                                                                                           true,
-                                                                                                           false,
-                                                                                                           null,
-                                                                                                           new JcrNodeType[] {frozenNode}),}),
-                                              Arrays.asList(new JcrPropertyDefinition[] {
-                                                  new JcrPropertyDefinition(context, null, JcrLexicon.CREATED,
-                                                                            OnParentVersionBehavior.ABORT.getJcrValue(), true,
-                                                                            true, true, NO_DEFAULT_VALUES, PropertyType.DATE,
-                                                                            NO_CONSTRAINTS, false),
-                                                  new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
-                                                                            OnParentVersionBehavior.ABORT.getJcrValue(), false,
-                                                                            false, true, NO_DEFAULT_VALUES,
-                                                                            PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                  new JcrPropertyDefinition(context, null, JcrLexicon.SUCCESSORS,
-                                                                            OnParentVersionBehavior.ABORT.getJcrValue(), false,
-                                                                            false, true, NO_DEFAULT_VALUES,
-                                                                            PropertyType.REFERENCE, NO_CONSTRAINTS, true),}),
-                                              NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        // JcrNodeType version = new JcrNodeType(
+        // context,
+        // NO_NODE_TYPE_MANAGER,
+        // JcrNtLexicon.VERSION,
+        // Arrays.asList(new JcrNodeType[] {base, referenceable}),
+        // NO_PRIMARY_ITEM_NAME,
+        // Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+        // context,
+        // null,
+        // JcrLexicon.FROZEN_NODE,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // false,
+        // false,
+        // true,
+        // false,
+        // null,
+        // new JcrNodeType[] {frozenNode}),}),
+        // Arrays.asList(new JcrPropertyDefinition[] {
+        // new JcrPropertyDefinition(context, null, JcrLexicon.CREATED,
+        // OnParentVersionBehavior.ABORT.getJcrValue(), true,
+        // true, true, NO_DEFAULT_VALUES, PropertyType.DATE,
+        // NO_CONSTRAINTS, false),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
+        // OnParentVersionBehavior.ABORT.getJcrValue(), false,
+        // false, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.SUCCESSORS,
+        // OnParentVersionBehavior.ABORT.getJcrValue(), false,
+        // false, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, true),}),
+        // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        //
+        // JcrNodeType versionLabels = new JcrNodeType(
+        // context,
+        // NO_NODE_TYPE_MANAGER,
+        // JcrNtLexicon.VERSION_LABELS,
+        // Arrays.asList(new JcrNodeType[] {base}),
+        // NO_PRIMARY_ITEM_NAME,
+        // NO_CHILD_NODES,
+        // Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+        // context,
+        // null,
+        // ALL_NODES,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // false,
+        // false,
+        // true,
+        // NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE,
+        // NO_CONSTRAINTS,
+        // false),}),
+        // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        //
+        // JcrNodeType versionHistory = new JcrNodeType(
+        // context,
+        // NO_NODE_TYPE_MANAGER,
+        // JcrNtLexicon.VERSION_HISTORY,
+        // Arrays.asList(new JcrNodeType[] {base, referenceable}),
+        // NO_PRIMARY_ITEM_NAME,
+        // Arrays.asList(new JcrNodeDefinition[] {
+        // new JcrNodeDefinition(context, null, JcrLexicon.ROOT_VERSION,
+        // OnParentVersionBehavior.ABORT.getJcrValue(), true,
+        // true, true, false, JcrNtLexicon.VERSION,
+        // new JcrNodeType[] {version}),
+        // new JcrNodeDefinition(context, null, JcrLexicon.VERSION_LABELS,
+        // OnParentVersionBehavior.ABORT.getJcrValue(), true,
+        // true, true, false, JcrNtLexicon.VERSION_LABELS,
+        // new JcrNodeType[] {versionLabels}),
+        // new JcrNodeDefinition(context, null, ALL_NODES,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // false, false, true, false, JcrNtLexicon.VERSION,
+        // new JcrNodeType[] {version}),}),
+        // Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+        // context,
+        // null,
+        // JcrLexicon.VERSIONABLE_UUID,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // true,
+        // true,
+        // true,
+        // NO_DEFAULT_VALUES,
+        // PropertyType.STRING,
+        // NO_CONSTRAINTS,
+        // false),}),
+        // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        //
+        // Name CHILD_VERSION_HISTORY = new BasicName(JcrLexicon.Namespace.URI, "childVersionHistory");
+        // JcrNodeType versionedChild = new JcrNodeType(
+        // context,
+        // NO_NODE_TYPE_MANAGER,
+        // JcrNtLexicon.VERSIONED_CHILD,
+        // Arrays.asList(new JcrNodeType[] {base}),
+        // NO_PRIMARY_ITEM_NAME,
+        // NO_CHILD_NODES,
+        // Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+        // context,
+        // null,
+        // CHILD_VERSION_HISTORY,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // true,
+        // true,
+        // true,
+        // NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE,
+        // NO_CONSTRAINTS,
+        // false),}),
+        // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType versionLabels = new JcrNodeType(
-                                                    context,
-                                                    NO_NODE_TYPE_MANAGER,
-                                                    JcrNtLexicon.VERSION_LABELS,
-                                                    Arrays.asList(new JcrNodeType[] {base}),
-                                                    NO_PRIMARY_ITEM_NAME,
-                                                    NO_CHILD_NODES,
-                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                         context,
-                                                                                                                         null,
-                                                                                                                         ALL_NODES,
-                                                                                                                         OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                                                                         false,
-                                                                                                                         false,
-                                                                                                                         true,
-                                                                                                                         NO_DEFAULT_VALUES,
-                                                                                                                         PropertyType.REFERENCE,
-                                                                                                                         NO_CONSTRAINTS,
-                                                                                                                         false),}),
-                                                    NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+        // Disabling version-related types until DNA supports versioning, as per section 4.11 of the 1.0.1 specification
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder, frozenNode,
+            hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType /*, version, versionHistory,
+                   versionLabels, versionedChild */}));
 
-        JcrNodeType versionHistory = new JcrNodeType(
-                                                     context,
-                                                     NO_NODE_TYPE_MANAGER,
-                                                     JcrNtLexicon.VERSION_HISTORY,
-                                                     Arrays.asList(new JcrNodeType[] {base, referenceable}),
-                                                     NO_PRIMARY_ITEM_NAME,
-                                                     Arrays.asList(new JcrNodeDefinition[] {
-                                                         new JcrNodeDefinition(context, null, JcrLexicon.ROOT_VERSION,
-                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
-                                                                               true, true, false, JcrNtLexicon.VERSION,
-                                                                               new JcrNodeType[] {version}),
-                                                         new JcrNodeDefinition(context, null, JcrLexicon.VERSION_LABELS,
-                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
-                                                                               true, true, false, JcrNtLexicon.VERSION_LABELS,
-                                                                               new JcrNodeType[] {versionLabels}),
-                                                         new JcrNodeDefinition(context, null, ALL_NODES,
-                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                               false, false, true, false, JcrNtLexicon.VERSION,
-                                                                               new JcrNodeType[] {version}),}),
-                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                          context,
-                                                                                                                          null,
-                                                                                                                          JcrLexicon.VERSIONABLE_UUID,
-                                                                                                                          OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                                                                          true,
-                                                                                                                          true,
-                                                                                                                          true,
-                                                                                                                          NO_DEFAULT_VALUES,
-                                                                                                                          PropertyType.STRING,
-                                                                                                                          NO_CONSTRAINTS,
-                                                                                                                          false),}),
-                                                     NOT_MIXIN, UNORDERABLE_CHILD_NODES);
-
-        Name CHILD_VERSION_HISTORY = new BasicName(JcrLexicon.Namespace.URI, "childVersionHistory");
-        JcrNodeType versionedChild = new JcrNodeType(
-                                                     context,
-                                                     NO_NODE_TYPE_MANAGER,
-                                                     JcrNtLexicon.VERSIONED_CHILD,
-                                                     Arrays.asList(new JcrNodeType[] {base}),
-                                                     NO_PRIMARY_ITEM_NAME,
-                                                     NO_CHILD_NODES,
-                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                          context,
-                                                                                                                          null,
-                                                                                                                          CHILD_VERSION_HISTORY,
-                                                                                                                          OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                                                                          true,
-                                                                                                                          true,
-                                                                                                                          true,
-                                                                                                                          NO_DEFAULT_VALUES,
-                                                                                                                          PropertyType.REFERENCE,
-                                                                                                                          NO_CONSTRAINTS,
-                                                                                                                          false),}),
-                                                     NOT_MIXIN, UNORDERABLE_CHILD_NODES);
-
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder,
-            frozenNode, hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType, version,
-            versionHistory, versionLabels, versionedChild}));
-
         JcrNodeType lockable = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES,
                                                NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.LOCK_IS_DEEP,
@@ -582,37 +581,38 @@
                                                                              NO_CONSTRAINTS, false)}), IS_A_MIXIN,
                                                UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType versionable = new JcrNodeType(
-                                                  context,
-                                                  NO_NODE_TYPE_MANAGER,
-                                                  JcrMixLexicon.VERSIONABLE,
-                                                  Arrays.asList(new JcrNodeType[] {referenceable}),
-                                                  NO_PRIMARY_ITEM_NAME,
-                                                  NO_CHILD_NODES,
-                                                  Arrays.asList(new JcrPropertyDefinition[] {
-                                                      new JcrPropertyDefinition(context, null, JcrLexicon.BASE_VERSION,
-                                                                                OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                false, true, true, NO_DEFAULT_VALUES,
-                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, false),
-                                                      new JcrPropertyDefinition(context, null, JcrLexicon.IS_CHECKED_OUT,
-                                                                                OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                true, true, true, new Value[] {trueValue},
-                                                                                PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
-                                                      new JcrPropertyDefinition(context, null, JcrLexicon.MERGE_FAILED,
-                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
-                                                                                false, false, true, NO_DEFAULT_VALUES,
-                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                      new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
-                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                false, true, true, NO_DEFAULT_VALUES,
-                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                      new JcrPropertyDefinition(context, null, JcrLexicon.VERSION_HISTORY,
-                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                false, true, true, NO_DEFAULT_VALUES,
-                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),
-                                                  IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+        // JcrNodeType versionable = new JcrNodeType(
+        // context,
+        // NO_NODE_TYPE_MANAGER,
+        // JcrMixLexicon.VERSIONABLE,
+        // Arrays.asList(new JcrNodeType[] {referenceable}),
+        // NO_PRIMARY_ITEM_NAME,
+        // NO_CHILD_NODES,
+        // Arrays.asList(new JcrPropertyDefinition[] {
+        // new JcrPropertyDefinition(context, null, JcrLexicon.BASE_VERSION,
+        // OnParentVersionBehavior.IGNORE.getJcrValue(),
+        // false, true, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, false),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.IS_CHECKED_OUT,
+        // OnParentVersionBehavior.IGNORE.getJcrValue(),
+        // true, true, true, new Value[] {trueValue},
+        // PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.MERGE_FAILED,
+        // OnParentVersionBehavior.ABORT.getJcrValue(),
+        // false, false, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
+        // OnParentVersionBehavior.COPY.getJcrValue(),
+        // false, true, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+        // new JcrPropertyDefinition(context, null, JcrLexicon.VERSION_HISTORY,
+        // OnParentVersionBehavior.COPY.getJcrValue(),
+        // false, true, true, NO_DEFAULT_VALUES,
+        // PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),
+        // IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {lockable, referenceable, versionable}));
+        // Removing lockable as per section 4.11 of the JCR 1.0.1 specification
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {lockable, referenceable /*, versionable */}));
 
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java	2009-04-14 20:06:32 UTC (rev 823)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TypeRegistrationTest.java	2009-04-15 12:31:49 UTC (rev 824)
@@ -55,7 +55,6 @@
     private JcrNodeType referenceable;
     private JcrNodeType unstructured;
     private JcrNodeType root;
-    private JcrNodeType versionHistory;
     private JcrNodeType hierarchyNode;
     private JcrNodeType file;
 
@@ -74,7 +73,6 @@
         referenceable = repoTypeManager.getNodeType(JcrMixLexicon.REFERENCEABLE);
         unstructured = repoTypeManager.getNodeType(JcrNtLexicon.UNSTRUCTURED);
         root = repoTypeManager.getNodeType(DnaLexicon.ROOT);
-        versionHistory = repoTypeManager.getNodeType(JcrNtLexicon.VERSION_HISTORY);
         hierarchyNode = repoTypeManager.getNodeType(JcrNtLexicon.HIERARCHY_NODE);
         file = repoTypeManager.getNodeType(JcrNtLexicon.FILE);
     }
@@ -548,12 +546,12 @@
                                      context,
                                      AbstractJcrNodeTypeSource.NO_NODE_TYPE_MANAGER,
                                      nameFor(TEST_TYPE_NAME),
-                                     Arrays.asList(new JcrNodeType[] {versionHistory}),
+                                     Arrays.asList(new JcrNodeType[] {file}),
                                      AbstractJcrNodeTypeSource.NO_PRIMARY_ITEM_NAME,
                                      Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                   context,
                                                                                                   null,
-                                                                                                  JcrLexicon.ROOT_VERSION,
+                                                                                                  JcrLexicon.CONTENT,
                                                                                                   OnParentVersionBehavior.VERSION.getJcrValue(),
                                                                                                   false, false, false, false,
                                                                                                   JcrNtLexicon.BASE,



From dna-commits at lists.jboss.org  Wed Apr 15 08:35:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 08:35:50 -0400
Subject: [dna-commits] DNA SVN: r825 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lu4LG-0003oq-Ch@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 08:35:50 -0400 (Wed, 15 Apr 2009)
New Revision: 825

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
Log:
DNA-353 JcrSession.exportSystemView Does Not Handle skipBinary = true Properly

Applied the patch that corrects the behavior of JcrSession.exportSystemView.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	2009-04-15 12:31:49 UTC (rev 824)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	2009-04-15 12:35:50 UTC (rev 825)
@@ -1,3 +1,26 @@
+/*
+ * 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;
 
 import java.io.IOException;
@@ -80,7 +103,7 @@
             Property specialProperty = ((AbstractJcrNode)node).getProperty(specialPropertyName);
 
             if (specialProperty != null) {
-                emitProperty(specialProperty, contentHandler);
+                emitProperty(specialProperty, contentHandler, skipBinary);
             }
         }
 
@@ -119,11 +142,7 @@
             return;
         }
 
-        if (skipBinary && PropertyType.BINARY == prop.getType()) {
-            return;
-        }
-
-        emitProperty(property, contentHandler);
+        emitProperty(property, contentHandler, skipBinary);
     }
 
     /**
@@ -131,11 +150,13 @@
      * 
      * @param property the property to be exported
      * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML document is created.
+     * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
      * @throws SAXException if an exception occurs during generation of the XML document
      * @throws RepositoryException if an exception occurs accessing the content repository
      */
     private void emitProperty( Property property,
-                               ContentHandler contentHandler ) throws RepositoryException, SAXException {
+                               ContentHandler contentHandler,
+                               boolean skipBinary ) throws RepositoryException, SAXException {
         assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
                                                          + " on non-DNA property";
 
@@ -164,10 +185,10 @@
             Value[] values = prop.getValues();
             for (int i = 0; i < values.length; i++) {
 
-                emitValue(values[i], contentHandler, property.getType());
+                emitValue(values[i], contentHandler, property.getType(), skipBinary);
             }
         } else {
-            emitValue(property.getValue(), contentHandler, property.getType());
+            emitValue(property.getValue(), contentHandler, property.getType(), skipBinary);
         }
 
         // end the sv:property element
@@ -180,30 +201,35 @@
      * @param value the value to be exported
      * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML document is created.
      * @param propertyType the {@link PropertyType} for the given value
+     * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
      * @throws SAXException if an exception occurs during generation of the XML document
      * @throws RepositoryException if an exception occurs accessing the content repository
      */
     private void emitValue( Value value,
                             ContentHandler contentHandler,
-                            int propertyType ) throws RepositoryException, SAXException {
+                            int propertyType,
+                            boolean skipBinary ) throws RepositoryException, SAXException {
 
         if (PropertyType.BINARY == propertyType) {
             startElement(contentHandler, JcrSvLexicon.VALUE, null);
 
-            byte[] bytes = new byte[BASE_64_BUFFER_SIZE];
-            int len;
+            // Per section 6.5 of the 1.0.1 spec, we need to emit one empty-value tag for each value if the property is
+            // multi-valued and skipBinary is true
+            if (!skipBinary) {
+                byte[] bytes = new byte[BASE_64_BUFFER_SIZE];
+                int len;
 
-            try {
-                InputStream stream = new Base64.InputStream(value.getStream(), Base64.ENCODE | Base64.URL_SAFE
-                                                                               | Base64.DONT_BREAK_LINES);
+                try {
+                    InputStream stream = new Base64.InputStream(value.getStream(), Base64.ENCODE | Base64.URL_SAFE
+                                                                                   | Base64.DONT_BREAK_LINES);
 
-                while (-1 != (len = stream.read(bytes))) {
-                    contentHandler.characters(new String(bytes, 0, len).toCharArray(), 0, len);
+                    while (-1 != (len = stream.read(bytes))) {
+                        contentHandler.characters(new String(bytes, 0, len).toCharArray(), 0, len);
+                    }
+                } catch (IOException ioe) {
+                    throw new RepositoryException(ioe);
                 }
-            } catch (IOException ioe) {
-                throw new RepositoryException(ioe);
             }
-
             endElement(contentHandler, JcrSvLexicon.VALUE);
         } else {
             String s = value.getString();



From dna-commits at lists.jboss.org  Wed Apr 15 08:50:45 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 08:50:45 -0400
Subject: [dna-commits] DNA SVN: r826 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lu4Zh-0007ti-En@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 08:50:45 -0400 (Wed, 15 Apr 2009)
New Revision: 826

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-354 JcrSession.hasPendingChanges Is Not Implemented

Applied the patch (with changes) that implements this method by checking the SessionCache for unsaved (pending) changes.  The patch had checked the 'changes' map to determine whether there were changes, but this misses deletes.  Added a method to SessionCache that determines whether there are any pending changes, and implemented this method by checking whether there are any pending operations.

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-04-15 12:35:50 UTC (rev 825)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-15 12:50:45 UTC (rev 826)
@@ -571,11 +571,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @return false
      * @see javax.jcr.Session#hasPendingChanges()
      */
     public boolean hasPendingChanges() {
-        return false;
+        return cache.hasPendingChanges();
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-15 12:35:50 UTC (rev 825)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-15 12:50:45 UTC (rev 826)
@@ -218,6 +218,15 @@
     }
 
     /**
+     * Returns whether the session cache has any pending changes that need to be executed.
+     * 
+     * @return true if there are pending changes, or false if there is currently no changes
+     */
+    boolean hasPendingChanges() {
+        return operations.isExecuteRequired();
+    }
+
+    /**
      * Save any changes that have been accumulated by this session.
      * 
      * @throws RepositoryException if any error resulting while saving the changes to the repository
@@ -785,8 +794,8 @@
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING,
-                                                  definition.getId().getString());
+                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
+                                                                                                                 .getString());
                     setProperty(DnaLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
@@ -886,7 +895,7 @@
                 if (desiredUuid == null) {
                     desiredUuid = UUID.randomUUID();
                 }
-                
+
                 // We know that this property is single-valued
                 JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, desiredUuid.toString());
                 PropertyDefinition propertyDefinition = nodeTypes().findPropertyDefinition(primaryTypeName,
@@ -898,7 +907,8 @@
                 PropertyId propId = new PropertyId(desiredUuid, JcrLexicon.UUID);
                 JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
                 org.jboss.dna.graph.property.Property uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
-                PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
+                PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty,
+                                                         defn.isMultiple());
                 properties.put(JcrLexicon.UUID, propInfo);
             }
 
@@ -936,7 +946,10 @@
             // ---------------------------------------
             // Now record the changes to the store ...
             // ---------------------------------------
-            Graph.Create<Graph.Batch> create = operations.createUnder(currentLocation).nodeNamed(name).with(desiredUuid).with(primaryTypeProp);
+            Graph.Create<Graph.Batch> create = operations.createUnder(currentLocation)
+                                                         .nodeNamed(name)
+                                                         .with(desiredUuid)
+                                                         .with(primaryTypeProp);
             if (nodeDefnDefn != null) {
                 create = create.with(nodeDefinitionProp);
             }



From dna-commits at lists.jboss.org  Wed Apr 15 10:14:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 10:14:34 -0400
Subject: [dna-commits] DNA SVN: r827 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/request and 1
	other directories.
Message-ID: <E1Lu5so-0002bD-6A@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 10:14:33 -0400 (Wed, 15 Apr 2009)
New Revision: 827

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-354 JcrSession.hasPendingChanges Is Not Implemented

Previous changes that were made in addition to the patch did not properly implement the hasPendingChanges method.  When doing this, it was discovered that SessionCache was incorrectly creating it's Graph.Batch instance to not use the SessionCache's request builder (which used the cache's list of requests, allowing SessionCache.save(UUID) to play with the pending requests to save only those that affect the branch below the supplied UUID).  

This commit corrects the implementation, and all tests now pass.

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-04-15 12:50:45 UTC (rev 826)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-15 14:14:33 UTC (rev 827)
@@ -1899,7 +1899,7 @@
      * @see Results
      */
     public Batch batch() {
-        return new Batch(null);
+        return new Batch(new BatchRequestBuilder());
     }
 
     /**
@@ -1933,7 +1933,8 @@
         protected boolean executed = false;
 
         /*package*/Batch( BatchRequestBuilder builder ) {
-            this.requestQueue = builder != null ? builder : new BatchRequestBuilder();
+            assert builder != null;
+            this.requestQueue = builder;
             this.workspaceName = Graph.this.getCurrentWorkspaceName();
             this.nextRequests = new BatchConjunction() {
                 public Batch and() {
@@ -1961,7 +1962,7 @@
          * @return true if there are some requests in this batch that need to be executed, or false execution is not required
          */
         public boolean isExecuteRequired() {
-            return !executed || requestQueue.hasRequests();
+            return !executed && requestQueue.hasRequests();
         }
 
         /**

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-04-15 12:50:45 UTC (rev 826)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-04-15 14:14:33 UTC (rev 827)
@@ -72,6 +72,7 @@
         if (pendingRequest != null) {
             // There's a pending request, we need to build it ...
             add(pendingRequest.toRequest());
+            pendingRequest = null;
         }
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-15 12:50:45 UTC (rev 826)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-15 14:14:33 UTC (rev 827)
@@ -186,7 +186,7 @@
         // Create the batch operations ...
         this.requests = new LinkedList<Request>();
         this.requestBuilder = new BatchRequestBuilder(this.requests);
-        this.operations = this.store.batch();
+        this.operations = this.store.batch(this.requestBuilder);
     }
 
     JcrSession session() {



From dna-commits at lists.jboss.org  Wed Apr 15 10:28:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 10:28:30 -0400
Subject: [dna-commits] DNA SVN: r828 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lu66I-0004AZ-DX@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 10:28:30 -0400 (Wed, 15 Apr 2009)
New Revision: 828

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
Log:
DNA-355 Wrong Exception Thrown When Creating Reference Value from Non-Referenceable Node

Applied the patch, which corrects JcrSession.getValueFactories.createValue(Node) witto throw a RepositoryException (rather than an UnsupportedRepositoryOperationException) when a non-referenceable node is given as an argument, and which modifies some unit tests as needed.

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-04-15 14:14:33 UTC (rev 827)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-15 14:28:30 UTC (rev 828)
@@ -462,6 +462,9 @@
             }
 
             public Value createValue( Node value ) throws RepositoryException {
+                if (!value.isNodeType(JcrMixLexicon.REFERENCEABLE.getString(JcrSession.this.namespaces()))) {
+                    throw new RepositoryException();
+                }
                 String uuid = valueFactories.getStringFactory().create(value.getUUID());
                 return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, uuid);
             }

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-04-15 14:14:33 UTC (rev 827)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-04-15 14:28:30 UTC (rev 828)
@@ -336,11 +336,20 @@
         assertThat(factory.createValue(0L), notNullValue());
         Node node = Mockito.mock(Node.class);
         stub(node.getUUID()).toReturn(UUID.randomUUID().toString());
+        stub(node.isNodeType("mix:referenceable")).toReturn(true);
         assertThat(factory.createValue(node), notNullValue());
         assertThat(factory.createValue(""), notNullValue());
         assertThat(factory.createValue("", PropertyType.BINARY), notNullValue());
     }
 
+    @Test (expected=RepositoryException.class)
+    public void shouldNotCreateValueForNonReferenceableNode() throws Exception {
+        ValueFactory factory = session.getValueFactory();
+        Node node = Mockito.mock(Node.class);
+        stub(node.getUUID()).toReturn(UUID.randomUUID().toString());
+        factory.createValue(node);
+    }
+
     @Test
     public void shouldNotHavePendingChanges() throws Exception {
         assertThat(session.hasPendingChanges(), is(false));



From dna-commits at lists.jboss.org  Wed Apr 15 18:42:18 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 15 Apr 2009 18:42:18 -0400
Subject: [dna-commits] DNA SVN: r829 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/io and 7 other
	directories.
Message-ID: <E1LuDoA-0000T4-OR@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-15 18:42:18 -0400 (Wed, 15 Apr 2009)
New Revision: 829

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphImporter.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-358 Break the XmlHandler.Destination into an area where it can be reused by other importers

Pulled the Destination interface and the current implementations into a new 'org.jboss.dna.graph.io' package.  Move the GraphImporter class and it's test case into the same package.

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-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -50,6 +50,7 @@
 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.io.GraphImporter;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java	2009-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -1,278 +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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.List;
-import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.common.text.TextDecoder;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.connector.RepositorySource;
-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.Path;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.xml.XmlHandler;
-import org.jboss.dna.graph.xml.XmlHandler.Destination;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-/**
- * @author Randall Hauch
- * @author John Verhaeg
- */
-public class GraphImporter {
-
-    private final Graph graph;
-
-    public GraphImporter( Graph graph ) {
-        CheckArg.isNotNull(graph, "graph");
-        this.graph = graph;
-    }
-
-    /**
-     * Get the context in which the importer will be executed.
-     * 
-     * @return the execution context; never null
-     */
-    public ExecutionContext getContext() {
-        return this.graph.getContext();
-    }
-
-    /**
-     * The graph that this importer uses.
-     * 
-     * @return the graph; never null
-     */
-    public Graph getGraph() {
-        return graph;
-    }
-
-    /**
-     * Read the content from the supplied URI and import into the repository at the supplied location.
-     * 
-     * @param uri the URI where the importer can read the content that is to be imported
-     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
-     *        be null
-     * @return the batch of requests for creating the graph content that represents the imported content
-     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
-     * @throws IOException if there is a problem reading the content
-     * @throws SAXException if there is a problem with the SAX Parser
-     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
-     *         source}
-     */
-    public Graph.Batch importXml( URI uri,
-                                  Location location ) throws IOException, SAXException, RepositorySourceException {
-        return importXml(uri, location, false);
-    }
-
-    /**
-     * Read the content from the supplied URI and import into the repository at the supplied location.
-     * 
-     * @param uri the URI where the importer can read the content that is to be imported
-     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
-     *        be null
-     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
-     * @return the batch of requests for creating the graph content that represents the imported content
-     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
-     * @throws IOException if there is a problem reading the content
-     * @throws SAXException if there is a problem with the SAX Parser
-     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
-     *         source}
-     */
-    public Graph.Batch importXml( URI uri,
-                                  Location location,
-                                  boolean skip ) throws IOException, SAXException, RepositorySourceException {
-        CheckArg.isNotNull(uri, "uri");
-        CheckArg.isNotNull(location, "location");
-        CheckArg.isNotNull(location.getPath(), "location.getPath()");
-        InputStream stream = null;
-        try {
-            stream = uri.toURL().openStream();
-            return importXml(stream, location, skip);
-        } finally {
-            if (stream != null) stream.close();
-        }
-    }
-
-    /**
-     * Read the content from the supplied URI and import into the repository at the supplied location. This method does <i>not</i>
-     * close the stream.
-     * 
-     * @param stream the stream containing the content to be imported
-     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
-     *        be null
-     * @return the batch of requests for creating the graph content that represents the imported content
-     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
-     * @throws IOException if there is a problem reading the content
-     * @throws SAXException if there is a problem with the SAX Parser
-     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
-     *         source}
-     */
-    public Graph.Batch importXml( InputStream stream,
-                                  Location location ) throws IOException, SAXException, RepositorySourceException {
-        return importXml(stream, location, false);
-    }
-
-    /**
-     * Read the content from the supplied URI and import into the repository at the supplied location. This method does <i>not</i>
-     * close the stream.
-     * 
-     * @param stream the stream containing the content to be imported
-     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
-     *        be null
-     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
-     * @return the batch of requests for creating the graph content that represents the imported content
-     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
-     * @throws IOException if there is a problem reading the content
-     * @throws SAXException if there is a problem with the SAX Parser
-     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
-     *         source}
-     */
-    public Graph.Batch importXml( InputStream stream,
-                                  Location location,
-                                  boolean skip ) throws IOException, SAXException, RepositorySourceException {
-        CheckArg.isNotNull(stream, "uri");
-        CheckArg.isNotNull(location, "location");
-        CheckArg.isNotNull(location.getPath(), "location.getPath()");
-
-        // Create the destination for the XmlHandler ...
-        Graph.Batch batch = graph.batch();
-        XmlHandler.Destination destination = new CreateOnGraphInBatch(batch);
-
-        // Determine where the content is to be placed ...
-        Path parentPath = location.getPath();
-        Name nameAttribute = JcrLexicon.NAME;
-        Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
-        Name typeAttributeValue = null;
-        NamespaceRegistry reg = graph.getContext().getNamespaceRegistry();
-        if (reg.isRegisteredNamespaceUri(JcrNtLexicon.Namespace.URI)) {
-            typeAttributeValue = JcrNtLexicon.UNSTRUCTURED;
-        }
-
-        TextDecoder decoder = null;
-        XmlHandler.AttributeScoping scoping = XmlHandler.AttributeScoping.USE_DEFAULT_NAMESPACE;
-        XmlHandler handler = new XmlHandler(destination, skip, parentPath, decoder, nameAttribute, typeAttribute,
-                                            typeAttributeValue, scoping);
-        XMLReader reader = XMLReaderFactory.createXMLReader();
-        reader.setContentHandler(handler);
-        reader.setErrorHandler(handler);
-        reader.parse(new InputSource(stream));
-        if (stream != null) stream.close();
-        return batch;
-    }
-
-    /**
-     * Return an {@link XmlHandler} that can be used to import content directly into the supplied location. The operations
-     * resulting from the {@link XmlHandler} operations are batched until the {@link XmlHandler#endDocument()} is called, at which
-     * point all enqueued operations are submitted to the graph.
-     * 
-     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
-     *        be null
-     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
-     * @return the {@link XmlHandler} that can be used to import content
-     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
-     */
-    public XmlHandler getHandlerForImportingXml( Location location,
-                                                 boolean skip ) {
-        CheckArg.isNotNull(location, "location");
-        CheckArg.isNotNull(location.getPath(), "location.getPath()");
-
-        // Create the destination for the XmlHandler ...
-        Graph.Batch batch = graph.batch();
-        XmlHandler.Destination destination = new SubmitToGraphInBatch(batch);
-
-        // Determine where the content is to be placed ...
-        Path parentPath = location.getPath();
-        Name nameAttribute = JcrLexicon.NAME;
-        Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
-        Name typeAttributeValue = null;
-        NamespaceRegistry reg = graph.getContext().getNamespaceRegistry();
-        if (reg.isRegisteredNamespaceUri(JcrNtLexicon.Namespace.URI)) {
-            typeAttributeValue = JcrNtLexicon.UNSTRUCTURED;
-        }
-
-        TextDecoder decoder = null;
-        XmlHandler.AttributeScoping scoping = XmlHandler.AttributeScoping.USE_DEFAULT_NAMESPACE;
-        XmlHandler handler = new XmlHandler(destination, skip, parentPath, decoder, nameAttribute, typeAttribute,
-                                            typeAttributeValue, scoping);
-        return handler;
-    }
-
-    @NotThreadSafe
-    protected static class CreateOnGraphInBatch implements Destination {
-        protected final Graph.Batch batch;
-
-        protected CreateOnGraphInBatch( Graph.Batch batch ) {
-            assert batch != null;
-            this.batch = batch;
-        }
-
-        public ExecutionContext getExecutionContext() {
-            return batch.getGraph().getContext();
-        }
-
-        public void create( Path path,
-                            List<Property> properties ) {
-            assert properties != null;
-            if (properties.isEmpty()) {
-                batch.create(path).and();
-            } else {
-                batch.create(path, properties).and();
-            }
-        }
-
-        public void create( Path path,
-                            Property firstProperty,
-                            Property... additionalProperties ) {
-            if (firstProperty == null) {
-                batch.create(path).and();
-            } else {
-                batch.create(path, firstProperty, additionalProperties).and();
-            }
-        }
-
-        public void submit() {
-        }
-    }
-
-    @NotThreadSafe
-    protected final static class SubmitToGraphInBatch extends CreateOnGraphInBatch {
-        protected SubmitToGraphInBatch( Graph.Batch batch ) {
-            super(batch);
-        }
-
-        @Override
-        public void submit() {
-            super.submit();
-            batch.execute();
-        }
-    }
-
-}

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * 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.List;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * Interface used internally as the destination for the requests. This is used to abstract whether the requests should be
+ * submitted immediately or in a single batch.
+ * 
+ * @author Randall Hauch
+ */
+ at NotThreadSafe
+public interface Destination {
+
+    /**
+     * Obtain the execution context of the destination.
+     * 
+     * @return the destination's execution context
+     */
+    public ExecutionContext getExecutionContext();
+
+    /**
+     * Create a node at the supplied path and with the supplied attributes. The path will be absolute.
+     * 
+     * @param path the absolute path of the node
+     * @param properties the properties for the node; never null, but may be empty if there are no properties
+     */
+    public void create( Path path,
+                        List<Property> properties );
+
+    /**
+     * Create a node at the supplied path and with the supplied attributes. The path will be absolute.
+     * 
+     * @param path the absolute path of the node
+     * @param firstProperty the first property
+     * @param additionalProperties the remaining properties for the node
+     */
+    public void create( Path path,
+                        Property firstProperty,
+                        Property... additionalProperties );
+
+    /**
+     * Signal to this destination that any enqueued create requests should be submitted. Usually this happens at the end of the
+     * document parsing, but an implementer must allow for it to be called multiple times and anytime during parsing.
+     */
+    public void submit();
+}


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

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ *
+ * 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.List;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Graph.Batch;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * A {@link Destination} that makes the changes to a graph via a {@link Batch}.
+ */
+ at NotThreadSafe
+public class GraphBatchDestination implements Destination {
+    protected final Graph.Batch batch;
+    protected final boolean ignoreSubmit;
+
+    /**
+     * Create a new instance that will use the specified batch. When {@link #submit()} is called, the batch will be
+     * {@link Batch#execute() executed}.
+     * 
+     * @param batch the batch
+     * @throws IllegalArgumentException if the batch is null
+     */
+    public GraphBatchDestination( Graph.Batch batch ) {
+        this(batch, false);
+    }
+
+    /**
+     * Create a new instance that will use the specified batch. If {@code ignoreSubmit} is true, then {@link #submit()} does
+     * nothing (and the batch must be executed}; otherwise, {@link #submit()} immediately calls the {@link Batch#execute()
+     * executed}.
+     * 
+     * @param batch the batch
+     * @param ignoreSubmit true if the {@link #submit()} method should be ignored, or false otherwise
+     * @throws IllegalArgumentException if the batch is null
+     */
+    public GraphBatchDestination( Graph.Batch batch,
+                                  boolean ignoreSubmit ) {
+        assert batch != null;
+        this.batch = batch;
+        this.ignoreSubmit = ignoreSubmit;
+    }
+
+    /**
+     * Return whether this instance is ignoring calls to {@link #submit()}.
+     * 
+     * @return ignoreSubmit
+     */
+    public boolean isSubmitIgnored() {
+        return ignoreSubmit;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.io.Destination#getExecutionContext()
+     */
+    public ExecutionContext getExecutionContext() {
+        return batch.getGraph().getContext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.io.Destination#create(org.jboss.dna.graph.property.Path, java.util.List)
+     */
+    public void create( Path path,
+                        List<Property> properties ) {
+        assert properties != null;
+        if (properties.isEmpty()) {
+            batch.create(path).and();
+        } else {
+            batch.create(path, properties).and();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.io.Destination#create(org.jboss.dna.graph.property.Path, org.jboss.dna.graph.property.Property,
+     *      org.jboss.dna.graph.property.Property[])
+     */
+    public void create( Path path,
+                        Property firstProperty,
+                        Property... additionalProperties ) {
+        if (firstProperty == null) {
+            batch.create(path).and();
+        } else {
+            batch.create(path, firstProperty, additionalProperties).and();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.io.Destination#submit()
+     */
+    public void submit() {
+        // Execute only if we're not ignoring submits ...
+        if (!this.ignoreSubmit && !batch.hasExecuted()) batch.execute();
+
+    }
+}


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

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphImporter.java (from rev 809, trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphImporter.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphImporter.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphImporter.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -0,0 +1,229 @@
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import org.jboss.dna.common.text.TextDecoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositorySource;
+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.Path;
+import org.jboss.dna.graph.xml.XmlHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * @author Randall Hauch
+ * @author John Verhaeg
+ */
+public class GraphImporter {
+
+    private final Graph graph;
+
+    public GraphImporter( Graph graph ) {
+        CheckArg.isNotNull(graph, "graph");
+        this.graph = graph;
+    }
+
+    /**
+     * Get the context in which the importer will be executed.
+     * 
+     * @return the execution context; never null
+     */
+    public ExecutionContext getContext() {
+        return this.graph.getContext();
+    }
+
+    /**
+     * The graph that this importer uses.
+     * 
+     * @return the graph; never null
+     */
+    public Graph getGraph() {
+        return graph;
+    }
+
+    /**
+     * Read the content from the supplied URI and import into the repository at the supplied location.
+     * 
+     * @param uri the URI where the importer can read the content that is to be imported
+     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
+     *        be null
+     * @return the batch of requests for creating the graph content that represents the imported content
+     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
+     * @throws IOException if there is a problem reading the content
+     * @throws SAXException if there is a problem with the SAX Parser
+     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
+     *         source}
+     */
+    public Graph.Batch importXml( URI uri,
+                                  Location location ) throws IOException, SAXException, RepositorySourceException {
+        return importXml(uri, location, false);
+    }
+
+    /**
+     * Read the content from the supplied URI and import into the repository at the supplied location.
+     * 
+     * @param uri the URI where the importer can read the content that is to be imported
+     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
+     *        be null
+     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
+     * @return the batch of requests for creating the graph content that represents the imported content
+     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
+     * @throws IOException if there is a problem reading the content
+     * @throws SAXException if there is a problem with the SAX Parser
+     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
+     *         source}
+     */
+    public Graph.Batch importXml( URI uri,
+                                  Location location,
+                                  boolean skip ) throws IOException, SAXException, RepositorySourceException {
+        CheckArg.isNotNull(uri, "uri");
+        CheckArg.isNotNull(location, "location");
+        CheckArg.isNotNull(location.getPath(), "location.getPath()");
+        InputStream stream = null;
+        try {
+            stream = uri.toURL().openStream();
+            return importXml(stream, location, skip);
+        } finally {
+            if (stream != null) stream.close();
+        }
+    }
+
+    /**
+     * Read the content from the supplied URI and import into the repository at the supplied location. This method does <i>not</i>
+     * close the stream.
+     * 
+     * @param stream the stream containing the content to be imported
+     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
+     *        be null
+     * @return the batch of requests for creating the graph content that represents the imported content
+     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
+     * @throws IOException if there is a problem reading the content
+     * @throws SAXException if there is a problem with the SAX Parser
+     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
+     *         source}
+     */
+    public Graph.Batch importXml( InputStream stream,
+                                  Location location ) throws IOException, SAXException, RepositorySourceException {
+        return importXml(stream, location, false);
+    }
+
+    /**
+     * Read the content from the supplied URI and import into the repository at the supplied location. This method does <i>not</i>
+     * close the stream.
+     * 
+     * @param stream the stream containing the content to be imported
+     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
+     *        be null
+     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
+     * @return the batch of requests for creating the graph content that represents the imported content
+     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
+     * @throws IOException if there is a problem reading the content
+     * @throws SAXException if there is a problem with the SAX Parser
+     * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
+     *         source}
+     */
+    public Graph.Batch importXml( InputStream stream,
+                                  Location location,
+                                  boolean skip ) throws IOException, SAXException, RepositorySourceException {
+        CheckArg.isNotNull(stream, "uri");
+        CheckArg.isNotNull(location, "location");
+        CheckArg.isNotNull(location.getPath(), "location.getPath()");
+
+        // Create the destination for the XmlHandler ...
+        Graph.Batch batch = graph.batch();
+        Destination destination = new GraphBatchDestination(batch, true);
+
+        // Determine where the content is to be placed ...
+        Path parentPath = location.getPath();
+        Name nameAttribute = JcrLexicon.NAME;
+        Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
+        Name typeAttributeValue = null;
+        NamespaceRegistry reg = graph.getContext().getNamespaceRegistry();
+        if (reg.isRegisteredNamespaceUri(JcrNtLexicon.Namespace.URI)) {
+            typeAttributeValue = JcrNtLexicon.UNSTRUCTURED;
+        }
+
+        TextDecoder decoder = null;
+        XmlHandler.AttributeScoping scoping = XmlHandler.AttributeScoping.USE_DEFAULT_NAMESPACE;
+        XmlHandler handler = new XmlHandler(destination, skip, parentPath, decoder, nameAttribute, typeAttribute,
+                                            typeAttributeValue, scoping);
+        XMLReader reader = XMLReaderFactory.createXMLReader();
+        reader.setContentHandler(handler);
+        reader.setErrorHandler(handler);
+        reader.parse(new InputSource(stream));
+        if (stream != null) stream.close();
+        return batch;
+    }
+
+    /**
+     * Return an {@link XmlHandler} that can be used to import content directly into the supplied location. The operations
+     * resulting from the {@link XmlHandler} operations are batched until the {@link XmlHandler#endDocument()} is called, at which
+     * point all enqueued operations are submitted to the graph.
+     * 
+     * @param location the location in the {@link RepositorySource repository source} where the content is to be written; may not
+     *        be null
+     * @param skip true if the root element should be skipped, or false if a node should be created for the root XML element
+     * @return the {@link XmlHandler} that can be used to import content
+     * @throws IllegalArgumentException if the <code>stream</code> or destination path are null
+     */
+    public XmlHandler getHandlerForImportingXml( Location location,
+                                                 boolean skip ) {
+        CheckArg.isNotNull(location, "location");
+        CheckArg.isNotNull(location.getPath(), "location.getPath()");
+
+        // Create the destination for the XmlHandler ...
+        Graph.Batch batch = graph.batch();
+        Destination destination = new GraphBatchDestination(batch, false);
+
+        // Determine where the content is to be placed ...
+        Path parentPath = location.getPath();
+        Name nameAttribute = JcrLexicon.NAME;
+        Name typeAttribute = JcrLexicon.PRIMARY_TYPE;
+        Name typeAttributeValue = null;
+        NamespaceRegistry reg = graph.getContext().getNamespaceRegistry();
+        if (reg.isRegisteredNamespaceUri(JcrNtLexicon.Namespace.URI)) {
+            typeAttributeValue = JcrNtLexicon.UNSTRUCTURED;
+        }
+
+        TextDecoder decoder = null;
+        XmlHandler.AttributeScoping scoping = XmlHandler.AttributeScoping.USE_DEFAULT_NAMESPACE;
+        XmlHandler handler = new XmlHandler(destination, skip, parentPath, decoder, nameAttribute, typeAttribute,
+                                            typeAttributeValue, scoping);
+        return handler;
+    }
+
+}


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

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-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -34,6 +34,7 @@
 import org.jboss.dna.common.text.XmlNameEncoder;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.io.Destination;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -405,46 +406,4 @@
         return result;
     }
 
-    /**
-     * Interface used internally as the destination for the requests. This is used to abstract whether the requests should be
-     * submitted immediately or in a single batch.
-     * 
-     * @author Randall Hauch
-     */
-    @NotThreadSafe
-    public static interface Destination {
-
-        /**
-         * Obtain the execution context of the destination.
-         * 
-         * @return the destination's execution context
-         */
-        public ExecutionContext getExecutionContext();
-
-        /**
-         * Create a node at the supplied path and with the supplied attributes. The path will be absolute.
-         * 
-         * @param path the absolute path of the node
-         * @param properties the properties for the node; never null, but may be empty if there are no properties
-         */
-        public void create( Path path,
-                            List<Property> properties );
-
-        /**
-         * Create a node at the supplied path and with the supplied attributes. The path will be absolute.
-         * 
-         * @param path the absolute path of the node
-         * @param firstProperty the first property
-         * @param additionalProperties the remaining properties for the node
-         */
-        public void create( Path path,
-                            Property firstProperty,
-                            Property... additionalProperties );
-
-        /**
-         * Signal to this destination that any enqueued create requests should be submitted. Usually this happens at the end of
-         * the document parsing, but an implementer must allow for it to be called multiple times and anytime during parsing.
-         */
-        public void submit();
-    }
 }

Deleted: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java	2009-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -1,186 +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;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.stub;
-import java.io.File;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.transaction.xa.XAResource;
-import org.jboss.dna.graph.cache.CachePolicy;
-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.RepositorySourceListener;
-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.request.CompositeRequest;
-import org.jboss.dna.graph.request.CreateNodeRequest;
-import org.jboss.dna.graph.request.Request;
-import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
-
-/**
- * @author Randall Hauch
- */
-public class GraphImporterTest {
-
-    private Graph graph;
-    private GraphImporter importer;
-    private String sourceName;
-    private ExecutionContext context;
-    private URI xmlContent;
-    private MockRepositoryConnection connection;
-    private Request lastExecutedRequest;
-    private Path destinationPath;
-    @Mock
-    private RepositoryConnectionFactory sources;
-
-    @Before
-    public void beforeEach() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        xmlContent = new File("src/test/resources/repositoryImporterTestData1.xml").toURI();
-        context = new ExecutionContext();
-        context.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
-        context.getNamespaceRegistry().register("jcr", "http://www.jcp.org/jcr/1.0");
-        context.getNamespaceRegistry().register("nt", "http://www.jcp.org/jcr/nt/1.0");
-        sourceName = "sourceA";
-        destinationPath = context.getValueFactories().getPathFactory().create("/a/b");
-        graph = Graph.create(sourceName, sources, context);
-        importer = new GraphImporter(graph);
-        connection = new MockRepositoryConnection();
-        stub(sources.createConnection(sourceName)).toReturn(connection);
-    }
-
-    @Test
-    public void shouldImportXmlContentAndGenerateTheCorrectCommands() throws Exception {
-        System.out.println(xmlContent);
-        Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
-        batch.execute();
-        // 'lastExecutedCommand'
-        assertThat(lastExecutedRequest, is(instanceOf(CompositeRequest.class)));
-        Iterator<Request> iter = ((CompositeRequest)lastExecutedRequest).iterator();
-        // assertCreateNode(iter, "/a/b/", "jcr:primaryType={http://www.jboss.org/dna/xml/1.0}document");
-        assertCreateNode(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
-        assertCreateNode(iter, "/a/b/dna:system[1]/dna:sources[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
-        assertCreateNode(iter,
-                         "/a/b/dna:system[1]/dna:sources[1]/sourceA[1]",
-                         "repositoryName=repositoryA",
-                         "retryLimit=3",
-                         "jcr:primaryType={http://www.jboss.org/dna/1.0}xyz",
-                         "dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
-        assertCreateNode(iter,
-                         "/a/b/dna:system[1]/dna:sources[1]/sourceB[1]",
-                         "repositoryName=repositoryB",
-                         "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured",
-                         "dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
-        assertThat(iter.hasNext(), is(false));
-    }
-
-    public void assertCreateNode( Iterator<Request> iterator,
-                                  String path,
-                                  String... properties ) {
-        Request nextCommand = iterator.next();
-        assertThat(nextCommand, is(instanceOf(CreateNodeRequest.class)));
-        CreateNodeRequest createNode = (CreateNodeRequest)nextCommand;
-        Path expectedPath = context.getValueFactories().getPathFactory().create(path);
-        Path parentPath = createNode.under().getPath();
-        assertThat(parentPath, is(expectedPath.getParent()));
-        assertThat(createNode.named(), is(expectedPath.getLastSegment().getName()));
-        Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
-        for (Property prop : createNode.properties()) {
-            propertiesByName.put(prop.getName(), prop);
-        }
-        for (String propertyStr : properties) {
-            if (propertyStr == "any properties") {
-                propertiesByName.clear();
-                break;
-            }
-            Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
-            if (!matcher.matches()) continue;
-            System.out.println("Property: " + propertyStr + " ==> " + matcher);
-            Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
-            System.out.println("Property name: " + matcher.group(1));
-            String value = matcher.group(2); // doesn't handle multiple values!!
-            if (value.trim().length() == 0) value = null;
-            Property actual = propertiesByName.remove(propertyName);
-            Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
-            assertThat("missing property " + propertyName, actual, is(expectedProperty));
-        }
-        if (!propertiesByName.isEmpty()) {
-            System.out.println("Properties for " + path + "\n" + propertiesByName);
-        }
-        assertThat(propertiesByName.isEmpty(), is(true));
-    }
-
-    protected class MockRepositoryConnection implements RepositoryConnection {
-        public void close() {
-        }
-
-        @SuppressWarnings( "synthetic-access" )
-        public void execute( ExecutionContext context,
-                             Request request ) throws RepositorySourceException {
-            lastExecutedRequest = request;
-            if (request instanceof VerifyWorkspaceRequest) {
-                VerifyWorkspaceRequest workspaceRequest = (VerifyWorkspaceRequest)request;
-                workspaceRequest.setActualRootLocation(Location.create(context.getValueFactories().getPathFactory().createRootPath()));
-                workspaceRequest.setActualWorkspaceName("default");
-            }
-        }
-
-        public CachePolicy getDefaultCachePolicy() {
-            return null;
-        }
-
-        @SuppressWarnings( "synthetic-access" )
-        public String getSourceName() {
-            return sourceName;
-        }
-
-        public XAResource getXAResource() {
-            return null;
-        }
-
-        public boolean ping( long time,
-                             TimeUnit unit ) {
-            return true;
-        }
-
-        public void setListener( RepositorySourceListener listener ) {
-        }
-    }
-
-}

Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java (from rev 776, trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -0,0 +1,192 @@
+/*
+ * 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.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.io.File;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+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.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.RepositorySourceListener;
+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.request.CompositeRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class GraphImporterTest {
+
+    private Graph graph;
+    private GraphImporter importer;
+    private String sourceName;
+    private ExecutionContext context;
+    private URI xmlContent;
+    private MockRepositoryConnection connection;
+    private Request lastExecutedRequest;
+    private Path destinationPath;
+    @Mock
+    private RepositoryConnectionFactory sources;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        xmlContent = new File("src/test/resources/repositoryImporterTestData1.xml").toURI();
+        context = new ExecutionContext();
+        context.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
+        context.getNamespaceRegistry().register("jcr", "http://www.jcp.org/jcr/1.0");
+        context.getNamespaceRegistry().register("nt", "http://www.jcp.org/jcr/nt/1.0");
+        sourceName = "sourceA";
+        destinationPath = context.getValueFactories().getPathFactory().create("/a/b");
+        graph = Graph.create(sourceName, sources, context);
+        importer = new GraphImporter(graph);
+        connection = new MockRepositoryConnection();
+        stub(sources.createConnection(sourceName)).toReturn(connection);
+    }
+
+    @Test
+    public void shouldImportXmlContentAndGenerateTheCorrectCommands() throws Exception {
+        System.out.println(xmlContent);
+        Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
+        batch.execute();
+        // 'lastExecutedCommand'
+        assertThat(lastExecutedRequest, is(instanceOf(CompositeRequest.class)));
+        Iterator<Request> iter = ((CompositeRequest)lastExecutedRequest).iterator();
+        // assertCreateNode(iter, "/a/b/", "jcr:primaryType={http://www.jboss.org/dna/xml/1.0}document");
+        assertCreateNode(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
+        assertCreateNode(iter, "/a/b/dna:system[1]/dna:sources[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
+        assertCreateNode(iter,
+                         "/a/b/dna:system[1]/dna:sources[1]/sourceA[1]",
+                         "repositoryName=repositoryA",
+                         "retryLimit=3",
+                         "jcr:primaryType={http://www.jboss.org/dna/1.0}xyz",
+                         "dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
+        assertCreateNode(iter,
+                         "/a/b/dna:system[1]/dna:sources[1]/sourceB[1]",
+                         "repositoryName=repositoryB",
+                         "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured",
+                         "dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    public void assertCreateNode( Iterator<Request> iterator,
+                                  String path,
+                                  String... properties ) {
+        Request nextCommand = iterator.next();
+        assertThat(nextCommand, is(instanceOf(CreateNodeRequest.class)));
+        CreateNodeRequest createNode = (CreateNodeRequest)nextCommand;
+        Path expectedPath = context.getValueFactories().getPathFactory().create(path);
+        Path parentPath = createNode.under().getPath();
+        assertThat(parentPath, is(expectedPath.getParent()));
+        assertThat(createNode.named(), is(expectedPath.getLastSegment().getName()));
+        Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
+        for (Property prop : createNode.properties()) {
+            propertiesByName.put(prop.getName(), prop);
+        }
+        for (String propertyStr : properties) {
+            if (propertyStr == "any properties") {
+                propertiesByName.clear();
+                break;
+            }
+            Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
+            if (!matcher.matches()) continue;
+            System.out.println("Property: " + propertyStr + " ==> " + matcher);
+            Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
+            System.out.println("Property name: " + matcher.group(1));
+            String value = matcher.group(2); // doesn't handle multiple values!!
+            if (value.trim().length() == 0) value = null;
+            Property actual = propertiesByName.remove(propertyName);
+            Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
+            assertThat("missing property " + propertyName, actual, is(expectedProperty));
+        }
+        if (!propertiesByName.isEmpty()) {
+            System.out.println("Properties for " + path + "\n" + propertiesByName);
+        }
+        assertThat(propertiesByName.isEmpty(), is(true));
+    }
+
+    protected class MockRepositoryConnection implements RepositoryConnection {
+        public void close() {
+        }
+
+        @SuppressWarnings( "synthetic-access" )
+        public void execute( ExecutionContext context,
+                             Request request ) throws RepositorySourceException {
+            lastExecutedRequest = request;
+            if (request instanceof VerifyWorkspaceRequest) {
+                VerifyWorkspaceRequest workspaceRequest = (VerifyWorkspaceRequest)request;
+                workspaceRequest.setActualRootLocation(Location.create(context.getValueFactories()
+                                                                              .getPathFactory()
+                                                                              .createRootPath()));
+                workspaceRequest.setActualWorkspaceName("default");
+            }
+        }
+
+        public CachePolicy getDefaultCachePolicy() {
+            return null;
+        }
+
+        @SuppressWarnings( "synthetic-access" )
+        public String getSourceName() {
+            return sourceName;
+        }
+
+        public XAResource getXAResource() {
+            return null;
+        }
+
+        public boolean ping( long time,
+                             TimeUnit unit ) {
+            return true;
+        }
+
+        public void setListener( RepositorySourceListener listener ) {
+        }
+    }
+
+}


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

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java	2009-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -40,6 +40,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.io.Destination;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
@@ -62,7 +63,7 @@
 
     private XmlHandler handler;
     private ExecutionContext context;
-    private XmlHandler.Destination destination;
+    private Destination destination;
     private boolean skipRootElement = false;
     private Path parentPath;
     private TextDecoder decoder;
@@ -432,7 +433,7 @@
         }
     }
 
-    protected class RecordingDestination implements XmlHandler.Destination {
+    protected class RecordingDestination implements Destination {
         private final LinkedList<CreateNodeRequest> requests = new LinkedList<CreateNodeRequest>();
         private final String workspace = "Recording Workspace";
 

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-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -46,11 +46,11 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.GraphImporter;
 import org.jboss.dna.graph.Location;
 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.io.GraphImporter;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -72,12 +72,12 @@
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.GraphImporter;
 import org.jboss.dna.graph.JcrNtLexicon;
 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.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.io.GraphImporter;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.jcr.JcrRepository.Options;
 

Modified: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-15 14:28:30 UTC (rev 828)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-15 22:42:18 UTC (rev 829)
@@ -36,12 +36,12 @@
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.GraphImporter;
 import org.jboss.dna.graph.JcrNtLexicon;
 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.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.io.GraphImporter;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.jcr.JcrRepository.Options;
 



From dna-commits at lists.jboss.org  Thu Apr 16 09:58:49 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 09:58:49 -0400
Subject: [dna-commits] DNA SVN: r830 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LuS77-00050z-8z@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 09:58:49 -0400 (Thu, 16 Apr 2009)
New Revision: 830

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-356 Need to Throw ItemExistsException if addNode Call Fails Due to SNS

Added a patch that changes SessionCache.NodeEditor.addNode(...) to throw an ItemExistsException if the node cannot be added because an item at the specified path already exists and same-name siblings are not allowed, per the specification (see the JIRA issue for the details).  An additional lookup of the child node definition is performed, but this isn't a problem since it is already an error case and the extra work is justified to provide more useful information in the exception.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-15 22:42:18 UTC (rev 829)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 13:58:49 UTC (rev 830)
@@ -37,6 +37,7 @@
 import java.util.UUID;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
@@ -842,7 +843,7 @@
             if (desiredUuid == null) desiredUuid = UUID.randomUUID();
 
             // Verify that this node accepts a child of the supplied name (given any existing SNS nodes) ...
-            int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(name);
+            int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(name) + 1;
             JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(node.getPrimaryTypeName(),
                                                                                node.getMixinTypeNames(),
                                                                                name,
@@ -851,6 +852,21 @@
                                                                                true);
             // Make sure there was a valid child node definition ...
             if (definition == null) {
+
+                // Check if the definition would have worked with less SNS
+                definition = nodeTypes().findChildNodeDefinition(node.getPrimaryTypeName(),
+                                                                 node.getMixinTypeNames(),
+                                                                 name,
+                                                                 primaryTypeName,
+                                                                 numSns - 1,
+                                                                 true);
+                if (definition != null) {
+                    // Only failed because there was no SNS definition - throw ItemExistsException per 7.1.4 of 1.0.1 spec
+                    Path pathForChild = pathFactory.create(getPathFor(node), name, numSns + 1);
+                    String msg = JcrI18n.noSnsDefinitionForNode.text(pathForChild, workspaceName());
+                    throw new ItemExistsException(msg);
+                }
+                // Didn't work for other reasons - throw ConstraintViolationException
                 Path pathForChild = pathFactory.create(getPathFor(node), name, numSns + 1);
                 String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(pathForChild, workspaceName());
                 throw new ConstraintViolationException(msg);



From dna-commits at lists.jboss.org  Thu Apr 16 10:11:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 10:11:21 -0400
Subject: [dna-commits] DNA SVN: r831 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LuSJF-0001JA-PC@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 10:11:21 -0400 (Thu, 16 Apr 2009)
New Revision: 831

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-357 Hide dna:nodeDefinition Property from Users

Applied both patches.  The first hides the "dna:nodeDefinition" property from the JCR layer (meaning it might exist in the graph layer, but won't appear as properties of JCR nodes).  The second patch adds to "nt:base" a property definition for a "dna:nodeDefinition" property that is protected and not mandatory (nor autocreated).  The spec is not clear as to whether a JCR implementation can add properties to "nt:base" (the required supertype of all node types), the spec is clear that an implementation can add properties to any of the built-in node types through mixins.  Therefore, the second patch actually adds this new definition to "nt:base" by having "nt:base" extend the new "dna:defined" mixin, which has the new "dna:nodeDefinition" property definition.  

Because the "dna:nodeDefinition" is protected, JCR clients will not be allowed to define another property with this name, nor will they be able to set the property (using a residual definition) to any value.  All this validation is done through the normal process, requiring no code specific to "dna:nodeDefinition".  Nicely done!

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-16 13:58:49 UTC (rev 830)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-16 14:11:21 UTC (rev 831)
@@ -31,6 +31,7 @@
  */
 public class DnaLexicon extends org.jboss.dna.graph.DnaLexicon {
 
+    public static final Name DEFINED = new BasicName(Namespace.URI, "defined");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
     public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-16 13:58:49 UTC (rev 830)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-16 14:11:21 UTC (rev 831)
@@ -57,8 +57,28 @@
         // Value trueValue = new JcrValue(context.getValueFactories(), null, PropertyType.BOOLEAN, Boolean.TRUE);
         Value ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
 
+        JcrNodeType defined = new JcrNodeType(
+                                                    context,
+                                                    NO_NODE_TYPE_MANAGER,
+                                                    DnaLexicon.DEFINED,
+                                                    NO_SUPERTYPES,
+                                                    DnaLexicon.NODE_DEFINITON,
+                                                    NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         context,
+                                                                                                                         null,
+                                                                                                                         DnaLexicon.NODE_DEFINITON,
+                                                                                                                         OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         true,
+                                                                                                                         NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.STRING,
+                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
         // Stubbing in child node and property definitions for now
-        JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME,
+        JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE, Arrays.asList(new JcrNodeType[] {defined}), NO_PRIMARY_ITEM_NAME,
                                            NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
                                                new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_TYPE,
                                                                          OnParentVersionBehavior.COMPUTE.getJcrValue(), true,
@@ -565,7 +585,7 @@
         // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         // Disabling version-related types until DNA supports versioning, as per section 4.11 of the 1.0.1 specification
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder, frozenNode,
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {defined, base, unstructured, childNodeDefinition, file, folder, frozenNode,
             hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType /*, version, versionHistory,
                    versionLabels, versionedChild */}));
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 13:58:49 UTC (rev 830)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 14:11:21 UTC (rev 831)
@@ -401,6 +401,8 @@
         PropertyInfo info = findPropertyInfo(propertyId); // throws PathNotFoundException if node not there
         if (info == null) return null; // no such property on this node
 
+        if (DnaLexicon.NODE_DEFINITON.equals(info.getPropertyName())) return null;
+        
         // Now create the appropriate JCR Property object ...
         return createAndCacheJcrPropertyFor(info);
     }
@@ -411,7 +413,9 @@
         Set<Name> propertyNames = info.getPropertyNames();
         Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(propertyNames.size());
         for (Name propertyName : propertyNames) {
-            result.add(findJcrProperty(new PropertyId(nodeUuid, propertyName)));
+            if (!DnaLexicon.NODE_DEFINITON.equals(propertyName)) {
+                result.add(findJcrProperty(new PropertyId(nodeUuid, propertyName)));
+            }
         }
         return result;
     }



From dna-commits at lists.jboss.org  Thu Apr 16 10:53:11 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 10:53:11 -0400
Subject: [dna-commits] DNA SVN: r832 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LuSxj-0005e9-L5@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 10:53:11 -0400 (Thu, 16 Apr 2009)
New Revision: 832

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-359 Session.refresh and Node.refresh Methods Not Supported

Applied the patch that implements the Session.refresh and Node.refresh by adding two SessionCache.refresh methods (one to refresh all contents, the other to refresh only those items below a branch).  

There is one case that the patch doesn't handle (well), and that is when a node is removed from a branch, refreshing that branch will not refresh the removed node.  This needs to be addressed, and will be described in the issue.

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-04-16 14:11:21 UTC (rev 831)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-16 14:53:11 UTC (rev 832)
@@ -1597,11 +1597,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Item#refresh(boolean)
      */
-    public void refresh( boolean keepChanges ) {
-        throw new UnsupportedOperationException();
+    public void refresh( boolean keepChanges ) throws RepositoryException {
+        this.cache.refresh(this.nodeUuid, keepChanges);
     }
 
     /**

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-04-16 14:11:21 UTC (rev 831)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-16 14:53:11 UTC (rev 832)
@@ -665,6 +665,7 @@
      * @see javax.jcr.Session#refresh(boolean)
      */
     public void refresh( boolean keepChanges ) {
+        this.cache.refresh(keepChanges);
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 14:11:21 UTC (rev 831)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 14:53:11 UTC (rev 832)
@@ -34,6 +34,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.Stack;
 import java.util.UUID;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.Item;
@@ -228,6 +229,103 @@
     }
 
     /**
+     * Refreshes (removes the cached state) for all cached nodes.
+     * <p>
+     * If {@code keepChanges == true}, modified nodes will not have their state refreshed.
+     * </p>
+     * 
+     * @param keepChanges indicates whether changed nodes should be kept or refreshed from the repository.
+     */
+    public void refresh( boolean keepChanges ) {
+        if (keepChanges) {
+            // Keep the pending operations
+            Set<UUID> retainedSet = new HashSet<UUID>(this.changedNodes.size() + this.deletedNodes.size());
+            retainedSet.addAll(this.changedNodes.keySet());
+            retainedSet.addAll(this.deletedNodes.keySet());
+
+            // Removed any cached nodes not already in the changed or deleted set
+            this.cachedNodes.keySet().retainAll(retainedSet);
+        } else {
+            // Throw out the old pending operations
+            this.requests.clear();
+            this.changedNodes.clear();
+            this.cachedNodes.clear();
+            this.deletedNodes.clear();
+        }
+
+    }
+
+    /**
+     * Refreshes (removes the cached state) for the node with the given UUID and any of its descendants.
+     * <p>
+     * If {@code keepChanges == true}, modified nodes will not have their state refreshed.
+     * </p>
+     * 
+     * @param nodeUuid the UUID of the node that is to be saved; may not be null
+     * @param keepChanges indicates whether changed nodes should be kept or refreshed from the repository.
+     * @throws RepositoryException if any error resulting while saving the changes to the repository
+     */
+    public void refresh( UUID nodeUuid,
+                         boolean keepChanges ) throws RepositoryException {
+        // Find the path of the given node ...
+        Path path = getPathFor(nodeUuid);
+
+        // Build the set of affected node UUIDs
+        Set<UUID> nodesUnderBranch = new HashSet<UUID>();
+        Stack<UUID> nodesToVisit = new Stack<UUID>();
+
+        nodesToVisit.add(nodeUuid);
+        while (!nodesToVisit.isEmpty()) {
+            UUID uuid = nodesToVisit.pop();
+            nodesUnderBranch.add(uuid);
+
+            NodeInfo nodeInfo = cachedNodes.get(uuid);
+            // Newly added nodes will be changedNodes but not cachedNodes
+            if (nodeInfo == null) nodeInfo = changedNodes.get(uuid);
+
+            if (nodeInfo != null) {
+                for (ChildNode childNode : nodeInfo.getChildren()) {
+                    nodesToVisit.add(childNode.getUuid());
+                }
+            }
+        }
+
+        if (keepChanges) {
+            // Keep the pending operations
+            for (UUID uuid : nodesUnderBranch) {
+                // getPathFor can (and will) add entries to cachedNodes - hence the existence of nodesToCheck
+                if (getPathFor(uuid).isDecendantOf(path)) {
+                    if (!this.changedNodes.containsKey(uuid) && !this.deletedNodes.containsKey(uuid)) {
+                        this.cachedNodes.remove(uuid);
+                    }
+                }
+            }
+        } else {
+            this.cachedNodes.keySet().removeAll(nodesUnderBranch);
+            this.changedNodes.keySet().removeAll(nodesUnderBranch);
+            this.deletedNodes.keySet().removeAll(nodesUnderBranch);
+
+            // Throw out the old pending operations
+            if (operations.isExecuteRequired()) {
+
+                // Make sure the builder has finished all the requests ...
+                this.requestBuilder.finishPendingRequest();
+
+                // Remove all of the enqueued requests for this branch ...
+                for (Iterator<Request> iter = this.requests.iterator(); iter.hasNext();) {
+                    Request request = iter.next();
+                    assert request instanceof ChangeRequest;
+                    ChangeRequest change = (ChangeRequest)request;
+                    if (change.changes(workspaceName, path)) {
+                        iter.remove();
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
      * Save any changes that have been accumulated by this session.
      * 
      * @throws RepositoryException if any error resulting while saving the changes to the repository
@@ -263,7 +361,7 @@
     }
 
     /**
-     * Save any changes to the identified node or its decendants. The supplied node may not have been deleted or created in this
+     * Save any changes to the identified node or its descendants. The supplied node may not have been deleted or created in this
      * session since the last save operation.
      * 
      * @param nodeUuid the UUID of the node that is to be saved; may not be null
@@ -313,6 +411,22 @@
                 return;
             }
 
+            /*
+             * branchUuids contains all the roots of the changes, but there may be further changes under the roots (e.g., a
+             * newly created node will have it's parent's UUID in branchUuids, but not the new node's uuid. 
+             */
+            Set<UUID> uuidsUnderBranch = new HashSet<UUID>();
+            for (UUID branchUuid : branchUuids) {
+                uuidsUnderBranch.add(branchUuid);
+                ChangedNodeInfo changedNode = changedNodes.get(branchUuid);
+                if (changedNode != null) {
+                    for (ChildNode childNode : changedNode.getChildren()) {
+                        uuidsUnderBranch.add(childNode.getUuid());
+                    }
+                }
+
+            }
+
             // Now execute the branch ...
             Graph.Batch branchBatch = store.batch(new BatchRequestBuilder(branchRequests));
             try {
@@ -324,11 +438,9 @@
                 this.operations = store.batch(this.requestBuilder);
 
                 // Remove all the cached, changed or deleted items that were just saved ...
-                for (UUID changedUuid : branchUuids) {
-                    cachedNodes.remove(changedUuid);
-                    changedNodes.remove(changedUuid);
-                    deletedNodes.remove(changedUuid);
-                }
+                cachedNodes.keySet().removeAll(uuidsUnderBranch);
+                changedNodes.keySet().removeAll(uuidsUnderBranch);
+                deletedNodes.keySet().removeAll(uuidsUnderBranch);
             } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
                 throw new PathNotFoundException(e.getLocalizedMessage(), e);
             } catch (RuntimeException e) {
@@ -402,7 +514,7 @@
         if (info == null) return null; // no such property on this node
 
         if (DnaLexicon.NODE_DEFINITON.equals(info.getPropertyName())) return null;
-        
+
         // Now create the appropriate JCR Property object ...
         return createAndCacheJcrPropertyFor(info);
     }



From dna-commits at lists.jboss.org  Thu Apr 16 11:26:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 11:26:06 -0400
Subject: [dna-commits] DNA SVN: r833 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LuTTa-0008Sc-Lc@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 11:26:06 -0400 (Thu, 16 Apr 2009)
New Revision: 833

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
   trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-360 JcrSession.importXml and getContentHandler Are Not Implemented

Applied the patch, which implements Session.importXml and getContentHandler.  SerializationTest was commented back out (relative to the patch), since it tests the similar methods in Workspace, and these are not yet implemented.

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-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-16 15:26:06 UTC (rev 833)
@@ -915,6 +915,31 @@
                                String primaryNodeTypeName )
         throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
         RepositoryException {
+        return this.addNode(relPath, primaryNodeTypeName, null);
+    }
+
+    /**
+     * Adds the a new node with the given primary type (if specified) at the given relative path with the given UUID (if
+     * specified).
+     * 
+     * @param relPath the at which the new node should be created
+     * @param primaryNodeTypeName the desired primary type for the new node; null value indicates that the default primary type
+     *        from the appropriate definition for this node should be used
+     * @param desiredUuid the UUID (for the jcr.uuid property) of this node; may be null
+     * @return the newly created node
+     * @throws ItemExistsException if an item at the specified path already exists and same-name siblings are not allowed.
+     * @throws PathNotFoundException if the specified path implies intermediary nodes that do not exist.
+     * @throws VersionException not thrown at this time, but included for compatibility with the specification
+     * @throws ConstraintViolationException if the change would violate a node type or implementation-specific constraint.
+     * @throws LockException not thrown at this time, but included for compatibility with the specification
+     * @throws RepositoryException if another error occurs
+     * @see #addNode(String, String)
+     */
+    final AbstractJcrNode addNode( String relPath,
+                                   String primaryNodeTypeName,
+                                   UUID desiredUuid )
+        throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
+        RepositoryException {
         // Determine the path ...
         NodeEditor editor = null;
         Path path = null;
@@ -976,7 +1001,7 @@
         }
 
         // Create the child ...
-        ChildNode child = editor.createChild(childName, null, childPrimaryTypeName);
+        ChildNode child = editor.createChild(childName, desiredUuid, childPrimaryTypeName);
         return cache.findJcrNode(child.getUuid());
     }
 

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-04-16 15:26:06 UTC (rev 833)
@@ -0,0 +1,465 @@
+/*
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.UUID;
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.ItemExistsException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.nodetype.ConstraintViolationException;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.text.TextDecoder;
+import org.jboss.dna.common.text.XmlNameEncoder;
+import org.jboss.dna.common.util.Base64;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Content handler that provides SAX-based event handling that maps incoming documents to the repository based on the
+ * functionality described in section 7.3 of the JCR 1.0.1 specification.
+ * <p>
+ * Each content handler is only intended to be used once and discarded. This class is <b>NOT</b> thread-safe.
+ * </p>
+ * 
+ * @see JcrSession#getImportContentHandler(String, int)
+ * @see JcrWorkspace#getImportContentHandler(String, int)
+ */
+ at NotThreadSafe
+class JcrContentHandler extends DefaultHandler {
+
+    /**
+     * Encoder to properly escape XML names.
+     * 
+     * @see XmlNameEncoder
+     */
+    protected static final TextDecoder SYSTEM_VIEW_NAME_DECODER = new XmlNameEncoder();
+
+    protected static final TextDecoder DOCUMENT_VIEW_NAME_DECODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
+
+    protected final JcrSession session;
+    protected final int uuidBehavior;
+    protected final SaveMode saveMode;
+
+    protected final String primaryTypeName;
+    protected final String mixinTypesName;
+    protected final String uuidName;
+
+    private AbstractJcrNode currentNode;
+    private ContentHandler delegate;
+
+    enum SaveMode {
+        WORKSPACE,
+        SESSION
+    }
+
+    JcrContentHandler( JcrSession session,
+                       Path parentPath,
+                       int uuidBehavior,
+                       SaveMode saveMode ) throws PathNotFoundException, RepositoryException {
+        assert session != null;
+        assert parentPath != null;
+        assert uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW
+               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING
+               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING
+               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW;
+
+        this.session = session;
+        this.currentNode = (AbstractJcrNode)session.getNode(parentPath);
+        this.uuidBehavior = uuidBehavior;
+        this.saveMode = saveMode;
+
+        this.primaryTypeName = JcrLexicon.PRIMARY_TYPE.getString(this.session.namespaces());
+        this.mixinTypesName = JcrLexicon.MIXIN_TYPES.getString(this.session.namespaces());
+        this.uuidName = JcrLexicon.UUID.getString(this.session.namespaces());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    @Override
+    public void characters( char[] ch,
+                            int start,
+                            int length ) throws SAXException {
+        assert this.delegate != null;
+        delegate.characters(ch, start, length);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+     */
+    @Override
+    public void endElement( String uri,
+                            String localName,
+                            String name ) throws SAXException {
+        assert this.delegate != null;
+        delegate.endElement(uri, localName, name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    @Override
+    public void startElement( String uri,
+                              String localName,
+                              String name,
+                              Attributes atts ) throws SAXException {
+        checkDelegate(uri);
+        assert this.delegate != null;
+
+        delegate.startElement(uri, localName, name, atts);
+    }
+
+    private void checkDelegate( String namespaceUri ) {
+        if (delegate != null) return;
+
+        if (JcrSvLexicon.Namespace.URI.equals(namespaceUri)) {
+            this.delegate = new SystemViewContentHandler(this.currentNode);
+        } else {
+            this.delegate = new DocumentViewContentHandler(this.currentNode);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+     */
+    @Override
+    public void startPrefixMapping( String prefix,
+                                    String uri ) throws SAXException {
+        try {
+            // Read from the workspace's DNA registry, as its semantics are more friendly
+            NamespaceRegistry registry = session.workspace().context().getNamespaceRegistry();
+
+            String existingUri = registry.getNamespaceForPrefix(prefix);
+
+            if (existingUri != null) {
+                if (existingUri.equals(uri)) {
+                    // prefix/uri mapping is already in registry
+                    return;
+                }
+
+                throw new RepositoryException("Prefix " + prefix + " is already permanently mapped");
+            }
+
+            // Register through the JCR workspace to ensure consistency
+            session.getWorkspace().getNamespaceRegistry().registerNamespace(prefix, uri);
+        } catch (RepositoryException re) {
+            throw new EnclosingSAXException(re);
+        }
+    }
+
+    class EnclosingSAXException extends SAXException {
+
+        /**
+         */
+        private static final long serialVersionUID = -1044992767566435542L;
+
+        /**
+         * @param e
+         */
+        EnclosingSAXException( Exception e ) {
+            super(e);
+
+        }
+
+    }
+
+    private class SystemViewContentHandler extends DefaultHandler {
+        private Stack<AbstractJcrNode> parentStack;
+
+        private final String svNameName;
+        private final String svTypeName;
+
+        private String currentNodeName;
+        private String currentPropName;
+        private int currentPropType;
+
+        private StringBuffer valueBuffer;
+        private Map<String, List<Value>> currentProps;
+
+        /**
+         * @param currentNode
+         */
+        SystemViewContentHandler( AbstractJcrNode currentNode ) {
+            super();
+            this.parentStack = new Stack<AbstractJcrNode>();
+            this.parentStack.push(currentNode);
+
+            this.currentProps = new HashMap<String, List<Value>>();
+            this.valueBuffer = new StringBuffer();
+
+            this.svNameName = JcrSvLexicon.NAME.getString(session.namespaces());
+            this.svTypeName = JcrSvLexicon.TYPE.getString(session.namespaces());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String,
+         *      org.xml.sax.Attributes)
+         */
+        @Override
+        public void startElement( String uri,
+                                  String localName,
+                                  String name,
+                                  Attributes atts ) throws SAXException {
+            if ("node".equals(localName)) {
+                if (currentNodeName != null) {
+                    addNodeIfPending();
+                }
+
+                currentNodeName = atts.getValue(SYSTEM_VIEW_NAME_DECODER.decode(svNameName));
+            } else if ("property".equals(localName)) {
+                currentPropName = atts.getValue(SYSTEM_VIEW_NAME_DECODER.decode(svNameName));
+                currentPropType = PropertyType.valueFromName(atts.getValue(svTypeName));
+                currentProps.put(currentPropName, new ArrayList<Value>());
+            } else if (!"value".equals(localName)) {
+                throw new IllegalStateException("Unexpected element '" + name + "' in system view");
+            }
+        }
+
+        private void addNodeIfPending() throws SAXException {
+            if (currentNodeName != null) {
+                try {
+                    AbstractJcrNode parentNode = parentStack.peek();
+
+                    UUID uuid = null;
+                    List<Value> rawUuid = currentProps.get(uuidName);
+
+                    if (rawUuid != null) {
+                        assert rawUuid.size() == 1;
+                        uuid = UUID.fromString(rawUuid.get(0).getString());
+                    }
+
+                    AbstractJcrNode newNode = parentNode.addNode(currentNodeName,
+                                                                 currentProps.get(primaryTypeName).get(0).getString(),
+                                                                 uuid);
+
+                    for (Map.Entry<String, List<Value>> entry : currentProps.entrySet()) {
+                        if (entry.getKey().equals(primaryTypeName)) {
+                            continue;
+                        }
+
+                        if (entry.getKey().equals(mixinTypesName)) {
+                            for (Value value : entry.getValue()) {
+                                newNode.addMixin(value.getString());
+                            }
+                            continue;
+                        }
+
+                        if (entry.getKey().equals(uuidName)) {
+                            continue;
+                        }
+
+                        List<Value> values = entry.getValue();
+
+                        if (values.size() == 1) {
+                            newNode.setProperty(entry.getKey(), values.get(0));
+                        } else {
+                            newNode.setProperty(entry.getKey(), values.toArray(new Value[values.size()]));
+                        }
+                    }
+
+                    parentStack.push(newNode);
+                    currentProps.clear();
+                } catch (RepositoryException re) {
+                    throw new EnclosingSAXException(re);
+                }
+            }
+        }
+
+        @Override
+        public void endElement( String uri,
+                                String localName,
+                                String name ) throws SAXException {
+            if ("node".equals(localName)) {
+                addNodeIfPending();
+                currentNodeName = null;
+                parentStack.pop();
+            } else if ("value".equals(localName)) {
+                String s = valueBuffer.toString();
+                try {
+                    if (currentPropType == PropertyType.BINARY) {
+                        ByteArrayInputStream is = new ByteArrayInputStream(Base64.decode(s, Base64.URL_SAFE));
+                        currentProps.get(currentPropName).add(session.getValueFactory().createValue(is));
+                    } else {
+                        currentProps.get(currentPropName).add(session.getValueFactory().createValue(SYSTEM_VIEW_NAME_DECODER.decode(s),
+                                                                                                    currentPropType));
+                    }
+                } catch (RepositoryException re) {
+                    throw new EnclosingSAXException(re);
+                }
+                valueBuffer = new StringBuffer();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+         */
+        @Override
+        public void characters( char[] ch,
+                                int start,
+                                int length ) {
+            valueBuffer.append(ch, start, length);
+
+        }
+    }
+
+    private class DocumentViewContentHandler extends DefaultHandler {
+        private Stack<AbstractJcrNode> parentStack;
+
+        /**
+         * @param currentNode
+         */
+        DocumentViewContentHandler( AbstractJcrNode currentNode ) {
+            super();
+            this.parentStack = new Stack<AbstractJcrNode>();
+            parentStack.push(currentNode);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String,
+         *      org.xml.sax.Attributes)
+         */
+        @Override
+        public void startElement( String uri,
+                                  String localName,
+                                  String name,
+                                  Attributes atts ) throws SAXException {
+            try {
+                String primaryTypeName = atts.getValue(JcrContentHandler.this.primaryTypeName);
+                String rawUuid = atts.getValue(uuidName);
+                UUID uuid = (rawUuid != null ? UUID.fromString(rawUuid) : null);
+                AbstractJcrNode parentNode = parentStack.peek();
+
+                if (uuid != null) {
+                    AbstractJcrNode existingNodeWithUuid = (AbstractJcrNode)session.getNodeByUUID(rawUuid);
+                    if (existingNodeWithUuid != null) {
+                        switch (uuidBehavior) {
+                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
+                                parentNode = (AbstractJcrNode)existingNodeWithUuid.getParent();
+                                existingNodeWithUuid.remove();
+                                break;
+                            case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
+                                uuid = UUID.randomUUID();
+                                break;
+                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
+                                if (existingNodeWithUuid.path().isAtOrAbove(parentStack.firstElement().path())) {
+                                    throw new ConstraintViolationException();
+                                }
+                                existingNodeWithUuid.remove();
+                                break;
+                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
+                                throw new ItemExistsException();
+                        }
+                    }
+                }
+
+                name = DOCUMENT_VIEW_NAME_DECODER.decode(name);
+                AbstractJcrNode currentNode = parentNode.addNode(name, primaryTypeName, uuid);
+
+                for (int i = 0; i < atts.getLength(); i++) {
+                    if (JcrContentHandler.this.primaryTypeName.equals(atts.getQName(i))) {
+                        continue;
+                    }
+
+                    if (mixinTypesName.equals(atts.getQName(i))) {
+                        currentNode.addMixin(atts.getValue(i));
+                        continue;
+                    }
+
+                    if (uuidName.equals(atts.getQName(i))) {
+                        continue;
+                    }
+
+                    // We may want to use the workspace context here so that we only use the permanent namespace mappings
+                    // Name propName = session.executionContext.getValueFactories().getNameFactory().create(atts.getQName(i));
+                    // String value = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getValue(i));
+                    String value = atts.getValue(i);
+                    String propertyName = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getQName(i));
+                    currentNode.setProperty(propertyName, value);
+                }
+
+                parentStack.push(currentNode);
+            } catch (RepositoryException re) {
+                throw new EnclosingSAXException(re);
+            }
+
+        }
+
+        @Override
+        public void endElement( String uri,
+                                String localName,
+                                String name ) {
+            parentStack.pop();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+         */
+        @Override
+        public void characters( char[] ch,
+                                int start,
+                                int length ) throws SAXException {
+            try {
+                NamespaceRegistry namespaces = session.getExecutionContext().getNamespaceRegistry();
+                Node parentNode = parentStack.peek();
+                Node currentNode = parentNode.addNode(JcrLexicon.XMLTEXT.getString(namespaces),
+                                                      JcrNtLexicon.UNSTRUCTURED.getString(namespaces));
+                String s = new String(ch, start, length);
+                currentNode.setProperty(JcrLexicon.XMLCHARACTERS.getString(namespaces), s);
+
+            } catch (RepositoryException re) {
+                throw new EnclosingSAXException(re);
+            }
+        }
+    }
+}


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

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-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-16 15:26:06 UTC (rev 833)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.AccessControlException;
@@ -32,7 +33,9 @@
 import java.util.Set;
 import java.util.UUID;
 import javax.jcr.Credentials;
+import javax.jcr.InvalidSerializedDataException;
 import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.NamespaceException;
 import javax.jcr.Node;
@@ -46,6 +49,7 @@
 import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
@@ -60,9 +64,15 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
+import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
+import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
 
 /**
  * @author John Verhaeg
@@ -355,12 +365,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#getImportContentHandler(java.lang.String, int)
      */
     public ContentHandler getImportContentHandler( String parentAbsPath,
-                                                   int uuidBehavior ) {
-        throw new UnsupportedOperationException();
+                                                   int uuidBehavior ) throws PathNotFoundException, RepositoryException {
+        Path parentPath = this.executionContext.getValueFactories().getPathFactory().create(parentAbsPath);
+
+        return new JcrContentHandler(this, parentPath, uuidBehavior, SaveMode.SESSION);
     }
 
     /**
@@ -595,13 +606,29 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#importXML(java.lang.String, java.io.InputStream, int)
      */
     public void importXML( String parentAbsPath,
                            InputStream in,
-                           int uuidBehavior ) {
-        throw new UnsupportedOperationException();
+                           int uuidBehavior ) throws IOException, InvalidSerializedDataException, RepositoryException {
+
+        try {
+            XMLReader parser = XMLReaderFactory.createXMLReader();
+            parser.setContentHandler(getImportContentHandler(parentAbsPath, uuidBehavior));
+            parser.parse(new InputSource(in));
+        } catch (EnclosingSAXException ese) {
+            Exception cause = ese.getException();
+            if (cause instanceof ItemExistsException) {
+                throw (ItemExistsException)cause;
+            } else if (cause instanceof ConstraintViolationException) {
+                throw (ConstraintViolationException)cause;
+            }
+            throw new RepositoryException(cause);
+        } catch (SAXParseException se) {
+            throw new InvalidSerializedDataException(se);
+        } catch (SAXException se) {
+            throw new RepositoryException(se);
+        }
     }
 
     /**

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-16 15:26:06 UTC (rev 833)
@@ -27,11 +27,14 @@
 import java.net.URI;
 import java.security.AccessControlContext;
 import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import javax.jcr.Credentials;
-import javax.jcr.Repository;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 import org.apache.jackrabbit.test.JCRTestSuite;
@@ -50,6 +53,7 @@
 import org.apache.jackrabbit.test.api.SetPropertyDoubleTest;
 import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
 import org.apache.jackrabbit.test.api.SetPropertyLongTest;
+import org.apache.jackrabbit.test.api.SetPropertyNodeTest;
 import org.apache.jackrabbit.test.api.SetPropertyStringTest;
 import org.apache.jackrabbit.test.api.SetPropertyValueTest;
 import org.apache.jackrabbit.test.api.SetValueBinaryTest;
@@ -69,9 +73,11 @@
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest;
-import org.jboss.dna.graph.DnaLexicon;
+import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrMixLexicon;
 import org.jboss.dna.graph.JcrNtLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -202,7 +208,7 @@
             addTestSuite(SetPropertyDoubleTest.class);
             addTestSuite(SetPropertyInputStreamTest.class);
             addTestSuite(SetPropertyLongTest.class);
-            // addTestSuite(SetPropertyNodeTest.class);
+            addTestSuite(SetPropertyNodeTest.class);
             addTestSuite(SetPropertyStringTest.class);
             addTestSuite(SetPropertyValueTest.class);
             addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
@@ -228,16 +234,16 @@
             // addTestSuite(WorkspaceCopyReferenceableTest.class);
             // addTestSuite(WorkspaceCopySameNameSibsTest.class);
             // addTestSuite(WorkspaceCopyTest.class);
-            // addTestSuite(WorkspaceCopyVersionableTest.class);
+            addTestSuite(WorkspaceCopyVersionableTest.class);
             // addTestSuite(WorkspaceMoveReferenceableTest.class);
             // addTestSuite(WorkspaceMoveSameNameSibsTest.class);
             // addTestSuite(WorkspaceMoveTest.class);
-            // addTestSuite(WorkspaceMoveVersionableTest.class);
-            //
+            addTestSuite(WorkspaceMoveVersionableTest.class);
+
             addTestSuite(RepositoryLoginTest.class);
             // addTestSuite(ImpersonateTest.class);
             // addTestSuite(CheckPermissionTest.class);
-            //
+
             // addTestSuite(DocumentViewImportTest.class);
             // addTestSuite(SerializationTest.class);
 
@@ -257,7 +263,7 @@
             // addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
             // addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
             // addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
-            // addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
+            addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
         }
     }
 
@@ -265,11 +271,11 @@
      * Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
      */
     public static class InMemoryRepositoryStub extends RepositoryStub {
-        private Repository repository;
-        protected RepositoryConnection connection;
+        private JcrRepository repository;
+        protected InMemoryRepositorySource source;
         protected AccessControlContext accessControlContext = AccessController.getContext();
 
-        private Credentials credentials = new Credentials() {
+        private Credentials superUserCredentials = new Credentials() {
             private static final long serialVersionUID = 1L;
 
             @SuppressWarnings( "unused" )
@@ -278,17 +284,29 @@
             }
         };
 
-        protected ExecutionContext executionContext = new ExecutionContext() {
+        private Credentials readWriteCredentials = new Credentials() {
+            private static final long serialVersionUID = 1L;
 
-            @Override
-            public ExecutionContext create( AccessControlContext accessControlContext ) {
-                return executionContext;
+            @SuppressWarnings( "unused" )
+            public AccessControlContext getAccessControlContext() {
+                return accessControlContext;
             }
         };
 
+        private Credentials readOnlyCredentials = new Credentials() {
+            private static final long serialVersionUID = 1L;
+
+            @SuppressWarnings( "unused" )
+            public AccessControlContext getAccessControlContext() {
+                return accessControlContext;
+            }
+        };
+
+        protected ExecutionContext executionContext = new ExecutionContext();
+
         protected RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
             public RepositoryConnection createConnection( String sourceName ) {
-                return connection;
+                return source.getConnection();
             }
         };
 
@@ -296,29 +314,27 @@
             super(env);
 
             // Create the in-memory (DNA) repository
-            InMemoryRepositorySource source = new InMemoryRepositorySource();
+            source = new InMemoryRepositorySource();
 
             // Various calls will fail if you do not set a non-null name for the source
             source.setName("TestRepositorySource");
 
+            // Make sure the path to the namespaces exists ...
+            Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
+            graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
+
             // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
             Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
-            connection = source.getConnection();
+
             repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(),
                                            null, options);
+            RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
 
-            // Make sure the path to the namespaces exists ...
-            Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
-            graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
-
             // Set up some sample nodes in the graph to match the expected test configuration
             try {
+                nodeTypes.registerNodeTypes(new TckTestNodeTypeSource(executionContext, nodeTypes));
 
-                // TODO: Should there be an easier way to define these since they will be needed for all JCR repositories?
-                executionContext.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
-                executionContext.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
-                executionContext.getNamespaceRegistry().register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
-                executionContext.getNamespaceRegistry().register(JcrSvLexicon.Namespace.PREFIX, JcrSvLexicon.Namespace.URI);
+                executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
 
                 Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
                 GraphImporter importer = new GraphImporter(graph);
@@ -341,9 +357,7 @@
          */
         @Override
         public Credentials getSuperuserCredentials() {
-            // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
-            // SimpleCredentials.
-            return credentials;
+            return superUserCredentials;
         }
 
         /**
@@ -353,9 +367,7 @@
          */
         @Override
         public Credentials getReadOnlyCredentials() {
-            // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
-            // SimpleCredentials.
-            return credentials;
+            return readOnlyCredentials;
         }
 
         /**
@@ -365,9 +377,7 @@
          */
         @Override
         public Credentials getReadWriteCredentials() {
-            // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
-            // SimpleCredentials.
-            return credentials;
+            return readWriteCredentials;
         }
 
         /**
@@ -376,10 +386,88 @@
          * @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
          */
         @Override
-        public Repository getRepository() {
+        public JcrRepository getRepository() {
             return repository;
         }
 
     }
 
+    static class TckTestNodeTypeSource extends AbstractJcrNodeTypeSource {
+        /** The list of node types. */
+        private final List<JcrNodeType> nodeTypes;
+
+        TckTestNodeTypeSource( ExecutionContext context,
+                               RepositoryNodeTypeManager nodeTypeManager ) {
+            super(null);
+
+            nodeTypes = new ArrayList<JcrNodeType>();
+
+            JcrNodeType base = nodeTypeManager.getNodeType(JcrNtLexicon.BASE);
+
+            if (base == null) {
+                String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.NO_SAME_NAME_SIBS.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            JcrNodeType referenceable = nodeTypeManager.getNodeType(JcrMixLexicon.REFERENCEABLE);
+
+            if (referenceable == null) {
+                String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            JcrNodeType unstructured = nodeTypeManager.getNodeType(JcrNtLexicon.UNSTRUCTURED);
+
+            if (unstructured == null) {
+                String baseTypeName = JcrNtLexicon.UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            // Stubbing in child node and property definitions for now
+            JcrNodeType noSameNameSibs = new JcrNodeType(
+                                                         context,
+                                                         NO_NODE_TYPE_MANAGER,
+                                                         TestLexicon.NO_SAME_NAME_SIBS,
+                                                         Arrays.asList(new JcrNodeType[] {base}),
+                                                         NO_PRIMARY_ITEM_NAME,
+                                                         Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                      context,
+                                                                                                                      null,
+                                                                                                                      ALL_NODES,
+                                                                                                                      OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                      new JcrNodeType[] {base}),}),
+                                                         NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType referenceableUnstructured = new JcrNodeType(
+                                                                    context,
+                                                                    NO_NODE_TYPE_MANAGER,
+                                                                    TestLexicon.REFERENCEABLE_UNSTRUCTURED,
+                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                    NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES,
+                                                                    NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {referenceableUnstructured, noSameNameSibs}));
+
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
+        }
+
+    }
+
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-16 15:26:06 UTC (rev 833)
@@ -46,5 +46,8 @@
     public static final Name CONSTRAINED_PATH = new BasicName(Namespace.URI, "constrainedPath");
     public static final Name CONSTRAINED_REFERENCE = new BasicName(Namespace.URI, "constrainedReference");
     public static final Name CONSTRAINED_STRING = new BasicName(Namespace.URI, "constrainedString");
-    
+
+    public static final Name REFERENCEABLE_UNSTRUCTURED = new BasicName(Namespace.URI, "referenceableUnstructured");
+    public static final Name NO_SAME_NAME_SIBS = new BasicName(Namespace.URI, "noSameNameSibs");
+
 }

Modified: trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-04-16 15:26:06 UTC (rev 833)
@@ -28,7 +28,8 @@
           xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
           xmlns:dna="http://www.jboss.org/dna/1.0"
           jcr:primaryType="nt:unstructured">
-    <nt:unstructured jcr:name="node1" prop1="&lt;foo&amp;foo&gt;" >
+    <nt:unstructured jcr:name="serializationNode" />
+    <nt:unstructured jcr:name="node1" prop1="&lt;foo&amp;foo&gt;">
        <!--  
            This stanza checks for the jcr:xmltext special case for export as per JCR 1.0.1 section 6.4.2.3
          -->

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-16 14:53:11 UTC (rev 832)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-16 15:26:06 UTC (rev 833)
@@ -7,5 +7,30 @@
 javax.jcr.tck.propertyname1=prop1
 javax.jcr.tck.propertyname2=prop2
 javax.jcr.tck.workspacename=
-javax.jcr.tck.nodetype=nt:unstructured
+javax.jcr.tck.nodetype=dnatest\:referenceableUnstructured
 javax.jcr.tck.nodetypenochildren=dna:namespace
+javax.jcr.tck.sourceFolderName=source
+javax.jcr.tck.targetFolderName=target
+javax.jcr.tck.rootNodeName=rootNode
+javax.jcr.tck.propertySkipped=propertySkipped
+javax.jcr.tck.propertyValueMayChange=propertyValueMayChange
+javax.jcr.tck.nodeTypesTestNode=nodeTypesTestNode
+javax.jcr.tck.mixinTypeTestNode=mixinTypeTestNode
+javax.jcr.tck.propertyTypesTestNode=propertyTypesTestNode
+javax.jcr.tck.sameNameChildrenTestNode=sameNameChildrenTestNode
+javax.jcr.tck.multiValuePropertiesTestNode=multiValuePropertiesTestNode
+javax.jcr.tck.referenceableNodeTestNode=referenceableNodeTestNode
+javax.jcr.tck.orderChildrenTestNode=orderChildrenTestNode
+javax.jcr.tck.namespaceTestNode=namespaceTestNode
+javax.jcr.tck.sameNameSibsFalseChildNodeDefinition=dnatest\:noSameNameSibs
+javax.jcr.tck.stringTestProperty=stringTestProperty
+javax.jcr.tck.binaryTestProperty=binaryTestProperty
+javax.jcr.tck.dateTestProperty=dateTestProperty
+javax.jcr.tck.longTestProperty=longTestProperty
+javax.jcr.tck.doubleTestProperty=doubleTestProperty
+javax.jcr.tck.booleanTestProperty=booleanTestProperty
+javax.jcr.tck.nameTestProperty=nameTestProperty
+javax.jcr.tck.pathTestProperty=pathTestProperty
+javax.jcr.tck.referenceTestProperty=referenceTestProperty
+javax.jcr.tck.multiValueTestProperty=multiValueTestProperty
+javax.jcr.tck.NodeTest.testAddNodeItemExistsException.nodetype=dnatest\:noSameNameSibs
\ No newline at end of file



From dna-commits at lists.jboss.org  Thu Apr 16 11:31:15 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 11:31:15 -0400
Subject: [dna-commits] DNA SVN: r834 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LuTYZ-0001eu-7h@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 11:31:14 -0400 (Thu, 16 Apr 2009)
New Revision: 834

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-361 SessionCache Throws Wrong Exception Type For Invalid Item State

Applied the patch (removing the duplicate lines from a previous patch).

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 15:26:06 UTC (rev 833)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 15:31:14 UTC (rev 834)
@@ -336,7 +336,7 @@
             try {
                 operations.execute();
             } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-                throw new PathNotFoundException(e.getLocalizedMessage(), e);
+                throw new InvalidItemStateException(e.getLocalizedMessage(), e);
             } catch (RuntimeException e) {
                 throw new RepositoryException(e.getLocalizedMessage(), e);
             }
@@ -442,7 +442,7 @@
                 changedNodes.keySet().removeAll(uuidsUnderBranch);
                 deletedNodes.keySet().removeAll(uuidsUnderBranch);
             } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-                throw new PathNotFoundException(e.getLocalizedMessage(), e);
+                throw new InvalidItemStateException(e.getLocalizedMessage(), e);
             } catch (RuntimeException e) {
                 throw new RepositoryException(e.getLocalizedMessage(), e);
             }



From dna-commits at lists.jboss.org  Thu Apr 16 12:08:24 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 12:08:24 -0400
Subject: [dna-commits] DNA SVN: r835 - in trunk/dna-jcr/src:
	main/java/org/jboss/dna/jcr/cache and 2 other directories.
Message-ID: <E1LuU8W-0004pp-CF@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 12:08:24 -0400 (Thu, 16 Apr 2009)
New Revision: 835

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NewNodeInfo.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/NewNodeInfoTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
Log:
DNA-362 AbstractJcrItem.isNew and isModified Are Not Implemented

Applied the patch, but rather than have ChangedNodeInfo contain a boolean that says whether its new, there is now a NewNodeInfo (that extends ChangedNodeInfo) that IMO makes it more obvious that this is a new node and clears up the places in SessionCache that instantiate it.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -82,26 +82,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @return <code>false</code>
-     * @see javax.jcr.Item#isModified()
-     */
-    public final boolean isModified() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @return <code>false</code>
-     * @see javax.jcr.Item#isNew()
-     */
-    public final boolean isNew() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see javax.jcr.Item#isSame(javax.jcr.Item)
      */
     public boolean isSame( Item otherItem ) throws RepositoryException {

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-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -1448,6 +1448,34 @@
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Item#isModified()
+     */
+    public final boolean isModified() {
+        try {
+            return nodeInfo().isModified();
+        }
+        catch (RepositoryException re) {
+            throw new IllegalStateException(re);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Item#isNew()
+     */
+    public final boolean isNew() {
+        try {
+            return nodeInfo().isNew();
+        }
+        catch (RepositoryException re) {
+            throw new IllegalStateException(re);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#merge(java.lang.String, boolean)
      */

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -154,6 +154,34 @@
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Item#isModified()
+     */
+    public final boolean isModified() {
+        try {
+            return propertyInfo().isModified();
+        }
+        catch (RepositoryException re) {
+            throw new IllegalStateException(re);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Item#isNew()
+     */
+    public final boolean isNew() {
+        try {
+            return propertyInfo().isNew();
+        }
+        catch (RepositoryException re) {
+            throw new IllegalStateException(re);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @return false
      * @see javax.jcr.Item#isNode()
      */

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -77,6 +77,7 @@
 import org.jboss.dna.jcr.cache.EmptyChildren;
 import org.jboss.dna.jcr.cache.ImmutableChildren;
 import org.jboss.dna.jcr.cache.ImmutableNodeInfo;
+import org.jboss.dna.jcr.cache.NewNodeInfo;
 import org.jboss.dna.jcr.cache.NodeInfo;
 import org.jboss.dna.jcr.cache.PropertyInfo;
 import com.google.common.base.ReferenceType;
@@ -728,7 +729,8 @@
             Property dnaProp = propertyFactory.create(name, objValue);
 
             // Create the property info ...
-            PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
+            PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple(),
+                                                        existing == null, existing != null);
 
             // Finally update the cached information and record the change ...
             node.setProperty(newProperty, factories());
@@ -841,7 +843,8 @@
             Property dnaProp = propertyFactory.create(name, objValues);
 
             // Create the property info ...
-            PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
+            PropertyInfo newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple(),
+                                                        existing == null, existing != null);
 
             // Finally update the cached information and record the change ...
             node.setProperty(newProperty, factories());
@@ -1040,7 +1043,7 @@
                 JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
                 org.jboss.dna.graph.property.Property uuidProperty = propertyFactory.create(JcrLexicon.UUID, desiredUuid);
                 PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty,
-                                                         defn.isMultiple());
+                                                         defn.isMultiple(), true, false);
                 properties.put(JcrLexicon.UUID, propInfo);
             }
 
@@ -1052,7 +1055,8 @@
                                                                               false);
             PropertyDefinitionId primaryTypeDefinitionId = primaryTypeDefn.getId();
             PropertyInfo primaryTypeInfo = new PropertyInfo(new PropertyId(desiredUuid, primaryTypeProp.getName()),
-                                                            primaryTypeDefinitionId, PropertyType.NAME, primaryTypeProp, false);
+                                                            primaryTypeDefinitionId, PropertyType.NAME, primaryTypeProp, false,
+                                                            true, false);
             properties.put(primaryTypeProp.getName(), primaryTypeInfo);
 
             // Create the property info for the "dna:nodeDefinition" child property ...
@@ -1065,15 +1069,13 @@
                 PropertyDefinitionId nodeDefnDefinitionId = nodeDefnDefn.getId();
                 PropertyInfo nodeDefinitionInfo = new PropertyInfo(new PropertyId(desiredUuid, nodeDefinitionProp.getName()),
                                                                    nodeDefnDefinitionId, PropertyType.STRING, nodeDefinitionProp,
-                                                                   true);
+                                                                   true, true, false);
                 properties.put(nodeDefinitionProp.getName(), nodeDefinitionInfo);
             }
 
             // Now create the child node info, putting it in the changed map (and not the cache map!) ...
-            NodeInfo info = new ImmutableNodeInfo(location, primaryTypeName, null, definition.getId(), node.getUuid(), null,
-                                                  properties);
-            ChangedNodeInfo changedInfo = new ChangedNodeInfo(info);
-            changedNodes.put(desiredUuid, changedInfo);
+            NewNodeInfo newInfo = new NewNodeInfo(location, primaryTypeName, definition.getId(), node.getUuid(), properties);
+            changedNodes.put(desiredUuid, newInfo);
 
             // ---------------------------------------
             // Now record the changes to the store ...
@@ -1741,7 +1743,7 @@
             // Record the property in the node information ...
             PropertyId propId = new PropertyId(uuid, name);
             JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
-            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), propertyType, dnaProp, defn.isMultiple());
+            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), propertyType, dnaProp, defn.isMultiple(), false, false);
             props.put(name, propInfo);
         }
 
@@ -1757,7 +1759,8 @@
                                                                                        false);
             PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
             JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
-            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
+            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple(),
+                                                     false, false);
             props.put(JcrLexicon.UUID, propInfo);
         } else {
             // Make sure there is NOT a "jcr:uuid" property ...

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -379,4 +379,22 @@
         changedProperties.put(name, null);
         return changed;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#isNew()
+     */
+    public boolean isNew() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#isModified()
+     */
+    public boolean isModified() {
+        return true;
+    }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -179,4 +179,24 @@
     public PropertyInfo getProperty( Name name ) {
         return this.properties.get(name);
     }
+
+    /**
+     * 
+     * {@inheritDoc}
+     * @return {@code false} always as this object represents unmodified nodes only
+     * @see org.jboss.dna.jcr.cache.NodeInfo#isNew()
+     */
+    public boolean isNew() {
+        return false;
+    }
+
+    /**
+     * 
+     * {@inheritDoc}
+     * @return {@code false} always as this object represents unmodified nodes only
+     * @see org.jboss.dna.jcr.cache.NodeInfo#isModified()
+     */
+    public boolean isModified() {
+        return false;
+    }
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NewNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NewNodeInfo.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NewNodeInfo.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ *
+ * 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.cache;
+
+import java.util.Map;
+import java.util.UUID;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * 
+ */
+public class NewNodeInfo extends ChangedNodeInfo {
+    /**
+     * Create an immutable NodeInfo instance.
+     * 
+     * @param originalLocation the original location
+     * @param primaryTypeName the name of the node's primary type
+     * @param definition the definition used when creating the node
+     * @param parent the parent
+     * @param properties the unmodifiable map of properties; may be null if there are no properties
+     */
+    public NewNodeInfo( Location originalLocation,
+                        Name primaryTypeName,
+                        NodeDefinitionId definition,
+                        UUID parent,
+                        Map<Name, PropertyInfo> properties ) {
+        super(new ImmutableNodeInfo(originalLocation, primaryTypeName, null, definition, parent, null, properties));
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Always returns true.
+     * </p>
+     * 
+     * @see org.jboss.dna.jcr.cache.ChangedNodeInfo#isNew()
+     */
+    @Override
+    public boolean isNew() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Always returns false, since a new node is not persisted yet.
+     * </p>
+     * 
+     * @see org.jboss.dna.jcr.cache.ChangedNodeInfo#isModified()
+     */
+    @Override
+    public boolean isModified() {
+        return false;
+    }
+
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NewNodeInfo.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -104,4 +104,23 @@
      * @return the property information, or null if this node has no property with the supplied name
      */
     public PropertyInfo getProperty( Name name );
+
+    /**
+     * Indicates whether the node represented by this {@link NodeInfo} is new (i.e., does not yet exist in the persistent
+     * repository).
+     * 
+     * @return {@code true} if the node represented by this {@link NodeInfo} has not yet been saved to the persistent repository.
+     * @see javax.jcr.Item#isNew()
+     */
+    public boolean isNew();
+
+    /**
+     * Indicates whether the node represented by this {@link NodeInfo} is modified (i.e., exists in the persistent repository with
+     * different child items).
+     * 
+     * @return {@code true} if the immediate child items of the node represented by this {@link NodeInfo} have been modified since
+     *         the last time the node was saved to the persistent repository
+     * @see javax.jcr.Item#isModified()
+     */
+    public boolean isModified();
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -48,17 +48,26 @@
     private final Property dnaProperty;
     private final int propertyType;
     private final boolean multiValued;
+    private final boolean isNew;
+    private final boolean isModified;
 
     public PropertyInfo( PropertyId propertyId,
                          PropertyDefinitionId definitionId,
                          int propertyType,
                          Property dnaProperty,
-                         boolean multiValued ) {
+                         boolean multiValued,
+                         boolean isNew,
+                         boolean isModified ) {
         this.propertyId = propertyId;
         this.definitionId = definitionId;
         this.propertyType = propertyType;
         this.dnaProperty = dnaProperty;
         this.multiValued = multiValued;
+        this.isNew = isNew;
+        this.isModified = isModified;
+
+        assert isNew ? !isModified : true;
+        assert isModified ? !isNew : true;
     }
 
     /**
@@ -123,6 +132,27 @@
     }
 
     /**
+     * Indicates whether this property/value combination is new (i.e., does not yet exist in the persistent repository).
+     * 
+     * @return {@code true} if the property has not yet been saved to the persistent repository.
+     * @see javax.jcr.Item#isNew()
+     */
+    public boolean isNew() {
+        return this.isNew;
+    }
+
+    /**
+     * Indicates whether this property/value combination is modified (i.e., exists in the persistent repository with a different
+     * value or values).
+     * 
+     * @return {@code true} if the property has been modified since the last time it was saved to the persistent repository
+     * @see javax.jcr.Item#isModified()
+     */
+    public boolean isModified() {
+        return this.isModified;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Object#hashCode()

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -73,6 +73,14 @@
                 return false;
             }
 
+            public boolean isNew() {
+                return false;
+            }
+
+            public boolean isModified() {
+                return false;
+            }
+
             public void refresh( boolean keepChanges ) {
                 throw new UnsupportedOperationException();
             }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java	2009-04-16 15:31:14 UTC (rev 834)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -151,6 +151,16 @@
     }
 
     @Test
+    public void shouldNotBeNew() {
+        assertThat(changes.isNew(), is(false));
+    }
+
+    @Test
+    public void shouldBeModified() {
+        assertThat(changes.isModified(), is(true));
+    }
+
+    @Test
     public void shouldHaveLocationFromOriginal() {
         assertThat(changes.getOriginalLocation(), is(sameInstance(location)));
     }

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/NewNodeInfoTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/NewNodeInfoTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/NewNodeInfoTest.java	2009-04-16 16:08:24 UTC (rev 835)
@@ -0,0 +1,672 @@
+/*
+ * 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.
+ *
+ * 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.cache;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingNames.hasChildren;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingUuids.hasChildren;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.NodeDefinitionId;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class NewNodeInfoTest {
+
+    private ExecutionContext context;
+    private PathFactory pathFactory;
+    private UUID uuid;
+    private Name primaryTypeName;
+    private Name[] requiredPrimaryTypes;
+    private Location location;
+    private NodeDefinitionId definitionId;
+    private Map<Name, PropertyInfo> properties;
+    private ChangedNodeInfo changes;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        context.getNamespaceRegistry().register("acme", "http://example.com/acme");
+        pathFactory = context.getValueFactories().getPathFactory();
+
+        // Set up the original ...
+        uuid = UUID.randomUUID();
+        location = Location.create(uuid);
+        primaryTypeName = name("acme:geniusType");
+        requiredPrimaryTypes = new Name[] {name("acme:requiredTypeA"), name("acme:requiredTypeB")};
+        definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"), requiredPrimaryTypes);
+        properties = new HashMap<Name, PropertyInfo>();
+
+        // Create the changed node representation ...
+        changes = new NewNodeInfo(location, primaryTypeName, definitionId, uuid, properties);
+    }
+
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Segment segment( String segment ) {
+        return pathFactory.createSegment(segment);
+    }
+
+    /**
+     * Utility to set a property to the original node representation. This will replace any existing property with the same name.
+     * 
+     * @param name the name of the property; may not be null
+     * @return the new property representation; never null
+     */
+    protected PropertyInfo makePropertyInOriginal( String name ) {
+        Name propName = name(name);
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        stub(propertyInfo.getPropertyName()).toReturn(propName);
+        properties.put(propName, propertyInfo);
+        return propertyInfo;
+    }
+
+    /**
+     * Utility to change a property in the changed representation.
+     * 
+     * @param name the name of the property to change; may not be null
+     * @return the new property; never null
+     */
+    protected PropertyInfo setPropertyInChanged( String name ) {
+        Name propName = name(name);
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        stub(propertyInfo.getPropertyName()).toReturn(propName);
+        changes.setProperty(propertyInfo, context.getValueFactories());
+        return propertyInfo;
+    }
+
+    protected ChildNode addChildInChanged( String childName ) {
+        ChildNode newChild = changes.addChild(name(childName), UUID.randomUUID(), pathFactory);
+        return newChild;
+    }
+
+    protected void removeChildFromChanged( ChildNode child ) {
+        // Verify that a child node with the supplied UUID is contained.
+        // Note that it may not be the same ChildNode instance if another SNS node with smaller index was removed
+        assertThat(changes.getChildren().getChild(child.getUuid()), is(notNullValue()));
+        // Now remove the child, making sure the result is the same as the next 'getChildren()' call ...
+        assertThat(changes.removeChild(child.getUuid(), pathFactory), is(notNullValue()));
+        // Verify it no longer exists ...
+        assertThat(changes.getChildren().getChild(child.getUuid()), is(nullValue()));
+    }
+
+    @Test
+    public void shouldBeNew() {
+        assertThat(changes.isNew(), is(true));
+    }
+
+    @Test
+    public void shouldNotBeModified() {
+        assertThat(changes.isModified(), is(false));
+    }
+
+    @Test
+    public void shouldInitiallyHaveLocation() {
+        assertThat(changes.getOriginalLocation(), is(sameInstance(location)));
+    }
+
+    @Test
+    public void shouldInitiallyHaveParentUuid() {
+        assertThat(changes.getParent(), is(sameInstance(uuid)));
+    }
+
+    @Test
+    public void shouldInitiallyHavePrimaryTypeName() {
+        assertThat(changes.getPrimaryTypeName(), is(sameInstance(primaryTypeName)));
+    }
+
+    @Test
+    public void shouldInitiallyHaveMixinNoTypeNames() {
+        assertThat(changes.getMixinTypeNames().size(), is(0));
+    }
+
+    @Test
+    public void shouldUpdateMixinTypeNamesWhenSettingJcrMixinTypeProperty() {
+        // Create the DNA property ...
+        Property mixinTypes = context.getPropertyFactory().create(JcrLexicon.MIXIN_TYPES, "dna:type1", "dna:type2");
+
+        // Modify the property ...
+        PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
+        stub(newPropertyInfo.getPropertyName()).toReturn(mixinTypes.getName());
+        stub(newPropertyInfo.getProperty()).toReturn(mixinTypes);
+        PropertyInfo previous = changes.setProperty(newPropertyInfo, context.getValueFactories());
+        assertThat(previous, is(nullValue()));
+
+        // Verify that the mixin types were updated ...
+        assertThat(changes.getProperty(name("jcr:mixinTypes")), is(sameInstance(newPropertyInfo)));
+        assertThat(changes.getMixinTypeNames(), hasItems(name("dna:type2"), name("dna:type1")));
+    }
+
+    @Test
+    public void shouldHaveNodeDefinitionId() {
+        assertThat(changes.getDefinitionId(), is(sameInstance(definitionId)));
+    }
+
+    @Test
+    public void shouldHaveNoChildren() {
+        assertThat(changes.getChildren().size(), is(0));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingChild() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add child ...
+        ChildNode childA1 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(1));
+        assertThat(changes.getChildren(), hasChildren(segment("childA")));
+
+        // Add more children ...
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        ChildNode childA3 = addChildInChanged("childA");
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(4));
+        assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+                                                      segment("childB[1]"),
+                                                      segment("childA[2]"),
+                                                      segment("childA[3]")));
+        assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid(), childA3.getUuid()));
+        assertThat(changes.getChildren(), hasItems(childA1, childB1, childA2, childA3));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingMultipleChildren() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(6));
+        assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+                                                      segment("childB[1]"),
+                                                      segment("childA[2]"),
+                                                      segment("childA[3]"),
+                                                      segment("childC[1]"),
+                                                      segment("childC[2]")));
+        assertThat(changes.getChildren(), hasChildren(childA1.getUuid(),
+                                                      childB1.getUuid(),
+                                                      childA2.getUuid(),
+                                                      childA3.getUuid(),
+                                                      childC1.getUuid(),
+                                                      childC2.getUuid()));
+        assertThat(changes.getChildren(), hasItems(childA1, childB1, childA2, childA3, childC1, childC2));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingMultipleChildrenAndRemovingOthers() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Delete a child that was added and another that was an original ...
+        removeChildFromChanged(childC1);
+        removeChildFromChanged(childA2);
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(4));
+        assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+                                                      segment("childB[1]"),
+                                                      segment("childA[2]"),
+                                                      segment("childC[1]")));
+        assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA3.getUuid(), childC2.getUuid()));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingMultipleChildrenAndThenRemovingThoseJustAdded() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Delete a child that was added and another that was an original...
+        removeChildFromChanged(childA3);
+        removeChildFromChanged(childC1); // causes replacement of 'childC2' with lower SNS index
+        removeChildFromChanged(childC2);
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(3));
+        assertThat(changes.getChildren(), hasChildren(segment("childA[1]"), segment("childB[1]"), segment("childA[2]")));
+        assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid()));
+
+        // Do it again, but change the order of delete to delete from the back ...
+
+        // Add some children in the changed representation ...
+        childA3 = addChildInChanged("childA");
+        childC1 = addChildInChanged("childC");
+        childC2 = addChildInChanged("childC");
+
+        // Delete a child that was added and another that was an original ...
+        removeChildFromChanged(childC2);
+        removeChildFromChanged(childC1);
+        removeChildFromChanged(childA3);
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(3));
+        assertThat(changes.getChildren(), hasChildren(segment("childA[1]"), segment("childB[1]"), segment("childA[2]")));
+        assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid()));
+
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterDeletingChild() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Delete a child that was added and another that was an original ...
+        removeChildFromChanged(childA1);
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(2));
+        assertThat(changes.getChildren(), hasChildren(segment("childB[1]"), segment("childA[1]")));
+        assertThat(changes.getChildren(), hasChildren(childB1.getUuid(), childA2.getUuid()));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterDeletingMultipleChildren() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Delete a child that was added and another that was an original ...
+        removeChildFromChanged(childA1);
+        removeChildFromChanged(childA2);
+
+        // Verify that all children are there in the proper order ...
+        assertThat(changes.getChildren().size(), is(1));
+        assertThat(changes.getChildren(), hasChildren(segment("childB[1]")));
+        assertThat(changes.getChildren(), hasChildren(childB1.getUuid()));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterDeletingAllChildrenFromTheFirsttChildToTheLast() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Delete all children, from the front to the back ...
+        removeChildFromChanged(childA1); // causes replacement of 'childA2' with lower SNS index
+        removeChildFromChanged(childA2);
+        removeChildFromChanged(childB1);
+
+        // Verify that all children have been removed ...
+        assertThat(changes.getChildren().size(), is(0));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterDeletingAllChildrenFromTheLastChildToTheFirst() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Delete all children, from the back to the front ...
+        removeChildFromChanged(childA2);
+        removeChildFromChanged(childB1);
+        removeChildFromChanged(childA1);
+
+        // Verify that all children have been removed ...
+        assertThat(changes.getChildren().size(), is(0));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingSomeChildrenThenDeletingAllChildrenFromTheFirstChildToTheLast() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Delete all children, from the front to the back ...
+        removeChildFromChanged(childA3);
+        removeChildFromChanged(childC1); // causes replacement of 'childC2' with lower SNS index
+        removeChildFromChanged(childC2);
+        removeChildFromChanged(childA1); // causes replacement of 'childA2' with lower SNS index
+        removeChildFromChanged(childB1);
+        removeChildFromChanged(childA2);
+
+        // Verify that all children have been removed ...
+        assertThat(changes.getChildren().size(), is(0));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingSomeChildrenThenDeletingAllChildrenFromTheLastChildToTheFirst() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Delete all children, from the back to the front ...
+        removeChildFromChanged(childC2);
+        removeChildFromChanged(childC1);
+        removeChildFromChanged(childA3);
+        removeChildFromChanged(childA2);
+        removeChildFromChanged(childB1);
+        removeChildFromChanged(childA1);
+
+        // Verify that all children have been removed ...
+        assertThat(changes.getChildren().size(), is(0));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterReorderingChildren() {
+
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldNotRemoveFromNodeInfoWithNoChildChangesAChildThatMatchesSegmentButNotUuid() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
+        Children before = changes.getChildren();
+        int beforeSize = before.size();
+        assertThat(changes.removeChild(UUID.randomUUID(), pathFactory), is(nullValue()));
+        Children after = changes.getChildren();
+        assertThat(after.size(), is(beforeSize));
+        assertThat(after, is(sameInstance(before)));
+        assertThat(after, is(sameInstance(changes.getChildren())));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldNotRemoveFromNodeInfoWithSomeChildChangesAChildThatMatchesSegmentButNotUuid() {
+        assertThat(changes.getChildren().size(), is(0));
+
+        // Add some children ...
+        ChildNode childA1 = addChildInChanged("childA");
+        ChildNode childB1 = addChildInChanged("childB");
+        ChildNode childA2 = addChildInChanged("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add some children in the changed representation ...
+        ChildNode childA3 = addChildInChanged("childA");
+        ChildNode childC1 = addChildInChanged("childC");
+        ChildNode childC2 = addChildInChanged("childC");
+
+        // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
+        Children before = changes.getChildren();
+        int beforeSize = before.size();
+        assertThat(changes.removeChild(UUID.randomUUID(), pathFactory), is(nullValue()));
+        Children after = changes.getChildren();
+        assertThat(after.size(), is(beforeSize));
+        assertThat(after, is(sameInstance(before)));
+        assertThat(after, is(sameInstance(changes.getChildren())));
+    }
+
+    @Test
+    public void shouldFindPropertyThatHasNotBeenModifiedButIsInOriginal() {
+        PropertyInfo propertyInfo = makePropertyInOriginal("test");
+        assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+    }
+
+    @Test
+    public void shouldFindPropertyThatHasBeenModifiedFromOriginal() {
+        PropertyInfo propertyInfo = makePropertyInOriginal("test");
+        assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+
+        // Modify the property ...
+        PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
+        stub(newPropertyInfo.getPropertyName()).toReturn(name("test"));
+        PropertyInfo previous = changes.setProperty(newPropertyInfo, context.getValueFactories());
+        assertThat(previous, is(sameInstance(propertyInfo)));
+
+        // Verify we can find the new property ...
+        assertThat(changes.getProperty(name("test")), is(sameInstance(newPropertyInfo)));
+    }
+
+    @Test
+    public void shouldNotFindPropertyThatHasBeenDeletedFromOriginal() {
+        PropertyInfo propertyInfo = makePropertyInOriginal("test");
+        assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+
+        // Delete the property ...
+        PropertyInfo previous = changes.removeProperty(name("test"));
+        assertThat(previous, is(sameInstance(propertyInfo)));
+
+        // Verify we can not find the new property ...
+        assertThat(changes.getProperty(name("test")), is(nullValue()));
+    }
+
+    @Test
+    public void shouldNotFindPropertyThatIsNotInOriginal() {
+        assertThat(changes.getProperty(name("test")), is(nullValue()));
+
+        makePropertyInOriginal("test");
+        assertThat(changes.getProperty(name("nonExistant")), is(nullValue()));
+    }
+
+    @Test
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasNoChangedProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(4));
+        assertThat(names, hasItems(propA.getPropertyName(),
+                                   propB.getPropertyName(),
+                                   propC.getPropertyName(),
+                                   propD.getPropertyName()));
+    }
+
+    @Test
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasDeletedAllProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Remove all properties ...
+        assertThat(changes.removeProperty(propA.getPropertyName()), is(sameInstance(propA)));
+        assertThat(changes.removeProperty(propB.getPropertyName()), is(sameInstance(propB)));
+        assertThat(changes.removeProperty(propC.getPropertyName()), is(sameInstance(propC)));
+        assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(0));
+    }
+
+    @Test
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasDeletedSomeProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Remove some properties ...
+        assertThat(changes.removeProperty(propB.getPropertyName()), is(sameInstance(propB)));
+        assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(2));
+        assertThat(names, hasItems(propA.getPropertyName(), propC.getPropertyName()));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedSomeProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Change some properties ...
+        PropertyInfo propB2 = setPropertyInChanged("propB");
+        PropertyInfo propC2 = setPropertyInChanged("propC");
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(4));
+        assertThat(names, hasItems(propA.getPropertyName(),
+                                   propB2.getPropertyName(),
+                                   propC2.getPropertyName(),
+                                   propD.getPropertyName()));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasAddedSomeProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Add some properties ...
+        PropertyInfo propE = setPropertyInChanged("propE");
+        PropertyInfo propF = setPropertyInChanged("propF");
+        PropertyInfo propG = setPropertyInChanged("propG");
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(7));
+        assertThat(names, hasItems(propA.getPropertyName(),
+                                   propB.getPropertyName(),
+                                   propC.getPropertyName(),
+                                   propD.getPropertyName(),
+                                   propE.getPropertyName(),
+                                   propF.getPropertyName()));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedAndDeletedAndAddedSomeProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Change some properties ...
+        PropertyInfo propB2 = setPropertyInChanged("propB");
+        PropertyInfo propC2 = setPropertyInChanged("propC");
+        // Add some properties ...
+        PropertyInfo propE = setPropertyInChanged("propE");
+        // Remove some properties ...
+        assertThat(changes.removeProperty(propB2.getPropertyName()), is(sameInstance(propB2)));
+        assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(3));
+        assertThat(names, hasItems(propA.getPropertyName(), propC2.getPropertyName(), propE.getPropertyName()));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedAndDeletedAllProperties() {
+        PropertyInfo propA = makePropertyInOriginal("propA");
+        PropertyInfo propB = makePropertyInOriginal("propB");
+        PropertyInfo propC = makePropertyInOriginal("propC");
+        PropertyInfo propD = makePropertyInOriginal("propD");
+        // Change some properties ...
+        PropertyInfo propB2 = setPropertyInChanged("propB");
+        PropertyInfo propC2 = setPropertyInChanged("propC");
+        // Remove all properties ...
+        assertThat(changes.removeProperty(propA.getPropertyName()), is(sameInstance(propA)));
+        assertThat(changes.removeProperty(propB2.getPropertyName()), is(sameInstance(propB2)));
+        assertThat(changes.removeProperty(propC2.getPropertyName()), is(sameInstance(propC2)));
+        assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+        // Now ask for names
+        Set<Name> names = changes.getPropertyNames();
+        assertThat(names.size(), is(0));
+    }
+
+}


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



From dna-commits at lists.jboss.org  Thu Apr 16 13:50:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 16 Apr 2009 13:50:46 -0400
Subject: [dna-commits] DNA SVN: r836 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LuVja-0001gu-R5@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-16 13:50:46 -0400 (Thu, 16 Apr 2009)
New Revision: 836

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrLexicon.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrNtLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
Log:
DNA-363 Define node types via CND files

The first step of several.  This change moves some of the names in the JCR-related lexicons from the "dna-jcr" project's lexicons into the "dna-graph" project's corresponding lexicons.  

Also added to the "nt:nodeType" and "nt:propertyDefinition" node types in JcrBuiltinNodeTypeSource were several additional property definitions new in JSR-283 .  However, because the JSR-170 TCK unit tests are checking that only the JSR-170 property definitions are there, these new definitions have been commented out for the time being.  (JSR-283 also makes all of the property definitions on "nt:nodeType", "nt:propertyDefinition", and "nt:childNodeDefinition" to be protected, though JSR-170 TCK unit tests are checking that they are not.  These also were left as not being protected so that we are JSR-170 compliant.)

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrLexicon.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrLexicon.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -46,4 +46,28 @@
     public static final Name MIMETYPE = new BasicName(Namespace.URI, "mimeType");
     public static final Name DATA = new BasicName(Namespace.URI, "data");
     public static final Name LAST_MODIFIED = new BasicName(Namespace.URI, "lastModified");
+
+    // Names used in the node type definitions ...
+    public static final Name AUTO_CREATED = new BasicName(Namespace.URI, "autoCreated");
+    public static final Name CHILD_NODE_DEFINITION = new BasicName(Namespace.URI, "childNodeDefinition");
+    public static final Name DEFAULT_PRIMARY_TYPE = new BasicName(Namespace.URI, "defaultPrimaryType");
+    public static final Name DEFAULT_VALUES = new BasicName(Namespace.URI, "defaultValues");
+    public static final Name HAS_ORDERABLE_CHILD_NODES = new BasicName(Namespace.URI, "hasOrderableChildNodes");
+    public static final Name IS_ABSTRACT = new BasicName(Namespace.URI, "isAbstract");
+    public static final Name IS_FULL_TEXT_SEARCHABLE = new BasicName(Namespace.URI, "isFullTextSearchable");
+    public static final Name IS_MIXIN = new BasicName(Namespace.URI, "isMixin");
+    public static final Name IS_QUERY_ORDERABLE = new BasicName(Namespace.URI, "isQueryOrderable");
+    public static final Name IS_QUERYABLE = new BasicName(Namespace.URI, "isQueryable");
+    public static final Name MANDATORY = new BasicName(Namespace.URI, "mandatory");
+    public static final Name MULTIPLE = new BasicName(Namespace.URI, "multiple");
+    public static final Name ON_PARENT_VERSION = new BasicName(Namespace.URI, "onParentVersion");
+    public static final Name PRIMARY_ITEM_NAME = new BasicName(Namespace.URI, "primaryItemName");
+    public static final Name PROPERTY_DEFINITION = new BasicName(Namespace.URI, "propertyDefinition");
+    public static final Name PROTECTED = new BasicName(Namespace.URI, "protected");
+    public static final Name REQUIRED_PRIMARY_TYPES = new BasicName(Namespace.URI, "requiredPrimaryTypes");
+    public static final Name REQUIRED_TYPE = new BasicName(Namespace.URI, "requiredType");
+    public static final Name SAME_NAME_SIBLINGS = new BasicName(Namespace.URI, "sameNameSiblings");
+    public static final Name SUPERTYPES = new BasicName(Namespace.URI, "supertypes");
+    public static final Name VALUE_CONSTRAINTS = new BasicName(Namespace.URI, "valueConstraints");
+
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrNtLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrNtLexicon.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/JcrNtLexicon.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -41,4 +41,10 @@
     public static final Name FOLDER = new BasicName(Namespace.URI, "folder");
     public static final Name RESOURCE = new BasicName(Namespace.URI, "resource");
     public static final Name BASE = new BasicName(Namespace.URI, "base");
+
+    // Names used in the node type definitions ...
+    public static final Name NODE_TYPE = new BasicName(Namespace.URI, "nodeType");
+    public static final Name CHILD_NODE_DEFINITION = new BasicName(Namespace.URI, "childNodeDefinition");
+    public static final Name PROPERTY_DEFINITION = new BasicName(Namespace.URI, "propertyDefinition");
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -27,6 +27,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.property.Name;
@@ -42,6 +43,12 @@
     protected static final List<JcrNodeType> NO_SUPERTYPES = Collections.<JcrNodeType>emptyList();
     protected static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
     protected static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
+    protected static final String[] ALL_PROPERTY_TYPES_WITH_UNDEFINED = new String[] {
+        PropertyType.nameFromValue(PropertyType.BINARY), PropertyType.nameFromValue(PropertyType.BOOLEAN),
+        PropertyType.nameFromValue(PropertyType.DATE), PropertyType.nameFromValue(PropertyType.DOUBLE),
+        PropertyType.nameFromValue(PropertyType.LONG), PropertyType.nameFromValue(PropertyType.NAME),
+        PropertyType.nameFromValue(PropertyType.PATH), PropertyType.nameFromValue(PropertyType.REFERENCE),
+        PropertyType.nameFromValue(PropertyType.STRING), PropertyType.nameFromValue(PropertyType.UNDEFINED),};
 
     /** Indicates that the node type has no primary item name - added for readability */
     protected static final Name NO_PRIMARY_ITEM_NAME = null;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -38,6 +38,14 @@
 @Immutable
 class JcrBuiltinNodeTypeSource extends AbstractJcrNodeTypeSource {
 
+    protected static final String[] ON_PARENT_VERSION_VALUES = new String[] {OnParentVersionBehavior.ABORT.getName(),
+        OnParentVersionBehavior.COMPUTE.getName(), OnParentVersionBehavior.COPY.getName(),
+        OnParentVersionBehavior.IGNORE.getName(), OnParentVersionBehavior.INITIALIZE.getName(),
+        OnParentVersionBehavior.VERSION.getName(),};
+
+    protected static final boolean NT_NODE_TYPES_PROTECTED = false; // JSR-170 requires they not be protected, but JSR-283 says
+    // they are.
+
     /** The list of primary node types. */
     private final List<JcrNodeType> nodeTypes;
 
@@ -58,28 +66,29 @@
         Value ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
 
         JcrNodeType defined = new JcrNodeType(
-                                                    context,
-                                                    NO_NODE_TYPE_MANAGER,
-                                                    DnaLexicon.DEFINED,
-                                                    NO_SUPERTYPES,
-                                                    DnaLexicon.NODE_DEFINITON,
-                                                    NO_CHILD_NODES,
-                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                         context,
-                                                                                                                         null,
-                                                                                                                         DnaLexicon.NODE_DEFINITON,
-                                                                                                                         OnParentVersionBehavior.INITIALIZE.getJcrValue(),
-                                                                                                                         false,
-                                                                                                                         false,
-                                                                                                                         true,
-                                                                                                                         NO_DEFAULT_VALUES,
-                                                                                                                         PropertyType.STRING,
-                                                                                                                         NO_CONSTRAINTS,
-                                                                                                                         false),}),
-                                                    IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+                                              context,
+                                              NO_NODE_TYPE_MANAGER,
+                                              DnaLexicon.DEFINED,
+                                              NO_SUPERTYPES,
+                                              DnaLexicon.NODE_DEFINITON,
+                                              NO_CHILD_NODES,
+                                              Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                   context,
+                                                                                                                   null,
+                                                                                                                   DnaLexicon.NODE_DEFINITON,
+                                                                                                                   OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                                                                   false,
+                                                                                                                   false,
+                                                                                                                   true,
+                                                                                                                   NO_DEFAULT_VALUES,
+                                                                                                                   PropertyType.STRING,
+                                                                                                                   NO_CONSTRAINTS,
+                                                                                                                   false),}),
+                                              IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
         // Stubbing in child node and property definitions for now
-        JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE, Arrays.asList(new JcrNodeType[] {defined}), NO_PRIMARY_ITEM_NAME,
-                                           NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+        JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE,
+                                           Arrays.asList(new JcrNodeType[] {defined}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                           Arrays.asList(new JcrPropertyDefinition[] {
                                                new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_TYPE,
                                                                          OnParentVersionBehavior.COMPUTE.getJcrValue(), true,
                                                                          true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
@@ -125,53 +134,55 @@
                                                                                         null,
                                                                                         JcrLexicon.AUTO_CREATED,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                        false),
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.DEFAULT_PRIMARY_TYPE,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, false, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                                                        false, false, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.MANDATORY,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                        false),
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.NAME,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, false, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                                                        false, false, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.ON_PARENT_VERSION,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.STRING, NO_CONSTRAINTS,
-                                                                                        false),
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.PROTECTED,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                        false),
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                        NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
                                                                                         context,
                                                                                         null,
                                                                                         JcrLexicon.REQUIRED_PRIMARY_TYPES,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false,
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
                                                                                         new Value[] {ntBaseValue},
                                                                                         PropertyType.NAME, NO_CONSTRAINTS, true),
                                                               new JcrPropertyDefinition(
@@ -179,9 +190,9 @@
                                                                                         null,
                                                                                         JcrLexicon.SAME_NAME_SIBLINGS,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                        false, true, false, NO_DEFAULT_VALUES,
-                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                        false)}), NOT_MIXIN,
+                                                                                        false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                        NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                        NO_CONSTRAINTS, false)}), NOT_MIXIN,
                                                           UNORDERABLE_CHILD_NODES);
 
         JcrNodeType hierarchyNode = new JcrNodeType(
@@ -315,104 +326,142 @@
                                                                                        null,
                                                                                        JcrLexicon.AUTO_CREATED,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                       false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                       NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.DEFAULT_VALUES,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, false, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.UNDEFINED, NO_CONSTRAINTS,
-                                                                                       true),
+                                                                                       false, false, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
+                                                                                       NO_CONSTRAINTS, true),
+                                                             // new JcrPropertyDefinition(
+                                                             // context,
+                                                             // null,
+                                                             // JcrLexicon.IS_FULL_TEXT_SEARCHABLE,
+                                                             // OnParentVersionBehavior.COPY.getJcrValue(),
+                                                             // false, true, NT_NODE_TYPES_PROTECTED,
+                                                             // NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                             // NO_CONSTRAINTS, false),
+                                                             // new JcrPropertyDefinition(
+                                                             // context,
+                                                             // null,
+                                                             // JcrLexicon.IS_QUERY_ORDERABLE,
+                                                             // OnParentVersionBehavior.COPY.getJcrValue(),
+                                                             // false, true, NT_NODE_TYPES_PROTECTED,
+                                                             // NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                             // NO_CONSTRAINTS, false),
+                                                             // new JcrPropertyDefinition(
+                                                             // context,
+                                                             // null,
+                                                             // JcrLexicon.IS_QUERYABLE,
+                                                             // OnParentVersionBehavior.COPY.getJcrValue(),
+                                                             // false, true, NT_NODE_TYPES_PROTECTED,
+                                                             // NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                             // NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.MANDATORY,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                       false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                       NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.MULTIPLE,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                       false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                       NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.NAME,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, false, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                                                       false, false, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                                       NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.ON_PARENT_VERSION,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                                       ON_PARENT_VERSION_VALUES, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.PROTECTED,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
-                                                                                       false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                                                       NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.REQUIRED_TYPE,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, true, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                                                       false, true, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                                       ALL_PROPERTY_TYPES_WITH_UNDEFINED, false),
                                                              new JcrPropertyDefinition(
                                                                                        context,
                                                                                        null,
                                                                                        JcrLexicon.VALUE_CONSTRAINTS,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                       false, false, false, NO_DEFAULT_VALUES,
-                                                                                       PropertyType.STRING, NO_CONSTRAINTS, true)}),
-                                                         NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+                                                                                       false, false, NT_NODE_TYPES_PROTECTED,
+                                                                                       NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                                       NO_CONSTRAINTS, true)}), NOT_MIXIN,
+                                                         UNORDERABLE_CHILD_NODES);
 
         JcrNodeType nodeType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.NODE_TYPE,
                                                Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME,
                                                Arrays.asList(new JcrNodeDefinition[] {
                                                    new JcrNodeDefinition(context, null, JcrLexicon.CHILD_NODE_DEFINITION,
                                                                          OnParentVersionBehavior.VERSION.getJcrValue(), false,
-                                                                         false, false, true, JcrNtLexicon.CHILD_NODE_DEFINITION,
+                                                                         false, NT_NODE_TYPES_PROTECTED, true,
+                                                                         JcrNtLexicon.CHILD_NODE_DEFINITION,
                                                                          new JcrNodeType[] {childNodeDefinition}),
                                                    new JcrNodeDefinition(context, null, JcrLexicon.PROPERTY_DEFINITION,
                                                                          OnParentVersionBehavior.VERSION.getJcrValue(), false,
-                                                                         false, false, true, JcrNtLexicon.PROPERTY_DEFINITION,
+                                                                         false, NT_NODE_TYPES_PROTECTED, true,
+                                                                         JcrNtLexicon.PROPERTY_DEFINITION,
                                                                          new JcrNodeType[] {propertyDefinition})}),
                                                Arrays.asList(new JcrPropertyDefinition[] {
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                             true, false, NO_DEFAULT_VALUES,
+                                                                             true, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES,
                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                   // new JcrPropertyDefinition(context, null, JcrLexicon.IS_ABSTRACT,
+                                                   // OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                   // true, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                   // NO_CONSTRAINTS, false),
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.IS_MIXIN,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                             true, false, NO_DEFAULT_VALUES,
+                                                                             true, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES,
                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                   // new JcrPropertyDefinition(context, null, JcrLexicon.IS_QUERYABLE,
+                                                   // OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                   // true, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES, PropertyType.BOOLEAN,
+                                                   // NO_CONSTRAINTS, false),
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.NODE_TYPE_NAME,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                             true, false, NO_DEFAULT_VALUES, PropertyType.NAME,
-                                                                             NO_CONSTRAINTS, false),
+                                                                             true, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES,
+                                                                             PropertyType.NAME, NO_CONSTRAINTS, false),
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_ITEM_NAME,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                             false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
-                                                                             NO_CONSTRAINTS, false),
+                                                                             false, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES,
+                                                                             PropertyType.NAME, NO_CONSTRAINTS, false),
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.SUPERTYPES,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                             false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
-                                                                             NO_CONSTRAINTS, true),}), NOT_MIXIN,
-                                               UNORDERABLE_CHILD_NODES);
+                                                                             false, NT_NODE_TYPES_PROTECTED, NO_DEFAULT_VALUES,
+                                                                             PropertyType.NAME, NO_CONSTRAINTS, true),}),
+                                               NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType query = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.QUERY,
                                             Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
@@ -585,9 +634,9 @@
         // NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         // Disabling version-related types until DNA supports versioning, as per section 4.11 of the 1.0.1 specification
-        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {defined, base, unstructured, childNodeDefinition, file, folder, frozenNode,
-            hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType /*, version, versionHistory,
-                   versionLabels, versionedChild */}));
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {defined, base, unstructured, childNodeDefinition, file, folder,
+            frozenNode, hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType /*, version, versionHistory,
+                                                                                                              versionLabels, versionedChild */}));
 
         JcrNodeType lockable = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES,
                                                NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -31,47 +31,29 @@
  */
 class JcrLexicon extends org.jboss.dna.graph.JcrLexicon {
 
-    public static final Name AUTO_CREATED = new BasicName(Namespace.URI, "autoCreated");
     public static final Name BASE_VERSION = new BasicName(Namespace.URI, "baseVersion");
-    public static final Name CHILD_NODE_DEFINITION = new BasicName(Namespace.URI, "childNodeDefinition");
     public static final Name CONTENT = new BasicName(Namespace.URI, "content");
     public static final Name CREATED = new BasicName(Namespace.URI, "created");
     public static final Name DATA = new BasicName(Namespace.URI, "data");
-    public static final Name DEFAULT_PRIMARY_TYPE = new BasicName(Namespace.URI, "defaultPrimaryType");
-    public static final Name DEFAULT_VALUES = new BasicName(Namespace.URI, "defaultValues");
     public static final Name ENCODING = new BasicName(Namespace.URI, "encoding");
     public static final Name FROZEN_MIXIN_TYPES = new BasicName(Namespace.URI, "frozenMixinTypes");
     public static final Name FROZEN_NODE = new BasicName(Namespace.URI, "frozenNode");
     public static final Name FROZEN_PRIMARY_TYPE = new BasicName(Namespace.URI, "frozenPrimaryType");
     public static final Name FROZEN_UUID = new BasicName(Namespace.URI, "frozenUuid");
-    public static final Name HAS_ORDERABLE_CHILD_NODES = new BasicName(Namespace.URI, "hasOrderableChildNodes");
     public static final Name IS_CHECKED_OUT = new BasicName(Namespace.URI, "isCheckedOut");
-    public static final Name IS_MIXIN = new BasicName(Namespace.URI, "isMixin");
     public static final Name LANGUAGE = new BasicName(Namespace.URI, "language");
     public static final Name LAST_MODIFIED = new BasicName(Namespace.URI, "lastModified");
     public static final Name LOCK_IS_DEEP = new BasicName(Namespace.URI, "lockIsDeep");
     public static final Name LOCK_OWNER = new BasicName(Namespace.URI, "lockOwner");
-    public static final Name MANDATORY = new BasicName(Namespace.URI, "mandatory");
     public static final Name MERGE_FAILED = new BasicName(Namespace.URI, "mergeFailed");
-    public static final Name MULTIPLE = new BasicName(Namespace.URI, "multiple");
-    public static final Name NAME = new BasicName(Namespace.URI, "name");
     public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");
     public static final Name NODE_TYPE_NAME = new BasicName(Namespace.URI, "nodeTypeName");
-    public static final Name ON_PARENT_VERSION = new BasicName(Namespace.URI, "onParentVersion");
     public static final Name PREDECESSORS = new BasicName(Namespace.URI, "predecessors");
-    public static final Name PRIMARY_ITEM_NAME = new BasicName(Namespace.URI, "primaryItemName");
-    public static final Name PROPERTY_DEFINITION = new BasicName(Namespace.URI, "propertyDefinition");
-    public static final Name PROTECTED = new BasicName(Namespace.URI, "protected");
-    public static final Name REQUIRED_PRIMARY_TYPES = new BasicName(Namespace.URI, "requiredPrimaryTypes");
-    public static final Name REQUIRED_TYPE = new BasicName(Namespace.URI, "requiredType");
     public static final Name ROOT = new BasicName(Namespace.URI, "root");
     public static final Name ROOT_VERSION = new BasicName(Namespace.URI, "rootVersion");
-    public static final Name SAME_NAME_SIBLINGS = new BasicName(Namespace.URI, "sameNameSiblings");
     public static final Name STATEMENT = new BasicName(Namespace.URI, "statement");
     public static final Name SUCCESSORS = new BasicName(Namespace.URI, "successors");
-    public static final Name SUPERTYPES = new BasicName(Namespace.URI, "supertypes");
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
-    public static final Name VALUE_CONSTRAINTS = new BasicName(Namespace.URI, "valueConstraints");
     public static final Name VERSIONABLE_UUID = new BasicName(Namespace.URI, "versionableUuid");
     public static final Name VERSION_HISTORY = new BasicName(Namespace.URI, "versionHistory");
     public static final Name VERSION_LABELS = new BasicName(Namespace.URI, "versionLabels");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	2009-04-16 16:08:24 UTC (rev 835)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	2009-04-16 17:50:46 UTC (rev 836)
@@ -28,12 +28,9 @@
 
 public class JcrNtLexicon extends org.jboss.dna.graph.JcrNtLexicon {
 
-    public static final Name CHILD_NODE_DEFINITION = new BasicName(Namespace.URI, "childNodeDefinition");
     public static final Name FROZEN_NODE = new BasicName(Namespace.URI, "frozenNode");
     public static final Name HIERARCHY_NODE = new BasicName(Namespace.URI, "hierarchyNode");
     public static final Name LINKED_FILE = new BasicName(Namespace.URI, "linkedFile");
-    public static final Name NODE_TYPE = new BasicName(Namespace.URI, "nodeType");
-    public static final Name PROPERTY_DEFINITION = new BasicName(Namespace.URI, "propertyDefinition");
     public static final Name QUERY = new BasicName(Namespace.URI, "query");
     public static final Name VERSION = new BasicName(Namespace.URI, "version");
     public static final Name VERSIONED_CHILD = new BasicName(Namespace.URI, "versionedChild");



From dna-commits at lists.jboss.org  Fri Apr 17 09:54:29 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 17 Apr 2009 09:54:29 -0400
Subject: [dna-commits] DNA SVN: r837 - in trunk/dna-jcr/src:
	main/java/org/jboss/dna/jcr/cache and 1 other directories.
Message-ID: <E1LuoWT-0000vH-Ps@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-17 09:54:29 -0400 (Fri, 17 Apr 2009)
New Revision: 837

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-359 Session.refresh and Node.refresh Methods Not Supported

Applied the second patch, which corrects how the nodes under the refreshed node are computed (to also take into account nodes that were removed from the subgraph).

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-16 17:50:46 UTC (rev 836)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-17 13:54:29 UTC (rev 837)
@@ -280,10 +280,14 @@
             UUID uuid = nodesToVisit.pop();
             nodesUnderBranch.add(uuid);
 
-            NodeInfo nodeInfo = cachedNodes.get(uuid);
-            // Newly added nodes will be changedNodes but not cachedNodes
-            if (nodeInfo == null) nodeInfo = changedNodes.get(uuid);
-
+            NodeInfo nodeInfo = null;
+            ChangedNodeInfo changedInfo = this.changedNodes.get(uuid);
+            if (changedInfo != null) {
+                nodesToVisit.addAll(changedInfo.getUuidsForRemovedChildren());
+                nodeInfo = changedInfo;
+            } else {
+                nodeInfo = this.cachedNodes.get(uuid);
+            }
             if (nodeInfo != null) {
                 for (ChildNode childNode : nodeInfo.getChildren()) {
                     nodesToVisit.add(childNode.getUuid());

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-16 17:50:46 UTC (rev 836)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-17 13:54:29 UTC (rev 837)
@@ -23,6 +23,8 @@
  */
 package org.jboss.dna.jcr.cache;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -95,6 +97,15 @@
     }
 
     /**
+     * Return the original node information. May be null if this is a new node.
+     * 
+     * @return the original node information
+     */
+    public NodeInfo getOriginal() {
+        return original;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getOriginalLocation()
@@ -195,6 +206,23 @@
     }
 
     /**
+     * Get the UUIDs for the children for this node that have been removed since the node was last persisted.
+     * 
+     * @return a collection of the UUIDs of the removed children; never null but possibly empty
+     */
+    public Collection<UUID> getUuidsForRemovedChildren() {
+        if (original == null) return Collections.emptySet();
+
+        Set<UUID> removedChildren = new HashSet<UUID>();
+        for (ChildNode originalChildNode : original.getChildren()) {
+            if (!this.changedChildren.childrenByUuid.containsKey(originalChildNode.getUuid())) {
+                removedChildren.add(originalChildNode.getUuid());
+            }
+        }
+        return removedChildren;
+    }
+
+    /**
      * Add a child to the children. This method does nothing if the child is already in the children.
      * 
      * @param childName the name of the child that is to be added; may not be null

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-16 17:50:46 UTC (rev 836)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-17 13:54:29 UTC (rev 837)
@@ -43,7 +43,11 @@
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
 import org.apache.jackrabbit.test.api.NodeAddMixinTest;
 import org.apache.jackrabbit.test.api.NodeCanAddMixinTest;
+import org.apache.jackrabbit.test.api.NodeItemIsModifiedTest;
+import org.apache.jackrabbit.test.api.NodeItemIsNewTest;
 import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.PropertyItemIsModifiedTest;
+import org.apache.jackrabbit.test.api.PropertyItemIsNewTest;
 import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
 import org.apache.jackrabbit.test.api.SessionUUIDTest;
@@ -213,12 +217,12 @@
             addTestSuite(SetPropertyValueTest.class);
             addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
             // addTestSuite(SetPropertyAssumeTypeTest.class);
-            //
-            // addTestSuite(NodeItemIsModifiedTest.class);
-            // addTestSuite(NodeItemIsNewTest.class);
-            // addTestSuite(PropertyItemIsModifiedTest.class);
-            // addTestSuite(PropertyItemIsNewTest.class);
 
+            addTestSuite(NodeItemIsModifiedTest.class);
+            addTestSuite(NodeItemIsNewTest.class);
+            addTestSuite(PropertyItemIsModifiedTest.class);
+            addTestSuite(PropertyItemIsNewTest.class);
+
             addTestSuite(NodeAddMixinTest.class);
             addTestSuite(NodeCanAddMixinTest.class);
             addTestSuite(NodeRemoveMixinTest.class);



From dna-commits at lists.jboss.org  Fri Apr 17 10:21:31 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 17 Apr 2009 10:21:31 -0400
Subject: [dna-commits] DNA SVN: r838 - in trunk/dna-jcr/src/main:
	resources/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Luowd-0001Pj-5q@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-17 10:21:31 -0400 (Fri, 17 Apr 2009)
New Revision: 838

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
Log:
DNA-359 Session.refresh and Node.refresh Methods Not Supported

Applied the patch that figures out whether any nodes that were removed from the branch were not deleted (meaning they were moved to another branch).  Note that this does handle the case where a node was moved to another parent within the branch being refreshed.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-17 13:54:29 UTC (rev 837)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-17 14:21:31 UTC (rev 838)
@@ -83,6 +83,7 @@
     public static I18n unableToSaveNodeThatWasCreatedSincePreviousSave;
     public static I18n unableToSetMultiValuedPropertyUsingSingleValue;
     public static I18n unableToSetSingleValuedPropertyUsingMultipleValues;
+    public static I18n unableToRefreshBranchSinceAtLeastOneNodeMovedToParentOutsideOfBranch;
     public static I18n allPropertyValuesMustHaveSameType;
 
     public static I18n unableToRemoveRootNode;
@@ -126,7 +127,7 @@
     public static I18n cannotRedefineProperty;
     public static I18n autocreatedPropertyNeedsDefault;
     public static I18n singleValuedPropertyNeedsSingleValuedDefault;
-    
+
     static {
         try {
             I18n.initialize(JcrI18n.class);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-17 13:54:29 UTC (rev 837)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-17 14:21:31 UTC (rev 838)
@@ -268,12 +268,10 @@
      */
     public void refresh( UUID nodeUuid,
                          boolean keepChanges ) throws RepositoryException {
-        // Find the path of the given node ...
-        Path path = getPathFor(nodeUuid);
-
         // Build the set of affected node UUIDs
         Set<UUID> nodesUnderBranch = new HashSet<UUID>();
         Stack<UUID> nodesToVisit = new Stack<UUID>();
+        Set<UUID> nodesRemovedFromBranch = new HashSet<UUID>();
 
         nodesToVisit.add(nodeUuid);
         while (!nodesToVisit.isEmpty()) {
@@ -283,7 +281,9 @@
             NodeInfo nodeInfo = null;
             ChangedNodeInfo changedInfo = this.changedNodes.get(uuid);
             if (changedInfo != null) {
-                nodesToVisit.addAll(changedInfo.getUuidsForRemovedChildren());
+                Collection<UUID> removedNodes = changedInfo.getUuidsForRemovedChildren();
+                nodesToVisit.addAll(removedNodes);
+                nodesRemovedFromBranch.addAll(removedNodes);
                 nodeInfo = changedInfo;
             } else {
                 nodeInfo = this.cachedNodes.get(uuid);
@@ -295,6 +295,23 @@
             }
         }
 
+        if (!nodesRemovedFromBranch.isEmpty()) {
+            // Skip any nodes that were moved from one parent to another within this branch ...
+            nodesRemovedFromBranch.removeAll(nodesUnderBranch);
+            // Skip any nodes that were actually deleted (not moved)...
+            nodesRemovedFromBranch.removeAll(this.deletedNodes.keySet());
+            if (!nodesRemovedFromBranch.isEmpty()) {
+                // There was at least one node that was moved from this branch to another parent outside this branch
+                Path path = getPathFor(nodeUuid);
+                String msg = JcrI18n.unableToRefreshBranchSinceAtLeastOneNodeMovedToParentOutsideOfBranch.text(path,
+                                                                                                               workspaceName());
+                throw new RepositoryException(msg);
+            }
+        }
+
+        // Find the path of the given node ...
+        Path path = getPathFor(nodeUuid);
+
         if (keepChanges) {
             // Keep the pending operations
             for (UUID uuid : nodesUnderBranch) {

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-17 13:54:29 UTC (rev 837)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-17 14:21:31 UTC (rev 838)
@@ -73,6 +73,7 @@
 unableToSaveNodeThatWasCreatedSincePreviousSave = Unable to save node "{0}" in workspace "{1}" because it was created since the last save
 unableToSetMultiValuedPropertyUsingSingleValue = Unable to set existing multi-valued property "{0}" on node "{1}" in workspace "{2}" using single-value setter methods
 unableToSetSingleValuedPropertyUsingMultipleValues = Unable to set existing single-valued property "{0}" on node "{1}" in workspace "{2}" using multi-value setter methods
+unableToRefreshBranchSinceAtLeastOneNodeMovedToParentOutsideOfBranch = Unable to refresh "{0}" in workspace "{2}" because at least one of its decendants was moved to another node outside of the branch that is not being refreshed 
 allPropertyValuesMustHaveSameType = All values of property "{0}" on node "{3}" in workspace "{4}" must all be {2} values (values were: {1})
 
 unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"



From dna-commits at lists.jboss.org  Fri Apr 17 10:23:57 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 17 Apr 2009 10:23:57 -0400
Subject: [dna-commits] DNA SVN: r839 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Luoyz-0002O1-Ig@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-17 10:23:57 -0400 (Fri, 17 Apr 2009)
New Revision: 839

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-359 Session.refresh and Node.refresh Methods Not Supported

Added a short-circuit for 'refresh(UUID,boolean)' that checks whether the supplied UUID is that of the root, and if so 'refresh(boolean)' is called since it is significantly more efficient.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-17 14:21:31 UTC (rev 838)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-17 14:23:57 UTC (rev 839)
@@ -268,6 +268,13 @@
      */
     public void refresh( UUID nodeUuid,
                          boolean keepChanges ) throws RepositoryException {
+        assert nodeUuid != null;
+        // If the node being refreshed is the root node, then it's more efficient to refresh the whole workspace ...
+        if (this.root.equals(nodeUuid)) {
+            refresh(keepChanges);
+            return;
+        }
+
         // Build the set of affected node UUIDs
         Set<UUID> nodesUnderBranch = new HashSet<UUID>();
         Stack<UUID> nodesToVisit = new Stack<UUID>();



From dna-commits at lists.jboss.org  Mon Apr 20 15:07:48 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 20 Apr 2009 15:07:48 -0400
Subject: [dna-commits] DNA SVN: r840 - in trunk: dna-cnd and 31 other
	directories.
Message-ID: <E1LvyqK-00024J-8d@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-20 15:07:48 -0400 (Mon, 20 Apr 2009)
New Revision: 840

Added:
   trunk/dna-cnd/
   trunk/dna-cnd/.classpath
   trunk/dna-cnd/.project
   trunk/dna-cnd/.settings/
   trunk/dna-cnd/.settings/org.eclipse.jdt.core.prefs
   trunk/dna-cnd/pom.xml
   trunk/dna-cnd/src/
   trunk/dna-cnd/src/main/
   trunk/dna-cnd/src/main/antlr/
   trunk/dna-cnd/src/main/antlr/org/
   trunk/dna-cnd/src/main/antlr/org/jboss/
   trunk/dna-cnd/src/main/antlr/org/jboss/dna/
   trunk/dna-cnd/src/main/antlr/org/jboss/dna/cnd/
   trunk/dna-cnd/src/main/antlr/org/jboss/dna/cnd/Cnd.g
   trunk/dna-cnd/src/main/antlr3/
   trunk/dna-cnd/src/main/antlr3/org/
   trunk/dna-cnd/src/main/antlr3/org/jboss/
   trunk/dna-cnd/src/main/antlr3/org/jboss/dna/
   trunk/dna-cnd/src/main/antlr3/org/jboss/dna/cnd/
   trunk/dna-cnd/src/main/antlr3/org/jboss/dna/cnd/Cnd.g
   trunk/dna-cnd/src/main/java/
   trunk/dna-cnd/src/main/java/org/
   trunk/dna-cnd/src/main/java/org/jboss/
   trunk/dna-cnd/src/main/java/org/jboss/dna/
   trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/
   trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndI18n.java
   trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java
   trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/QueryOperator.java
   trunk/dna-cnd/src/main/resources/
   trunk/dna-cnd/src/main/resources/org/
   trunk/dna-cnd/src/main/resources/org/jboss/
   trunk/dna-cnd/src/main/resources/org/jboss/dna/
   trunk/dna-cnd/src/main/resources/org/jboss/dna/cnd/
   trunk/dna-cnd/src/main/resources/org/jboss/dna/cnd/CndI18n.properties
   trunk/dna-cnd/src/test/
   trunk/dna-cnd/src/test/java/
   trunk/dna-cnd/src/test/java/org/
   trunk/dna-cnd/src/test/java/org/jboss/
   trunk/dna-cnd/src/test/java/org/jboss/dna/
   trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/
   trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java
   trunk/dna-cnd/src/test/resources/
   trunk/dna-cnd/src/test/resources/cnd/
   trunk/dna-cnd/src/test/resources/cnd/empty.cnd
   trunk/dna-cnd/src/test/resources/cnd/images.cnd
   trunk/dna-cnd/src/test/resources/cnd/invalid.cnd
   trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd
   trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-283-early-draft.cnd
   trunk/dna-cnd/src/test/resources/cnd/mp3.cnd
   trunk/dna-cnd/src/test/resources/log4j.properties
Modified:
   trunk/pom.xml
Log:
DNA-363 Define node types via CND files

Added a new 'dna-cnd' project containing a graph importer that reads JCR CND files and populates a repository with the content (based upon the JCR 'nt:nodeType', 'nt:propertyDefinition', and 'nt:childNodeDefinition').


Property changes on: trunk/dna-cnd
___________________________________________________________________
Name: svn:ignore
   + target


Added: trunk/dna-cnd/.classpath
===================================================================
--- trunk/dna-cnd/.classpath	                        (rev 0)
+++ trunk/dna-cnd/.classpath	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" path="src/main/antlr3"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="src" path="target/generated-sources/antlr3"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Added: trunk/dna-cnd/.project
===================================================================
--- trunk/dna-cnd/.project	                        (rev 0)
+++ trunk/dna-cnd/.project	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna-cnd</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>
+        <buildCommand>
+            <name>org.antlr.eclipse.core.warningcleanerbuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+        <buildCommand>
+            <name>org.antlr.eclipse.core.smapbuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

Added: trunk/dna-cnd/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- trunk/dna-cnd/.settings/org.eclipse.jdt.core.prefs	                        (rev 0)
+++ trunk/dna-cnd/.settings/org.eclipse.jdt.core.prefs	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,60 @@
+#Fri Apr 17 14:06:37 CDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=error
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=error
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=error
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=ignore
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning

Added: trunk/dna-cnd/pom.xml
===================================================================
--- trunk/dna-cnd/pom.xml	                        (rev 0)
+++ trunk/dna-cnd/pom.xml	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,107 @@
+<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">
+  <parent>
+    <artifactId>dna</artifactId>
+    <groupId>org.jboss.dna</groupId>
+    <version>0.4-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.jboss.dna</groupId>
+  <artifactId>dna-cnd</artifactId>
+  <name>JBoss DNA CND Utility</name>
+  <description>JBoss DNA utility for processing and working with JCR CND files</description>
+  <dependencies>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-graph</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.antlr</groupId>
+      <artifactId>antlr-runtime</artifactId>
+      <version>3.1.3</version>        
+      <exclusions>
+        <exclusion>
+          <groupId>org.antlr</groupId>
+          <artifactId>stringtemplate</artifactId>
+        </exclusion>                                  
+      </exclusions>                        
+    </dependency>
+    
+    <!-- Testing -->
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-graph</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+	  <dependency>
+	    <groupId>org.antlr</groupId>
+	    <artifactId>antlr</artifactId>
+	    <version>3.1.3</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.antlr</groupId>
+          <artifactId>stringtemplate</artifactId>
+        </exclusion>      
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-project</artifactId>
+        </exclusion>                                                                
+      </exclusions>                                             
+	    <!-- we try to use scope to hide it from transitivity -->
+	    <scope>test</scope> <!-- or perhaps 'provided' (see later discussion) or 'import' (maven >= 2.0.9) -->
+	  </dependency>
+		<dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+    </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>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- 
+    Java Concurrency in Practice annotations
+    -->
+    <dependency>
+      <groupId>net.jcip</groupId>
+      <artifactId>jcip-annotations</artifactId>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.antlr</groupId>
+        <artifactId>antlr3-maven-plugin</artifactId>
+        <version>3.1.3-1</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>antlr</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file

Added: trunk/dna-cnd/src/main/antlr/org/jboss/dna/cnd/Cnd.g
===================================================================
--- trunk/dna-cnd/src/main/antlr/org/jboss/dna/cnd/Cnd.g	                        (rev 0)
+++ trunk/dna-cnd/src/main/antlr/org/jboss/dna/cnd/Cnd.g	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,360 @@
+/*
+ * 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.
+ */
+grammar Cnd;
+
+options {
+	output=AST;
+}
+
+
+tokens {
+	NAMESPACES;
+	PREFIX;
+	URI;
+	NODE;
+	NAME;
+	PRIMARY_TYPE;
+	SUPERTYPES;
+	NODE_TYPES;
+	NODE_TYPE_ATTRIBUTES;
+	HAS_ORDERABLE_CHILD_NODES;
+	IS_MIXIN;
+	IS_ABSTRACT;
+	IS_QUERYABLE;
+	PRIMARY_ITEM_NAME;
+	PROPERTY_DEFINITION;
+	REQUIRED_TYPE;
+	DEFAULT_VALUES;
+	VALUE_CONSTRAINTS;
+	AUTO_CREATED;
+	MANDATORY;
+	PROTECTED;
+	REQUIRED_TYPE;
+	ON_PARENT_VERSION;
+	MULTIPLE;
+	QUERY_OPERATORS;
+	IS_FULL_TEXT_SEARCHABLE;
+	IS_QUERY_ORDERERABLE;
+	CHILD_NODE_DEFINITION;
+	REQUIRED_PRIMARY_TYPES;
+	DEFAULT_PRIMARY_TYPE;
+	SAME_NAME_SIBLINGS;
+}
+
+ at header {
+/*
+ * 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.cnd;
+}
+
+ at members {
+ at Override
+public void emitErrorMessage( String msg ) {
+    // don't write messages to System.err ...
+    //super.emitErrorMessage(msg);
+}
+}
+
+ at lexer::header {
+/*
+ * 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.cnd;
+}
+
+ at rulecatch {
+  catch (RecognitionException re) {
+    reportError(re);
+  }
+}
+
+// ------------
+// Parser rules
+// ------------
+
+// These rules generate an AST that was designed with a structure that may be 
+// automatically processed into a graph structure.  This structure is:
+//
+//   NAMESPACES
+//    +- NODE (multiple)
+//        +- PREFIX
+//            +- string value
+//        +- URI
+//            +- string value
+//   NODE_TYPES
+//    +- NODE (multiple)
+//        +- NAME                                 [nt:nodeType/@jcr:name]
+//            +- string value
+//        +- PRIMARY_TYPE                         [nt:base/@jcr:primaryType]
+//            +- string with value 'nt:nodeType'
+//        +- SUPERTYPES                           [nt:nodeType/@jcr:supertypes]
+//            +- string value(s)
+//        +- IS_ABSTRACT                          [nt:nodeType/@jcr:isAbstract]
+//            +- string containing boolean value (or false if not present)
+//        +- HAS_ORDERABLE_CHILD_NODES            [nt:nodeType/@jcr:hasOrderableChildNodes]
+//            +- string containing boolean value (or false if not present)
+//        +- IS_MIXIN                             [nt:nodeType/@jcr:isMixin]
+//            +- string containing boolean value (or false if not present)
+//        +- IS_QUERYABLE                         [nt:nodeType/@jcr:isQueryable]
+//            +- string containing boolean value (or true if not present)
+//        +- PRIMARY_ITEM_NAME                    [nt:nodeType/@jcr:primaryItemName]
+//            +- string containing string value
+//        +- PROPERTY_DEFINITION                  [nt:nodeType/@jcr:propertyDefinition]
+//            +- NODE (multiple)
+//                +- NAME                         [nt:propertyDefinition/@jcr:name]
+//                    +- string value
+//                +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+//                    +- string with value 'nt:propertyDefinition'
+//                +- REQUIRED_TYPE                [nt:propertyDefinition/@jcr:propertyType]
+//                    +- string value (limited to one of the predefined types)
+//                +- DEFAULT_VALUES               [nt:propertyDefinition/@jcr:defaultValues]
+//                    +- string value(s)
+//                +- MULTIPLE                     [nt:propertyDefinition/@jcr:multiple]
+//                    +- string containing boolean value (or false if not present)
+//                +- MANDATORY                    [nt:propertyDefinition/@jcr:mandatory]
+//                    +- string containing boolean value (or false if not present)
+//                +- AUTO_CREATED                 [nt:propertyDefinition/@jcr:autoCreated]
+//                    +- string containing boolean value (or false if not present)
+//                +- PROTECTED                    [nt:propertyDefinition/@jcr:protected]
+//                    +- string containing boolean value (or false if not present)
+//                +- ON_PARENT_VERSION            [nt:propertyDefinition/@jcr:onParentVersion]
+//                    +- string value (limited to one of the predefined literal values)
+//                +- QUERY_OPERATORS              
+//                    +- string value (containing a comma-separated list of operator literals)
+//                +- IS_FULL_TEXT_SEARCHABLE      [nt:propertyDefinition/@jcr:isFullTextSearchable]
+//                    +- string containing boolean value (or true if not present)
+//                +- IS_QUERY_ORDERABLE           [nt:propertyDefinition/@jcr:isQueryOrderable]
+//                    +- string containing boolean value (or true if not present)
+//                +- VALUE_CONSTRAINTS            [nt:propertyDefinition/@jcr:valueConstraints]
+//                    +- string value(s)
+//        +- CHILD_NODE_DEFINITION                [nt:nodeType/@jcr:childNodeDefinition]
+//            +- NODE (multiple)
+//                +- NAME                         [nt:childNodeDefinition/@jcr:name]
+//                    +- string value
+//                +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+//                    +- string with value 'nt:childNodeDefinition'
+//                +- REQUIRED_PRIMARY_TYPES       [nt:childNodeDefinition/@jcr:requiredPrimaryTypes]
+//                    +- string values (limited to names)
+//                +- DEFAULT_PRIMARY_TYPE         [nt:childNodeDefinition/@jcr:defaultPrimaryType]
+//                    +- string value (limited to a name)
+//                +- MANDATORY                    [nt:childNodeDefinition/@jcr:mandatory]
+//                    +- string containing boolean value (or false if not present)
+//                +- AUTO_CREATED                 [nt:childNodeDefinition/@jcr:autoCreated]
+//                    +- string containing boolean value (or false if not present)
+//                +- PROTECTED                    [nt:childNodeDefinition/@jcr:protected]
+//                    +- string containing boolean value (or false if not present)
+//                +- SAME_NAME_SIBLINGS           [nt:childNodeDefinition/@jcr:sameNameSiblings]
+//                    +- string containing boolean value (or false if not present)
+//                +- ON_PARENT_VERSION            [nt:childNodeDefinition/@jcr:onParentVersion]
+//                    +- string value (limited to one of the predefined literal values)
+//
+// Comments
+// --------
+// The JSR-283 specification states that comments are allowed in CND files but are to be removed prior
+// to parsing and processing.  This grammar accomplishes this by sending the MULTI_LINE_COMMENT and
+// SINGLE_LINE_COMMENT tokens to the HIDDEN channel (along with all whitespace tokens).
+//
+// Case sensitivity
+// ----------------
+// ANTLR 3 has no way of generating a case-insensitive lexer/parser (since this is dependent upon locale).
+// However, it's possible to do this as outlined at http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782.
+// Note that the approach that overrides the "LT" method does not change the content but merely changes the
+// character returned by the method, which are the characters used to match the rules.
+//
+// And this must be done for this grammar, since the CND tokens are case-insensitive.  Note that this
+// grammar defines all tokens as lower-case, so the ANTLRFileStream subclass must perform a 'toLowerCase()'
+// rather than a 'toUpperCase()'.
+//
+// Post Processing
+// ---------------
+// A number of the values in the AST must be post-processed to perform the requisite validation.
+//
+// The string values may or may not be wrapped in quotes (single or double).  Any quotes will
+// need to be removed in post-processing.
+//
+// Also, the QUERY_OPERATORS string value should contain a comma-separated list of operators 
+// wrapped by single quotes.  As noted below, this is a side effect of how this grammar's lexer
+// automatically produces a STRING token whenever single-quoted strings are found, so by the
+// time the parser rules are run, the operators are encapsulated in a STRING token.
+// (It's arguable whether this is a defect in the CND grammar, or a side effect of how this grammar
+// uses ANTLR; other parser and lexer rules were considered, but most caused a massive increase in
+// the size of the generated code and a massive decrease in performance.)  In short, these
+// operators must be validated in a post-processing step.
+//
+// Generating JCR content
+// ----------------------
+//
+// The resulting AST's structure was designed such that it directly corresponds to the "nt:nodeType", 
+// "nt:propertyDefinition", and "nt:childNodeDefinition" node types that are specified in JSR-283
+// (proposed draft), in the hopes that this approach could be used with a "standard" AST tree walker
+// that could automatically generate the JCR content.
+//
+// First of all, the structure follows a repeating multi-level pattern, where the "NODE" appears 
+// in the AST when a new node should be created, with JCR properties
+// identified by AST nodes under "NODE" that have a single child (e.g., the property's value),
+// or with JCR child nodes identified by AST nodes under "NODE" that also contain an AST "NODE"
+// (with properties below it).
+//
+// The AST node names were designed to be easily translated into property names.  ANTLR uses
+// all-caps for AST node names and '_' to separate words in those names.  To produce the JCR name,
+// simply convert the first word to lowercase, and convert to lowercase all but the first character
+// of each remaining word, and finally remove all '_' characters.
+//
+// This grammar (mostly) uses the "jcr" prefix (the namespaces should probably be "dna").
+// This could be automated by defining a mapping between the AST node names and the property names
+// (as well as defining a default namespace for any AST node name that is to be converted automatically).
+//
+cnd : (namespaceMapping|nodeTypeDefinition)* EOF
+	-> ^(NAMESPACES namespaceMapping*)? ^(NODE_TYPES nodeTypeDefinition*)? ;
+
+// Namespace mappings 
+namespaceMapping : '<' prefix '=' uri '>' -> ^(NODE prefix uri);
+prefix : STRING -> ^(PREFIX STRING);
+uri : STRING -> ^(URI STRING);
+
+// Node type definitions
+nodeTypeDefinition :	nodeTypeName supertypes? nodeTypeOptions? ( propertyDefinition | childNodeDefinition )* 
+	-> ^(NODE nodeTypeName ^(PRIMARY_TYPE STRING["nt:nodeType"]) supertypes? nodeTypeOptions? ^(PROPERTY_DEFINITION propertyDefinition*) ^(CHILD_NODE_DEFINITION childNodeDefinition*)) ;
+nodeTypeName : '[' STRING ']' -> ^(NAME STRING) ;
+supertypes : '>' stringList -> ^(SUPERTYPES stringList);
+nodeTypeOptions :	nodeTypeOption+;
+nodeTypeOption :	orderable | mixin | isAbstract | noQuery | primaryItem ;
+orderable :	('o'|'ord'|'orderable') -> ^(HAS_ORDERABLE_CHILD_NODES STRING["true"]);
+mixin :	('m' | 'mix' | 'mixin') -> ^(IS_MIXIN STRING["true"]);
+isAbstract :	('a'|'abs'|'abstract') -> ^(IS_ABSTRACT STRING["true"]);
+noQuery :	('nq'|'noquery') -> ^(IS_QUERYABLE STRING["false"]);
+primaryItem : ('primaryitem'|'!')	STRING -> ^(PRIMARY_ITEM_NAME STRING);
+
+// Property definitions ...
+propertyDefinition : propertyName propertyType? defaultValues? ( propertyAttributes | valueConstraints )*
+	-> ^(NODE propertyName ^(PRIMARY_TYPE STRING["nt:propertyDefinition"]) propertyType? defaultValues? propertyAttributes* valueConstraints*);
+propertyName : '-' STRING -> ^(NAME STRING);
+propertyType : '(' propertyTypeLiteral ')' -> ^(REQUIRED_TYPE propertyTypeLiteral); 
+propertyTypeLiteral	: ('string'|'binary'|'long'|'double'|'boolean'|'date'|'name'|'path'|'reference'|'*');
+defaultValues :	'=' stringList -> ^(DEFAULT_VALUES stringList);
+propertyAttributes : ( (onParentVersioningLiteral)=>onParentVersioning | (autoCreated)=> autoCreated | (multiple)=>multiple | (mandatory)=>mandatory | (isProtected)=>isProtected | (queryOperators)=>queryOperators | (noFullText)=>noFullText | (noQueryOrder)=>noQueryOrder)+ ;
+//propertyAttributes : ( (onParentVersioning)=> onParentVersioning | ('a')=>autoCreated | ('m')=>mandatory |('p')=>isProtected | ('mul'|'*')=>multiple | ('q')=>queryOperators | ('nof')=>noFullText | ('nqord'|'noq')=>noQueryOrder)+ ;
+valueConstraints : '<' stringList -> ^(VALUE_CONSTRAINTS stringList);
+autoCreated :	('a'|'aut'|'autocreated') -> ^(AUTO_CREATED STRING["true"]);
+mandatory :	('m'|'man'|'mandatory') -> ^(MANDATORY STRING["true"]);
+isProtected :	('p'|'pro'|'protected') -> ^(PROTECTED STRING["true"]);
+onParentVersioning :	onParentVersioningLiteral -> ^(ON_PARENT_VERSION onParentVersioningLiteral);
+onParentVersioningLiteral : ('copy'|'version'|'initialize'|'compute'|'ignore'|'abort');
+multiple :	('*'|'mul'|'multiple') -> ^(MULTIPLE STRING["true"]);
+noFullText :	('nof'|'nofulltext') -> ^(IS_FULL_TEXT_SEARCHABLE STRING["false"]);
+noQueryOrder :	('nqord'|'noqueryorder') -> ^(IS_QUERY_ORDERERABLE STRING["false"]);
+queryOperators :	('qop'|'queryops') STRING -> ^(QUERY_OPERATORS STRING);
+// The grammar defines the query operators to be wrapped by single quotes, and therefore the lexer produces a single STRING token.
+// Since we cannot break this token up, we simply store the operators as a STRING literal, and will have to process
+// at a later step.
+
+// Child node definitions ...
+childNodeDefinition : nodeName requiredTypes? defaultType? nodeAttributes?
+  -> ^(NODE nodeName ^(PRIMARY_TYPE STRING["nt:childNodeDefinition"]) requiredTypes? defaultType? nodeAttributes?);
+nodeName : '+' STRING -> ^(NAME STRING);
+requiredTypes :'(' stringList ')' -> ^(REQUIRED_PRIMARY_TYPES stringList); 
+defaultType :	'=' STRING -> ^(DEFAULT_PRIMARY_TYPE STRING);
+nodeAttributes : nodeAttribute+;
+nodeAttribute : autoCreated | mandatory | isProtected | onParentVersioning | sns ;
+sns : ('sns'|'*') -> ^(SAME_NAME_SIBLINGS STRING["true"]) ;
+
+// General rules
+stringList : STRING (',' STRING )* -> STRING*;
+
+// ------------
+// Lexer rules
+// ------------
+
+// Comments are currently sent to a separate channel
+MULTI_LINE_COMMENT : ('/*' (options {greedy=false;} : . )* '*/') { $channel=HIDDEN;};
+SINGLE_LINE_COMMENT : '//' ~('\n' | '\r')*  { $channel=HIDDEN;};
+
+// Quoted strings allow for strings containing characters that otherwise would be CND delimiters.
+// Note that the single- and double-quote characters must be escaped within the string.
+// Also note that escape sequences are allowed as well.
+STRING : QUOTED_STRING | UNQUOTED_STRING;
+
+// Quoted strings may contain escaped characters.
+fragment QUOTED_STRING
+    : '"' ( EscapeSequence | ~('\\'|'"'))* '"'
+    | '\'' ( EscapeSequence | ~('\\'|'\''))* '\'' 
+    ;
+
+fragment EscapeSequence 
+  : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\'
+         |('0'..'3') (('0'..'7') ('0'..'7')?)?
+         |'u'(('0'..'9')|('a'..'f')) (('0'..'9')|('a'..'f')) (('0'..'9')|('a'..'f'))
+         )
+;
+
+// An unquoted string is a word delimited by whitespace and CND tokens.
+fragment UNQUOTED_STRING
+: (~(' '|'\r'|'\t'|'\u000C'|'\n'	// whitespace
+    |'='|'<'|'>'|'['|']'|','|'-'|'('|')'	// tokens
+  ))+;
+
+WS : (' '|'\r'|'\t'|'\u000C'|'\n')+	{$channel=HIDDEN;} ;
+
+

Added: trunk/dna-cnd/src/main/antlr3/org/jboss/dna/cnd/Cnd.g
===================================================================
--- trunk/dna-cnd/src/main/antlr3/org/jboss/dna/cnd/Cnd.g	                        (rev 0)
+++ trunk/dna-cnd/src/main/antlr3/org/jboss/dna/cnd/Cnd.g	2009-04-20 19:07:48 UTC (rev 840)
@@ -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.
+ */
+grammar Cnd;
+
+options {
+    output=AST;
+}
+
+
+tokens {
+    NAMESPACES;
+    PREFIX;
+    URI;
+    NODE;
+    NAME;
+    PRIMARY_TYPE;
+    SUPERTYPES;
+    NODE_TYPES;
+    NODE_TYPE_ATTRIBUTES;
+    HAS_ORDERABLE_CHILD_NODES;
+    IS_MIXIN;
+    IS_ABSTRACT;
+    IS_QUERYABLE;
+    PRIMARY_ITEM_NAME;
+    PROPERTY_DEFINITION;
+    REQUIRED_TYPE;
+    DEFAULT_VALUES;
+    VALUE_CONSTRAINTS;
+    AUTO_CREATED;
+    MANDATORY;
+    PROTECTED;
+    REQUIRED_TYPE;
+    ON_PARENT_VERSION;
+    MULTIPLE;
+    IS_PRIMARY_PROPERTY;
+    QUERY_OPERATORS;
+    IS_FULL_TEXT_SEARCHABLE;
+    IS_QUERY_ORDERERABLE;
+    CHILD_NODE_DEFINITION;
+    REQUIRED_PRIMARY_TYPES;
+    DEFAULT_PRIMARY_TYPE;
+    SAME_NAME_SIBLINGS;
+}
+
+ at header {
+/*
+ * 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.cnd;
+}
+
+ at members {
+ at Override
+public void emitErrorMessage( String msg ) {
+    // don't write messages to System.err ...
+    //super.emitErrorMessage(msg);
+}
+}
+
+ at lexer::header {
+/*
+ * 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.cnd;
+}
+
+ at rulecatch {
+  catch (RecognitionException re) {
+    reportError(re);
+  }
+}
+
+// ------------
+// Parser rules
+// ------------
+
+// These rules generate an AST that was designed with a structure that may be 
+// automatically processed into a graph structure.  This structure is:
+//
+//   NAMESPACES
+//    +- NODE (multiple)
+//        +- PREFIX
+//            +- string value
+//        +- URI
+//            +- string value
+//   NODE_TYPES
+//    +- NODE (multiple)
+//        +- NAME                                 [nt:nodeType/@jcr:name]
+//            +- string value
+//        +- PRIMARY_TYPE                         [nt:base/@jcr:primaryType]
+//            +- string with value 'nt:nodeType'
+//        +- SUPERTYPES                           [nt:nodeType/@jcr:supertypes]
+//            +- string value(s)
+//        +- IS_ABSTRACT                          [nt:nodeType/@jcr:isAbstract]
+//            +- string containing boolean value (or false if not present)
+//        +- HAS_ORDERABLE_CHILD_NODES            [nt:nodeType/@jcr:hasOrderableChildNodes]
+//            +- string containing boolean value (or false if not present)
+//        +- IS_MIXIN                             [nt:nodeType/@jcr:isMixin]
+//            +- string containing boolean value (or false if not present)
+//        +- IS_QUERYABLE                         [nt:nodeType/@jcr:isQueryable]
+//            +- string containing boolean value (or true if not present)
+//        +- PRIMARY_ITEM_NAME                    [nt:nodeType/@jcr:primaryItemName]
+//            +- string containing string value
+//        +- PROPERTY_DEFINITION                  [nt:nodeType/@jcr:propertyDefinition]
+//            +- NODE (multiple)
+//                +- NAME                         [nt:propertyDefinition/@jcr:name]
+//                    +- string value
+//                +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+//                    +- string with value 'nt:propertyDefinition'
+//                +- REQUIRED_TYPE                [nt:propertyDefinition/@jcr:propertyType]
+//                    +- string value (limited to one of the predefined types)
+//                +- DEFAULT_VALUES               [nt:propertyDefinition/@jcr:defaultValues]
+//                    +- string value(s)
+//                +- MULTIPLE                     [nt:propertyDefinition/@jcr:multiple]
+//                    +- string containing boolean value (or false if not present)
+//                +- MANDATORY                    [nt:propertyDefinition/@jcr:mandatory]
+//                    +- string containing boolean value (or false if not present)
+//                +- AUTO_CREATED                 [nt:propertyDefinition/@jcr:autoCreated]
+//                    +- string containing boolean value (or false if not present)
+//                +- PROTECTED                    [nt:propertyDefinition/@jcr:protected]
+//                    +- string containing boolean value (or false if not present)
+//                +- ON_PARENT_VERSION            [nt:propertyDefinition/@jcr:onParentVersion]
+//                    +- string value (limited to one of the predefined literal values)
+//                +- QUERY_OPERATORS              
+//                    +- string value (containing a comma-separated list of operator literals)
+//                +- IS_FULL_TEXT_SEARCHABLE      [nt:propertyDefinition/@jcr:isFullTextSearchable]
+//                    +- string containing boolean value (or true if not present)
+//                +- IS_QUERY_ORDERABLE           [nt:propertyDefinition/@jcr:isQueryOrderable]
+//                    +- string containing boolean value (or true if not present)
+//                +- VALUE_CONSTRAINTS            [nt:propertyDefinition/@jcr:valueConstraints]
+//                    +- string value(s)
+//        +- CHILD_NODE_DEFINITION                [nt:nodeType/@jcr:childNodeDefinition]
+//            +- NODE (multiple)
+//                +- NAME                         [nt:childNodeDefinition/@jcr:name]
+//                    +- string value
+//                +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+//                    +- string with value 'nt:childNodeDefinition'
+//                +- REQUIRED_PRIMARY_TYPES       [nt:childNodeDefinition/@jcr:requiredPrimaryTypes]
+//                    +- string values (limited to names)
+//                +- DEFAULT_PRIMARY_TYPE         [nt:childNodeDefinition/@jcr:defaultPrimaryType]
+//                    +- string value (limited to a name)
+//                +- MANDATORY                    [nt:childNodeDefinition/@jcr:mandatory]
+//                    +- string containing boolean value (or false if not present)
+//                +- AUTO_CREATED                 [nt:childNodeDefinition/@jcr:autoCreated]
+//                    +- string containing boolean value (or false if not present)
+//                +- PROTECTED                    [nt:childNodeDefinition/@jcr:protected]
+//                    +- string containing boolean value (or false if not present)
+//                +- SAME_NAME_SIBLINGS           [nt:childNodeDefinition/@jcr:sameNameSiblings]
+//                    +- string containing boolean value (or false if not present)
+//                +- ON_PARENT_VERSION            [nt:childNodeDefinition/@jcr:onParentVersion]
+//                    +- string value (limited to one of the predefined literal values)
+//
+// JSR-283 node types
+// ------------------
+// This grammar does support reading in the new 'decimal', 'weakreference' and 'uri' property types.  Therefore, it
+// is up to the calling component to verify that these types are allowed.  Supporting these new types means
+// that this CND grammar can be used for both JSR-170 and JSR-283.
+//
+// Comments
+// --------
+// The JSR-283 specification states that comments are allowed in CND files but are to be removed prior
+// to parsing and processing.  This grammar accomplishes this by sending the MULTI_LINE_COMMENT and
+// SINGLE_LINE_COMMENT tokens to the HIDDEN channel (along with all whitespace tokens).
+//
+// Case sensitivity
+// ----------------
+// ANTLR 3 has no way of generating a case-insensitive lexer/parser (since this is dependent upon locale).
+// However, it's possible to do this as outlined at http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782.
+// Note that the approach that overrides the "LT" method does not change the content but merely changes the
+// character returned by the method, which are the characters used to match the rules.
+//
+// And this must be done for this grammar, since the CND tokens are case-insensitive.  Note that this
+// grammar defines all tokens as lower-case, so the ANTLRFileStream subclass must perform a 'toLowerCase()'
+// rather than a 'toUpperCase()'.
+//
+// Post Processing
+// ---------------
+// A number of the values in the AST must be post-processed to perform the requisite validation.
+//
+// The string values may or may not be wrapped in quotes (single or double).  Any quotes will
+// need to be removed in post-processing.
+//
+// Also, the QUERY_OPERATORS string value should contain a comma-separated list of operators 
+// wrapped by single quotes.  As noted below, this is a side effect of how this grammar's lexer
+// automatically produces a STRING token whenever single-quoted strings are found, so by the
+// time the parser rules are run, the operators are encapsulated in a STRING token.
+// (It's arguable whether this is a defect in the CND grammar, or a side effect of how this grammar
+// uses ANTLR; other parser and lexer rules were considered, but most caused a massive increase in
+// the size of the generated code and a massive decrease in performance.)  In short, these
+// operators must be validated in a post-processing step.
+//
+// Generating JCR content
+// ----------------------
+//
+// The resulting AST's structure was designed such that it directly corresponds to the "nt:nodeType", 
+// "nt:propertyDefinition", and "nt:childNodeDefinition" node types that are specified in JSR-283
+// (proposed draft), in the hopes that this approach could be used with a "standard" AST tree walker
+// that could automatically generate the JCR content.
+//
+// First of all, the structure follows a repeating multi-level pattern, where the "NODE" appears 
+// in the AST when a new node should be created, with JCR properties
+// identified by AST nodes under "NODE" that have a single child (e.g., the property's value),
+// or with JCR child nodes identified by AST nodes under "NODE" that also contain an AST "NODE"
+// (with properties below it).
+//
+// The AST node names were designed to be easily translated into property names.  ANTLR uses
+// all-caps for AST node names and '_' to separate words in those names.  To produce the JCR name,
+// simply convert the first word to lowercase, and convert to lowercase all but the first character
+// of each remaining word, and finally remove all '_' characters.
+//
+// This grammar (mostly) uses the "jcr" prefix (the namespaces should probably be "dna").
+// This could be automated by defining a mapping between the AST node names and the property names
+// (as well as defining a default namespace for any AST node name that is to be converted automatically).
+//
+cnd : (namespaceMapping|nodeTypeDefinition)* EOF
+    -> ^(NAMESPACES namespaceMapping*)? ^(NODE_TYPES nodeTypeDefinition*)? ;
+
+// Namespace mappings 
+namespaceMapping : '<' prefix '=' uri '>' -> ^(NODE prefix uri);
+prefix :
+  | 'mix' -> ^(PREFIX 'mix')    // Must break out this built-in prefix because it happens to be a keyword
+    |   STRING -> ^(PREFIX STRING);
+uri : STRING -> ^(URI STRING);
+
+// Node type definitions
+nodeTypeDefinition :    nodeTypeName supertypes? nodeTypeOptions? ( propertyDefinition | childNodeDefinition )* 
+    -> ^(NODE nodeTypeName ^(PRIMARY_TYPE STRING["nt:nodeType"]) supertypes? nodeTypeOptions? ^(PROPERTY_DEFINITION propertyDefinition*) ^(CHILD_NODE_DEFINITION childNodeDefinition*)) ;
+nodeTypeName : '[' STRING ']' -> ^(NAME STRING) ;
+supertypes : '>' stringList -> ^(SUPERTYPES stringList);
+nodeTypeOptions :   nodeTypeOption+;
+nodeTypeOption :    orderable | mixin | isAbstract | noQuery | primaryItem ;
+orderable : ('o'|'ord'|'orderable') -> ^(HAS_ORDERABLE_CHILD_NODES STRING["true"]);
+mixin : ('m' | 'mix' | 'mixin') -> ^(IS_MIXIN STRING["true"]);
+isAbstract :    ('a'|'abs'|'abstract') -> ^(IS_ABSTRACT STRING["true"]);
+noQuery :   ('nq'|'noquery') -> ^(IS_QUERYABLE STRING["false"]);
+primaryItem : ('primaryitem'|'!')   STRING -> ^(PRIMARY_ITEM_NAME STRING);
+
+// Property definitions ...
+propertyDefinition : propertyName propertyType? defaultValues? ( propertyAttributes | valueConstraints )*
+    -> ^(NODE propertyName ^(PRIMARY_TYPE STRING["nt:propertyDefinition"]) propertyType? defaultValues? propertyAttributes* valueConstraints*);
+propertyName : '-' ('*' -> ^(NAME '*') | STRING -> ^(NAME STRING));
+propertyType : '(' propertyTypeLiteral ')' -> ^(REQUIRED_TYPE propertyTypeLiteral); 
+propertyTypeLiteral : ('string'|'binary'|'long'|'double'|'boolean'|'decimal'|'date'|'name'|'path'|'reference'|'*'|'undefined'|'weakreference'|'uri');
+defaultValues : '=' stringList -> ^(DEFAULT_VALUES stringList);
+propertyAttributes : ( (isPrimary)=>isPrimary | (onParentVersioningLiteral)=>onParentVersioning | (autoCreated)=> autoCreated | (multiple)=>multiple | (mandatory)=>mandatory | (isProtected)=>isProtected | (queryOperators)=>queryOperators | (noFullText)=>noFullText | (noQueryOrder)=>noQueryOrder)+ ;
+//propertyAttributes : ( (onParentVersioning)=> onParentVersioning | ('a')=>autoCreated | ('m')=>mandatory |('p')=>isProtected | ('mul'|'*')=>multiple | ('q')=>queryOperators | ('nof')=>noFullText | ('nqord'|'noq')=>noQueryOrder)+ ;
+valueConstraints : '<' stringList -> ^(VALUE_CONSTRAINTS stringList);
+isPrimary : ('!'|'pri'|'primary') -> ^(IS_PRIMARY_PROPERTY STRING["true"]);
+autoCreated :   ('a'|'aut'|'autocreated') -> ^(AUTO_CREATED STRING["true"]);
+mandatory : ('m'|'man'|'mandatory') -> ^(MANDATORY STRING["true"]);
+isProtected :   ('p'|'pro'|'protected') -> ^(PROTECTED STRING["true"]);
+onParentVersioning :    onParentVersioningLiteral -> ^(ON_PARENT_VERSION onParentVersioningLiteral);
+onParentVersioningLiteral : ('copy'|'version'|'initialize'|'compute'|'ignore'|'abort');
+multiple :  ('*'|'mul'|'multiple') -> ^(MULTIPLE STRING["true"]);
+noFullText :    ('nof'|'nofulltext') -> ^(IS_FULL_TEXT_SEARCHABLE STRING["false"]);
+noQueryOrder :  ('nqord'|'noqueryorder') -> ^(IS_QUERY_ORDERERABLE STRING["false"]);
+queryOperators :    ('qop'|'queryops') STRING -> ^(QUERY_OPERATORS STRING);
+// The grammar defines the query operators to be wrapped by single quotes, and therefore the lexer produces a single STRING token.
+// Since we cannot break this token up, we simply store the operators as a STRING literal, and will have to process
+// at a later step.
+
+// Child node definitions ...
+childNodeDefinition : nodeName requiredTypes? defaultType? nodeAttributes?
+  -> ^(NODE nodeName ^(PRIMARY_TYPE STRING["nt:childNodeDefinition"]) requiredTypes? defaultType? nodeAttributes?);
+nodeName : '+' ('*' -> ^(NAME '*') | STRING -> ^(NAME STRING));
+requiredTypes :'(' stringList ')' -> ^(REQUIRED_PRIMARY_TYPES stringList); 
+defaultType :   '=' STRING -> ^(DEFAULT_PRIMARY_TYPE STRING);
+nodeAttributes : nodeAttribute+;
+nodeAttribute : autoCreated | mandatory | (isPrimary)=>isPrimary| (isProtected)=>isProtected | onParentVersioning | sns ;
+sns : ('sns'|'*'|'multiple') -> ^(SAME_NAME_SIBLINGS STRING["true"]) ;
+
+// General rules
+stringList : STRING (',' STRING )* -> STRING*;
+
+// ------------
+// Lexer rules
+// ------------
+
+// Comments are currently sent to a separate channel
+MULTI_LINE_COMMENT : ('/*' (options {greedy=false;} : . )* '*/') { $channel=HIDDEN;};
+SINGLE_LINE_COMMENT : '//' ~('\n' | '\r')*  { $channel=HIDDEN;};
+
+// Quoted strings allow for strings containing characters that otherwise would be CND delimiters.
+// Note that the single- and double-quote characters must be escaped within the string.
+// Also note that escape sequences are allowed as well.
+STRING : QUOTED_STRING | UNQUOTED_STRING;
+
+// Quoted strings may contain escaped characters.
+fragment QUOTED_STRING
+    : '"' ( EscapeSequence | ~('\\'|'"'))* '"'
+    | '\'' ( EscapeSequence | ~('\\'|'\''))* '\'' 
+    ;
+
+fragment EscapeSequence 
+  : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\'
+         |('0'..'3') (('0'..'7') ('0'..'7')?)?
+         |'u'(('0'..'9')|('a'..'f')) (('0'..'9')|('a'..'f')) (('0'..'9')|('a'..'f'))
+         )
+;
+
+// An unquoted string is a word delimited by whitespace and CND tokens.
+fragment UNQUOTED_STRING
+: (~(' '|'\r'|'\t'|'\u000C'|'\n'    // whitespace
+    |'='|'<'|'>'|'['|']'|','|'-'|'('|')'    // tokens
+  ))+;
+
+WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ {$channel=HIDDEN;} ;
\ No newline at end of file

Added: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndI18n.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndI18n.java	                        (rev 0)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndI18n.java	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,51 @@
+/*
+ * 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.cnd;
+
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author John Verhaeg
+ * @author Randall Hauch
+ */
+public final class CndI18n {
+
+    public static I18n passthrough;
+    public static I18n errorImportingCndContent;
+    public static I18n expectedBooleanLiteral;
+    public static I18n expectedStringLiteral;
+    public static I18n expectedValidNameLiteral;
+    public static I18n expectedValidQueryOperator;
+    public static I18n expectedValidPropertyTypeName;
+    public static I18n expectedValidOnParentVersion;
+    public static I18n locationFromLineNumberAndCharacter;
+
+    static {
+        try {
+            I18n.initialize(CndI18n.class);
+        } catch (final Exception err) {
+            System.err.println(err);
+        }
+    }
+}

Added: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java	                        (rev 0)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,743 @@
+/*
+ * 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.
+ *
+ * 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.cnd;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.NotThreadSafe;
+import org.antlr.runtime.ANTLRFileStream;
+import org.antlr.runtime.ANTLRInputStream;
+import org.antlr.runtime.CharStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.TokenStream;
+import org.antlr.runtime.tree.CommonTree;
+import org.antlr.runtime.tree.RewriteCardinalityException;
+import org.jboss.dna.common.collection.Problems;
+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.JcrLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.io.Destination;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+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.PropertyFactory;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
+
+/**
+ * A class that imports the node types contained in a JCR Compact Node Definition (CND) file into graph content. The content is
+ * written using the graph structured defined by JCR and the "{@code nt:nodeType}", "{@code nt:propertyDefinition}", and "{@code
+ * nt:childNodeDefinition}" node types.
+ * <p>
+ * Although instances of this class never change their behavior and all processing is done in local contexts, {@link Destination}
+ * is not thread-safe and therefore this component may not be considered thread-safe.
+ * </p>
+ */
+ at NotThreadSafe
+public class CndImporter {
+
+    private static final Set<String> VALID_PROPERTY_TYPES = Collections.unmodifiableSet(new HashSet<String>(
+                                                                                                            Arrays.asList(new String[] {
+                                                                                                                "STRING",
+                                                                                                                "BINARY", "LONG",
+                                                                                                                "DOUBLE",
+                                                                                                                "BOOLEAN",
+                                                                                                                "DECIMAL",
+                                                                                                                "DATE", "NAME",
+                                                                                                                "PATH",
+                                                                                                                "REFERENCE",
+                                                                                                                "WEAKREFERENCE",
+                                                                                                                "URI",
+                                                                                                                "UNDEFINED"})));
+    private static final Set<String> VALID_ON_PARENT_VERSION = Collections.unmodifiableSet(new HashSet<String>(
+                                                                                                               Arrays.asList(new String[] {
+                                                                                                                   "COPY",
+                                                                                                                   "VERSION",
+                                                                                                                   "INITIALIZE",
+                                                                                                                   "COMPUTE",
+                                                                                                                   "IGNORE",
+                                                                                                                   "ABORT"})));
+    protected final Destination destination;
+    protected final Path parentPath;
+    private boolean debug = false;
+
+    /**
+     * Create a new importer that will place the content in the supplied destination under the supplied path.
+     * 
+     * @param destination the destination where content is to be written
+     * @param parentPath the path in the destination below which the generated content is to appear
+     * @throws IllegalArgumentException if either parameter is null
+     */
+    public CndImporter( Destination destination,
+                        Path parentPath ) {
+        CheckArg.isNotNull(destination, "destination");
+        CheckArg.isNotNull(parentPath, "parentPath");
+        this.destination = destination;
+        this.parentPath = parentPath;
+    }
+
+    void setDebug( boolean value ) {
+        this.debug = value;
+    }
+
+    protected ExecutionContext context() {
+        return this.destination.getExecutionContext();
+    }
+
+    protected NamespaceRegistry namespaces() {
+        return context().getNamespaceRegistry();
+    }
+
+    protected NameFactory nameFactory() {
+        return context().getValueFactories().getNameFactory();
+    }
+
+    protected ValueFactory<String> stringFactory() {
+        return context().getValueFactories().getStringFactory();
+    }
+
+    protected ValueFactory<Boolean> booleanFactory() {
+        return context().getValueFactories().getBooleanFactory();
+    }
+
+    /**
+     * Import the CND content from the supplied stream, placing the content into the importer's destination.
+     * 
+     * @param stream the stream containing the CND content
+     * @param problems where any problems encountered during import should be reported
+     * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
+     *        useful name
+     * @throws IOException if there is a problem reading from the supplied stream
+     */
+    public void importFrom( InputStream stream,
+                            Problems problems,
+                            String resourceName ) throws IOException {
+        CndLexer lex = new CndLexer(new CaseInsensitiveInputStream(stream));
+        importFrom(lex, resourceName, problems);
+    }
+
+    /**
+     * Import the CND content from the supplied stream, placing the content into the importer's destination.
+     * 
+     * @param content the string containing the CND content
+     * @param problems where any problems encountered during import should be reported
+     * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
+     *        useful name
+     * @throws IOException if there is a problem reading from the supplied stream
+     */
+    public void importFrom( String content,
+                            Problems problems,
+                            String resourceName ) throws IOException {
+        ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes());
+        importFrom(stream, problems, resourceName);
+    }
+
+    /**
+     * Import the CND content from the supplied stream, placing the content into the importer's destination.
+     * 
+     * @param file the file containing the CND content
+     * @param problems where any problems encountered during import should be reported
+     * @throws IOException if there is a problem reading from the supplied stream
+     */
+    public void importFrom( File file,
+                            Problems problems ) throws IOException {
+        CndLexer lex = new CndLexer(new CaseInsensitiveFileStream(file.getAbsolutePath()));
+        importFrom(lex, file.getCanonicalPath(), problems);
+    }
+
+    protected void importFrom( CndLexer lexer,
+                               String resourceName,
+                               Problems problems ) {
+        CommonTokenStream tokens = new CommonTokenStream(lexer);
+        CndParser parser = new Parser(tokens, problems, resourceName);
+
+        // Create a new context with our own namespace registry ...
+        ImportContext context = new ImportContext(context(), problems, resourceName);
+        CommonTree ast = null;
+        try {
+            ast = (CommonTree)parser.cnd().getTree();
+        } catch (RecognitionException e) {
+            // already handled by Parser, so we should not handle twice
+        } catch (RewriteCardinalityException e) {
+            // already handled by Parser, so we should not handle twice
+        } catch (RuntimeException e) {
+            problems.addError(e, CndI18n.errorImportingCndContent, (Object)resourceName, e.getMessage());
+        }
+
+        if (ast != null && problems.isEmpty()) {
+
+            // --------------
+            // Namespaces ...
+            // --------------
+
+            /* 
+              NAMESPACES
+               +- NODE (multiple)
+                   +- PREFIX
+                       +- string value
+                   +- URI
+                       +- string value
+             */
+
+            // Get the namespaces before we do anything else ...
+            CommonTree namespaces = (CommonTree)ast.getFirstChildWithType(CndLexer.NAMESPACES);
+            if (namespaces != null) {
+                for (int i = 0; i != namespaces.getChildCount(); ++i) {
+                    CommonTree namespace = (CommonTree)namespaces.getChild(i);
+                    String prefix = namespace.getFirstChildWithType(CndLexer.PREFIX).getChild(0).getText();
+                    String uri = namespace.getFirstChildWithType(CndLexer.URI).getChild(0).getText();
+                    // Register the namespace ...
+                    context.namespaces().register(removeQuotes(prefix), removeQuotes(uri));
+                }
+            }
+
+            // --------------
+            // Node Types ...
+            // --------------
+
+            /*
+            NODE_TYPES
+             +- NODE (multiple)
+                 +- NAME                                 [nt:nodeType/@jcr:name]
+                     +- string value
+                 +- PRIMARY_TYPE                         [nt:base/@jcr:primaryType]
+                     +- string with value 'nt:nodeType'
+                 +- SUPERTYPES                           [nt:nodeType/@jcr:supertypes]
+                     +- string value(s)
+                 +- IS_ABSTRACT                          [nt:nodeType/@jcr:isAbstract]
+                     +- string containing boolean value (or false if not present)
+                 +- HAS_ORDERABLE_CHILD_NODES            [nt:nodeType/@jcr:hasOrderableChildNodes]
+                     +- string containing boolean value (or false if not present)
+                 +- IS_MIXIN                             [nt:nodeType/@jcr:isMixin]
+                     +- string containing boolean value (or false if not present)
+                 +- IS_QUERYABLE                         [nt:nodeType/@jcr:isQueryable]
+                     +- string containing boolean value (or true if not present)
+                 +- PRIMARY_ITEM_NAME                    [nt:nodeType/@jcr:primaryItemName]
+                     +- string containing string value
+                 +- PROPERTY_DEFINITION                  [nt:nodeType/@jcr:propertyDefinition]
+                     +- NODE (multiple)
+                         +- NAME                         [nt:propertyDefinition/@jcr:name]
+                             +- string value
+                         +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+                             +- string with value 'nt:propertyDefinition'
+                         +- REQUIRED_TYPE                [nt:propertyDefinition/@jcr:propertyType]
+                             +- string value (limited to one of the predefined types)
+                         +- DEFAULT_VALUES               [nt:propertyDefinition/@jcr:defaultValues]
+                             +- string value(s)
+                         +- MULTIPLE                     [nt:propertyDefinition/@jcr:multiple]
+                             +- string containing boolean value (or false if not present)
+                         +- MANDATORY                    [nt:propertyDefinition/@jcr:mandatory]
+                             +- string containing boolean value (or false if not present)
+                         +- AUTO_CREATED                 [nt:propertyDefinition/@jcr:autoCreated]
+                             +- string containing boolean value (or false if not present)
+                         +- PROTECTED                    [nt:propertyDefinition/@jcr:protected]
+                             +- string containing boolean value (or false if not present)
+                         +- ON_PARENT_VERSION            [nt:propertyDefinition/@jcr:onParentVersion]
+                             +- string value (limited to one of the predefined literal values)
+                         +- QUERY_OPERATORS              
+                             +- string value (containing a comma-separated list of operator literals)
+                         +- IS_FULL_TEXT_SEARCHABLE      [nt:propertyDefinition/@jcr:isFullTextSearchable]
+                             +- string containing boolean value (or true if not present)
+                         +- IS_QUERY_ORDERABLE           [nt:propertyDefinition/@jcr:isQueryOrderable]
+                             +- string containing boolean value (or true if not present)
+                         +- VALUE_CONSTRAINTS            [nt:propertyDefinition/@jcr:valueConstraints]
+                             +- string value(s)
+                 +- CHILD_NODE_DEFINITION                [nt:nodeType/@jcr:childNodeDefinition]
+                     +- NODE (multiple)
+                         +- NAME                         [nt:childNodeDefinition/@jcr:name]
+                             +- string value
+                         +- PRIMARY_TYPE                 [nt:base/@jcr:primaryType]
+                             +- string with value 'nt:childNodeDefinition'
+                         +- REQUIRED_PRIMARY_TYPES       [nt:childNodeDefinition/@jcr:requiredPrimaryTypes]
+                             +- string values (limited to names)
+                         +- DEFAULT_PRIMARY_TYPE         [nt:childNodeDefinition/@jcr:defaultPrimaryType]
+                             +- string value (limited to a name)
+                         +- MANDATORY                    [nt:childNodeDefinition/@jcr:mandatory]
+                             +- string containing boolean value (or false if not present)
+                         +- AUTO_CREATED                 [nt:childNodeDefinition/@jcr:autoCreated]
+                             +- string containing boolean value (or false if not present)
+                         +- PROTECTED                    [nt:childNodeDefinition/@jcr:protected]
+                             +- string containing boolean value (or false if not present)
+                         +- SAME_NAME_SIBLINGS           [nt:childNodeDefinition/@jcr:sameNameSiblings]
+                             +- string containing boolean value (or false if not present)
+                         +- ON_PARENT_VERSION            [nt:childNodeDefinition/@jcr:onParentVersion]
+                             +- string value (limited to one of the predefined literal values)
+            */
+
+            // Get the node types ...
+            CommonTree nodeTypes = (CommonTree)ast.getFirstChildWithType(CndLexer.NODE_TYPES);
+            if (nodeTypes != null) {
+                int numNodeTypes = 0;
+                // Walk each of the nodes underneath the NODE_TYPES parent node ...
+                for (int i = 0; i != nodeTypes.getChildCount(); ++i) {
+                    CommonTree nodeType = (CommonTree)nodeTypes.getChild(i);
+                    if (this.debug) System.out.println(nodeType.toStringTree());
+                    Path nodeTypePath = context.createNodeType(nodeType, parentPath);
+                    if (nodeTypePath == null) continue;
+                    ++numNodeTypes;
+
+                    CommonTree propertyDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.PROPERTY_DEFINITION);
+                    if (propertyDefinitions != null) {
+                        // Walk each of the nodes under PROPERTY_DEFINITION ...
+                        for (int j = 0; j != propertyDefinitions.getChildCount(); ++j) {
+                            CommonTree propDefn = (CommonTree)propertyDefinitions.getChild(j);
+                            context.createPropertyDefinition(propDefn, nodeTypePath);
+                        }
+                    }
+
+                    CommonTree childNodeDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.CHILD_NODE_DEFINITION);
+                    if (childNodeDefinitions != null) {
+                        // Walk each of the nodes under CHILD_NODE_DEFINITION ...
+                        for (int j = 0; j != childNodeDefinitions.getChildCount(); ++j) {
+                            CommonTree childDefn = (CommonTree)childNodeDefinitions.getChild(j);
+                            context.createChildDefinition(childDefn, nodeTypePath);
+                        }
+                    }
+                }
+
+                // Submit the destination
+                destination.submit();
+            }
+        }
+    }
+
+    protected final String removeQuotes( String text ) {
+        // Remove leading and trailing quotes, if there are any ...
+        return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
+    }
+
+    /**
+     * Utility class that uses a context with a local namespace registry, along with the problems and resource name.
+     */
+    protected final class ImportContext {
+        private final ExecutionContext context;
+        private final Problems problems;
+        private final String resourceName;
+
+        protected ImportContext( ExecutionContext context,
+                                 Problems problems,
+                                 String resourceName ) {
+            // Create a context that has a local namespace registry
+            NamespaceRegistry localNamespaces = new LocalNamespaceRegistry(context.getNamespaceRegistry());
+            this.context = context.with(localNamespaces);
+            this.problems = problems;
+            this.resourceName = resourceName;
+        }
+
+        protected ExecutionContext context() {
+            return this.context;
+        }
+
+        protected NamespaceRegistry namespaces() {
+            return this.context.getNamespaceRegistry();
+        }
+
+        protected NameFactory nameFactory() {
+            return this.context.getValueFactories().getNameFactory();
+        }
+
+        protected PathFactory pathFactory() {
+            return this.context.getValueFactories().getPathFactory();
+        }
+
+        protected ValueFactory<String> stringFactory() {
+            return this.context.getValueFactories().getStringFactory();
+        }
+
+        protected ValueFactory<Boolean> booleanFactory() {
+            return this.context.getValueFactories().getBooleanFactory();
+        }
+
+        protected void recordError( CommonTree node,
+                                    I18n msg,
+                                    Object... params ) {
+            String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
+            problems.addError(msg, resourceName, location, params);
+        }
+
+        protected void recordError( Throwable throwable,
+                                    CommonTree node,
+                                    I18n msg,
+                                    Object... params ) {
+            String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
+            problems.addError(throwable, msg, resourceName, location, params);
+        }
+
+        protected Name nameFrom( CommonTree node,
+                                 int childType ) {
+            CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
+            if (childNode != null && childNode.getChildCount() > 0) {
+                CommonTree textNode = (CommonTree)childNode.getChild(0);
+                if (textNode.getToken().getTokenIndex() < 0) return null;
+                String text = removeQuotes(childNode.getChild(0).getText());
+                try {
+                    return nameFactory().create(text);
+                } catch (ValueFormatException e) {
+                    recordError(e, node, CndI18n.expectedValidNameLiteral, text);
+                }
+            }
+            return null;
+        }
+
+        protected Name[] namesFrom( CommonTree node,
+                                    int childType ) {
+            CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
+            if (childNode != null && childNode.getChildCount() > 0) {
+                List<Name> names = new ArrayList<Name>();
+                for (int i = 0; i != childNode.getChildCount(); ++i) {
+                    String text = removeQuotes(childNode.getChild(i).getText());
+                    try {
+                        names.add(nameFactory().create(text));
+                    } catch (ValueFormatException e) {
+                        recordError(e, node, CndI18n.expectedValidNameLiteral, text);
+                    }
+                }
+                return names.toArray(new Name[names.size()]);
+            }
+            return new Name[] {};
+        }
+
+        protected String stringFrom( CommonTree node,
+                                     int childType ) {
+            CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
+            if (childNode != null && childNode.getChildCount() > 0) {
+                String text = removeQuotes(childNode.getChild(0).getText().trim());
+                try {
+                    return stringFactory().create(text);
+                } catch (ValueFormatException e) {
+                    recordError(e, node, CndI18n.expectedStringLiteral, text);
+                }
+            }
+            return null;
+        }
+
+        protected String[] stringsFrom( CommonTree node,
+                                        int childType ) {
+            CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
+            if (childNode != null && childNode.getChildCount() > 0) {
+                List<String> names = new ArrayList<String>();
+                for (int i = 0; i != childNode.getChildCount(); ++i) {
+                    String text = removeQuotes(childNode.getChild(i).getText().trim());
+                    try {
+                        names.add(stringFactory().create(text));
+                    } catch (ValueFormatException e) {
+                        recordError(e, node, CndI18n.expectedStringLiteral, text);
+                    }
+                }
+                return names.toArray(new String[names.size()]);
+            }
+            return new String[] {};
+        }
+
+        protected boolean booleanFrom( CommonTree node,
+                                       int childType,
+                                       boolean defaultValue ) {
+            CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
+            if (childNode != null && childNode.getChildCount() > 0) {
+                String text = removeQuotes(childNode.getChild(0).getText());
+                try {
+                    return booleanFactory().create(text);
+                } catch (ValueFormatException e) {
+                    recordError(e, node, CndI18n.expectedBooleanLiteral, text);
+                }
+            }
+            return defaultValue;
+        }
+
+        protected QueryOperator[] queryOperatorsFrom( CommonTree node,
+                                                      int childType ) {
+            String text = stringFrom(node, childType);
+            if (text != null) {
+                String[] literals = text.split(",");
+                if (literals.length != 0) {
+                    Set<QueryOperator> operators = new HashSet<QueryOperator>();
+                    for (String literal : literals) {
+                        literal = literal.trim();
+                        if (literal.length() == 0) continue;
+                        QueryOperator operator = QueryOperator.forText(literal);
+                        if (operator != null) {
+                            operators.add(operator);
+                        } else {
+                            recordError(node, CndI18n.expectedValidQueryOperator, literal);
+                        }
+                    }
+                    return operators.toArray(new QueryOperator[operators.size()]);
+                }
+            }
+            return new QueryOperator[] {};
+        }
+
+        protected String propertyTypeNameFrom( CommonTree node,
+                                               int childType ) {
+            String text = stringFrom(node, childType);
+            if (text.equals("*")) text = "undefined";
+            String upperText = text.toUpperCase();
+            if (!VALID_PROPERTY_TYPES.contains(upperText)) {
+                recordError(node, CndI18n.expectedValidPropertyTypeName, text, VALID_PROPERTY_TYPES);
+                return null;
+            }
+            return upperText;
+        }
+
+        protected String onParentVersionFrom( CommonTree node,
+                                              int childType ) {
+            String text = stringFrom(node, childType);
+            if (text == null) return "COPY";
+            String upperText = text.toUpperCase();
+            if (!VALID_ON_PARENT_VERSION.contains(upperText)) {
+                recordError(node, CndI18n.expectedValidOnParentVersion, text, VALID_ON_PARENT_VERSION);
+                return null;
+            }
+            return upperText;
+        }
+
+        protected Path createNodeType( CommonTree nodeType,
+                                       Path parentPath ) {
+            Name name = nameFrom(nodeType, CndLexer.NAME);
+            Name[] supertypes = namesFrom(nodeType, CndLexer.SUPERTYPES);
+            boolean isAbstract = booleanFrom(nodeType, CndLexer.IS_ABSTRACT, false);
+            boolean hasOrderableChildNodes = booleanFrom(nodeType, CndLexer.HAS_ORDERABLE_CHILD_NODES, false);
+            boolean isMixin = booleanFrom(nodeType, CndLexer.IS_MIXIN, false);
+            boolean isQueryable = booleanFrom(nodeType, CndLexer.IS_QUERYABLE, true);
+            Name primaryItemName = nameFrom(nodeType, CndLexer.PRIMARY_ITEM_NAME);
+
+            if (primaryItemName == null) {
+                // See if one of the property definitions is marked as the primary ...
+                CommonTree propertyDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.PROPERTY_DEFINITION);
+                if (propertyDefinitions != null) {
+                    // Walk each of the nodes under PROPERTY_DEFINITION ...
+                    for (int j = 0; j != propertyDefinitions.getChildCount(); ++j) {
+                        CommonTree propDefn = (CommonTree)propertyDefinitions.getChild(j);
+                        if (booleanFrom(propDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
+                            primaryItemName = nameFrom(propDefn, CndLexer.NAME);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (primaryItemName == null) {
+                // See if one of the child definitions is marked as the primary ...
+                CommonTree childNodeDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.CHILD_NODE_DEFINITION);
+                if (childNodeDefinitions != null) {
+                    // Walk each of the nodes under CHILD_NODE_DEFINITION ...
+                    for (int j = 0; j != childNodeDefinitions.getChildCount(); ++j) {
+                        CommonTree childDefn = (CommonTree)childNodeDefinitions.getChild(j);
+                        if (booleanFrom(childDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
+                            primaryItemName = nameFrom(childDefn, CndLexer.NAME);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // Create the node for the node type ...
+            if (name == null) return null;
+            Path path = pathFactory().create(parentPath, name);
+
+            PropertyFactory factory = context.getPropertyFactory();
+            destination.create(path,
+                               factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.NODE_TYPE),
+                               factory.create(JcrLexicon.SUPERTYPES, (Object[])supertypes),
+                               factory.create(JcrLexicon.IS_ABSTRACT, isAbstract),
+                               factory.create(JcrLexicon.HAS_ORDERABLE_CHILD_NODES, hasOrderableChildNodes),
+                               factory.create(JcrLexicon.IS_MIXIN, isMixin),
+                               factory.create(JcrLexicon.IS_QUERYABLE, isQueryable),
+                               factory.create(JcrLexicon.PRIMARY_ITEM_NAME, primaryItemName));
+
+            return path;
+        }
+
+        protected Path createPropertyDefinition( CommonTree propDefn,
+                                                 Path parentPath ) {
+            Name name = nameFrom(propDefn, CndLexer.NAME);
+            String requiredType = propertyTypeNameFrom(propDefn, CndLexer.REQUIRED_TYPE);
+            String[] defaultValues = stringsFrom(propDefn, CndLexer.DEFAULT_VALUES);
+            boolean multiple = booleanFrom(propDefn, CndLexer.MULTIPLE, false);
+            boolean mandatory = booleanFrom(propDefn, CndLexer.MANDATORY, false);
+            boolean autoCreated = booleanFrom(propDefn, CndLexer.AUTO_CREATED, false);
+            boolean isProtected = booleanFrom(propDefn, CndLexer.PROTECTED, false);
+            String onParentVersion = onParentVersionFrom(propDefn, CndLexer.ON_PARENT_VERSION);
+            /*QueryOperator[] queryOperators =*/queryOperatorsFrom(propDefn, CndLexer.QUERY_OPERATORS);
+            boolean isFullTextSearchable = booleanFrom(propDefn, CndLexer.IS_FULL_TEXT_SEARCHABLE, true);
+            boolean isQueryOrderable = booleanFrom(propDefn, CndLexer.IS_QUERY_ORDERERABLE, true);
+            String[] valueConstraints = stringsFrom(propDefn, CndLexer.VALUE_CONSTRAINTS);
+
+            // Create the node for the node type ...
+            if (name == null) return null;
+            Path path = pathFactory().create(parentPath, name);
+
+            PropertyFactory factory = context.getPropertyFactory();
+            destination.create(path,
+                               factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.PROPERTY_DEFINITION),
+                               factory.create(JcrLexicon.REQUIRED_TYPE, requiredType),
+                               factory.create(JcrLexicon.DEFAULT_VALUES, (Object[])defaultValues),
+                               factory.create(JcrLexicon.MULTIPLE, multiple),
+                               factory.create(JcrLexicon.MANDATORY, mandatory),
+                               factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
+                               factory.create(JcrLexicon.PROTECTED, isProtected),
+                               factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
+                               // factory.create(DnaLexicon.QUERY_OPERATORS, queryOperators),
+                               factory.create(JcrLexicon.IS_FULL_TEXT_SEARCHABLE, isFullTextSearchable),
+                               factory.create(JcrLexicon.IS_QUERY_ORDERABLE, isQueryOrderable),
+                               factory.create(JcrLexicon.VALUE_CONSTRAINTS, (Object[])valueConstraints));
+
+            return path;
+        }
+
+        protected Path createChildDefinition( CommonTree childDefn,
+                                              Path parentPath ) {
+            Name name = nameFrom(childDefn, CndLexer.NAME);
+            Name[] requiredPrimaryTypes = namesFrom(childDefn, CndLexer.REQUIRED_PRIMARY_TYPES);
+            Name defaultPrimaryType = nameFrom(childDefn, CndLexer.DEFAULT_PRIMARY_TYPE);
+            boolean mandatory = booleanFrom(childDefn, CndLexer.MANDATORY, false);
+            boolean autoCreated = booleanFrom(childDefn, CndLexer.AUTO_CREATED, false);
+            boolean isProtected = booleanFrom(childDefn, CndLexer.PROTECTED, false);
+            String onParentVersion = onParentVersionFrom(childDefn, CndLexer.ON_PARENT_VERSION);
+            boolean sameNameSiblings = booleanFrom(childDefn, CndLexer.SAME_NAME_SIBLINGS, false);
+
+            // Create the node for the node type ...
+            if (name == null) return null;
+            Path path = pathFactory().create(parentPath, name);
+
+            PropertyFactory factory = context.getPropertyFactory();
+            destination.create(path,
+                               factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.CHILD_NODE_DEFINITION),
+                               factory.create(JcrLexicon.REQUIRED_PRIMARY_TYPES, (Object[])requiredPrimaryTypes),
+                               factory.create(JcrLexicon.DEFAULT_PRIMARY_TYPE, defaultPrimaryType),
+                               factory.create(JcrLexicon.MANDATORY, mandatory),
+                               factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
+                               factory.create(JcrLexicon.PROTECTED, isProtected),
+                               factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
+                               factory.create(JcrLexicon.SAME_NAME_SIBLINGS, sameNameSiblings));
+
+            return path;
+        }
+    }
+
+    protected class Parser extends CndParser {
+        private final Problems problems;
+        private final String nameOfResource;
+
+        public Parser( TokenStream input,
+                       Problems problems,
+                       String nameOfResource ) {
+            super(input);
+            this.problems = problems;
+            this.nameOfResource = nameOfResource;
+        }
+
+        @Override
+        public void displayRecognitionError( String[] tokenNames,
+                                             RecognitionException e ) {
+            if (problems != null) {
+                String hdr = getErrorHeader(e);
+                String msg = getErrorMessage(e, tokenNames);
+                problems.addError(CndI18n.passthrough, nameOfResource, hdr, msg);
+            } else {
+                super.displayRecognitionError(tokenNames, e);
+            }
+        }
+    }
+
+    /**
+     * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
+     * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
+     */
+    protected class CaseInsensitiveInputStream extends ANTLRInputStream {
+        protected CaseInsensitiveInputStream( InputStream stream ) throws IOException {
+            super(stream);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.antlr.runtime.ANTLRStringStream#LA(int)
+         */
+        @Override
+        public int LA( int i ) {
+            if (i == 0) {
+                return 0; // undefined
+            }
+            if (i < 0) {
+                i++; // e.g., translate LA(-1) to use offset 0
+            }
+
+            if ((p + i - 1) >= n) {
+                return CharStream.EOF;
+            }
+            return Character.toLowerCase(data[p + i - 1]);
+        }
+    }
+
+    /**
+     * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
+     * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
+     */
+    protected class CaseInsensitiveFileStream extends ANTLRFileStream {
+        protected CaseInsensitiveFileStream( String fileName ) throws IOException {
+            super(fileName, null);
+        }
+
+        protected CaseInsensitiveFileStream( String fileName,
+                                             String encoding ) throws IOException {
+            super(fileName, encoding);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.antlr.runtime.ANTLRStringStream#LA(int)
+         */
+        @Override
+        public int LA( int i ) {
+            if (i == 0) {
+                return 0; // undefined
+            }
+            if (i < 0) {
+                i++; // e.g., translate LA(-1) to use offset 0
+            }
+
+            if ((p + i - 1) >= n) {
+
+                return CharStream.EOF;
+            }
+            return Character.toLowerCase(data[p + i - 1]);
+        }
+    }
+
+}

Added: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/QueryOperator.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/QueryOperator.java	                        (rev 0)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/QueryOperator.java	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ *
+ * 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.cnd;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * Enumeration of the query operators allowed in a CND file.
+ */
+public enum QueryOperator {
+
+    EQUAL("="),
+    NOT_EQUAL("<>"),
+    LESS_THAN("<"),
+    LESS_THAN_OR_EQUALS("<="),
+    GREATER_THAN("<"),
+    GREATER_THAN_OR_EQUALS("<="),
+    LIKE("LIKE");
+
+    private static final Map<String, QueryOperator> ALL_OPERATORS;
+    static {
+        Map<String, QueryOperator> operators = new HashMap<String, QueryOperator>();
+        for (QueryOperator operator : QueryOperator.values()) {
+            operators.put(operator.getText(), operator);
+        }
+        ALL_OPERATORS = Collections.unmodifiableMap(operators);
+    }
+
+    private final String text;
+
+    private QueryOperator( String text ) {
+        this.text = text;
+    }
+
+    /**
+     * @return text
+     */
+    public String getText() {
+        return text;
+    }
+
+    public static QueryOperator forText( String text ) {
+        CheckArg.isNotNull(text, "text");
+        return ALL_OPERATORS.get(text.trim().toUpperCase());
+    }
+
+    /**
+     * Return an iterator over all the operator enumeration literals.
+     * 
+     * @return an immutable iterator
+     */
+    public static Iterator<QueryOperator> iterator() {
+        return ALL_OPERATORS.values().iterator();
+    }
+}

Added: trunk/dna-cnd/src/main/resources/org/jboss/dna/cnd/CndI18n.properties
===================================================================
--- trunk/dna-cnd/src/main/resources/org/jboss/dna/cnd/CndI18n.properties	                        (rev 0)
+++ trunk/dna-cnd/src/main/resources/org/jboss/dna/cnd/CndI18n.properties	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+passthrough = {0}
+errorImportingCndContent = Error importing {0}: {1}
+expectedBooleanLiteral = Expected 'true' or 'false', but was "{0}"
+expectedStringLiteral = Expected a valid string, but was "{0}"
+expectedValidNameLiteral = Expected a valid name, but was "{0}"
+expectedValidPropertyTypeName = Property type name was "{0}" but should be one of "{1}"
+expectedValidOnParentVersion = On parent version value was "{0}" but should be one of "{1}"
+expectedValidQueryOperator = Expected a valid query operator ('=', '<>', '<', '<=', '>', '>=', or 'LIKE'), but was {0}
+locationFromLineNumberAndCharacter = Line {0} column {1}
\ No newline at end of file

Added: trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java
===================================================================
--- trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java	                        (rev 0)
+++ trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,611 @@
+/*
+ * 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.
+ *
+ * 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.cnd;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
+import static org.junit.Assert.assertThat;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.common.collection.Problem;
+import org.jboss.dna.common.collection.SimpleProblems;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.io.Destination;
+import org.jboss.dna.graph.io.GraphBatchDestination;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class CndImporterTest {
+
+    public static final String CND_FILE_PATH = "src/test/resources/cnd/";
+
+    private ExecutionContext context;
+    private InMemoryRepositorySource repository;
+    private Graph graph;
+    private Destination destination;
+    private Path rootPath;
+    private CndImporter importer;
+    private SimpleProblems problems;
+
+    @Before
+    public void beforeEach() {
+        problems = new SimpleProblems();
+        context = new ExecutionContext();
+        context.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
+        context.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
+        context.getNamespaceRegistry().register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
+
+        // Set up the repository and graph ...
+        repository = new InMemoryRepositorySource();
+        repository.setName("NodeTypes");
+        graph = Graph.create(repository, context);
+
+        // Set up the path where the content will go, and make sure that path exists in the repository ...
+        rootPath = context.getValueFactories().getPathFactory().create("/a");
+        graph.create(rootPath);
+
+        // Now set up the destination ...
+        destination = new GraphBatchDestination(graph.batch());
+
+        // Set up the importer ...
+        importer = new CndImporter(destination, rootPath);
+    }
+
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected void printProblems() {
+        for (Problem problem : problems) {
+            System.out.println(problem);
+        }
+    }
+
+    protected Node node( String pathToNode ) {
+        return graph.getNodeAt("/a/" + pathToNode);
+    }
+
+    protected InputStream openCndStream( String cndFileName ) throws IOException {
+        return this.getClass().getClassLoader().getResourceAsStream("cnd/" + cndFileName);
+    }
+
+    protected File openCndFile( String cndFileName ) throws IOException, URISyntaxException {
+        File result = new File(CND_FILE_PATH + cndFileName);
+        assertThat(result.exists(), is(true));
+        return result;
+    }
+
+    @Test
+    public void shouldReportErrorIfTheNodeTypeNameIsEmpty() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'> [] abstract";
+        importer.importFrom(cnd, problems, "string");
+        // printProblems();
+        assertThat(problems.size(), is(1));
+        assertThat(problems.iterator().next().getMessageString(), is("missing STRING at ']'"));
+    }
+
+    @Test
+    public void shouldReportErrorIfTheNodeTypeNameIsBlank() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'> [ ] abstract";
+        importer.importFrom(cnd, problems, "string");
+        // printProblems();
+        assertThat(problems.size(), is(1));
+        assertThat(problems.iterator().next().getMessageString(), is("missing STRING at ']'"));
+    }
+
+    @Test
+    public void shouldReportErrorIfTheNodeTypeNameIsNotFollowedByClosingBracket() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'> [  abstract";
+        importer.importFrom(cnd, problems, "string");
+        // printProblems();
+        assertThat(problems.size(), is(1));
+        assertThat(problems.iterator().next().getMessageString(), is("mismatched input 'abstract' expecting STRING"));
+    }
+
+    @Test
+    public void shouldReportErrorIfTheNodeTypeNameUsesInvalidNamespace() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'> [xyz:acme] abstract";
+        importer.importFrom(cnd, problems, "string");
+        // printProblems();
+        assertThat(problems.size(), is(1));
+        assertThat(problems.iterator().next().getMessage(), is(CndI18n.expectedValidNameLiteral));
+    }
+
+    @Test
+    public void shouldImportCndThatUsesAllFeatures() throws IOException {
+        // importer.setDebug(true);
+        String cnd = "<ex = 'http://namespace.com/ns'>\n"
+                     + "[ex:NodeType] > ex:ParentType1, ex:ParentType2 abstract orderable mixin noquery primaryitem ex:property\n"
+                     + "- ex:property (STRING) = 'default1', 'default2' mandatory autocreated protected multiple VERSION < 'constraint1', 'constraint2'\n"
+                     + " queryops '=, <>, <, <=, >, >=, LIKE' nofulltext noqueryorder\n"
+                     + "+ ex:node (ex:reqType1, ex:reqType2) = ex:defaultType mandatory autocreated protected sns version";
+        importer.importFrom(cnd, problems, "string");
+        // assertThat(problems.size(), is(1));
+        // printProblems();
+        context.getNamespaceRegistry().register("ex", "http://namespace.com/ns");
+        Node nodeType = node("ex:NodeType");
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_ABSTRACT, true));
+        Node prop = node("ex:NodeType/ex:property");
+        assertThat(prop, hasProperty(JcrLexicon.REQUIRED_TYPE, "STRING"));
+        assertThat(prop, hasProperty(JcrLexicon.DEFAULT_VALUES, new Object[] {"default1", "default2"}));
+        assertThat(prop, hasProperty(JcrLexicon.AUTO_CREATED, true));
+        assertThat(prop, hasProperty(JcrLexicon.MANDATORY, true));
+        assertThat(prop, hasProperty(JcrLexicon.PROTECTED, true));
+        assertThat(prop, hasProperty(JcrLexicon.MULTIPLE, true));
+        assertThat(prop, hasProperty(JcrLexicon.ON_PARENT_VERSION, "VERSION"));
+        assertThat(prop, hasProperty(JcrLexicon.VALUE_CONSTRAINTS, new Object[] {"constraint1", "constraint2"}));
+        assertThat(prop, hasProperty(JcrLexicon.IS_FULL_TEXT_SEARCHABLE, false));
+        assertThat(prop, hasProperty(JcrLexicon.IS_QUERY_ORDERABLE, false));
+        Node node = node("ex:NodeType/ex:node");
+        assertThat(node, hasProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES, new Object[] {name("ex:reqType1"), name("ex:reqType2")}));
+        assertThat(node, hasProperty(JcrLexicon.DEFAULT_PRIMARY_TYPE, name("ex:defaultType")));
+        assertThat(node, hasProperty(JcrLexicon.AUTO_CREATED, true));
+        assertThat(node, hasProperty(JcrLexicon.MANDATORY, true));
+        assertThat(node, hasProperty(JcrLexicon.PROTECTED, true));
+        assertThat(node, hasProperty(JcrLexicon.SAME_NAME_SIBLINGS, true));
+        assertThat(node, hasProperty(JcrLexicon.ON_PARENT_VERSION, "VERSION"));
+    }
+
+    @Test
+    public void shouldImportCndThatIsOnOneLine() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'> "
+                     + "[ns:NodeType] > ns:ParentType1, ns:ParentType2 abstract orderable mixin noquery primaryitem ex:property "
+                     + "- ex:property (STRING) = 'default1', 'default2' mandatory autocreated protected multiple VERSION < 'constraint1', 'constraint2' "
+                     + " queryops '=, <>, <, <=, >, >=, LIKE' nofulltext noqueryorder "
+                     + "+ ns:node (ns:reqType1, ns:reqType2) = ns:defaultType mandatory autocreated protected sns version";
+        // importer.importFrom(cnd, problems, "string");
+    }
+
+    @Test
+    public void shouldImportCndThatHasNoChildren() throws IOException {
+        String cnd = "<ns = 'http://namespace.com/ns'>\n"
+                     + "[ns:NodeType] > ns:ParentType1, ns:ParentType2 abstract orderable mixin noquery primaryitem ex:property\n"
+                     + "- ex:property (STRING) = 'default1', 'default2' mandatory autocreated protected multiple VERSION < 'constraint1', 'constraint2'\n"
+                     + " queryops '=, <>, <, <=, >, >=, LIKE' nofulltext noqueryorder";
+        // importer.importFrom(cnd, problems, "string");
+    }
+
+    @Test
+    public void shouldImportJcrBuiltinNodeTypesForJSR170() throws Exception {
+        importer.importFrom(openCndFile("jcr-builtins-170.cnd"), problems);
+        if (problems.size() != 0) printProblems();
+        assertThat(problems.size(), is(0));
+
+        // [nt:base]
+        // - jcr:primaryType (name) mandatory autocreated protected compute
+        // - jcr:mixinTypes (name) protected multiple compute
+        assertNodeType("nt:base", NO_SUPERTYPES, NO_PRIMARY_NAME);
+        assertProperty("nt:base", "jcr:primaryType", "Name", NO_DEFAULTS, new PropertyOptions[] {PropertyOptions.Mandatory,
+            PropertyOptions.Autocreated, PropertyOptions.Protected}, OnParentVersion.Compute);
+        assertProperty("nt:base", "jcr:mixinTypes", "Name", NO_DEFAULTS, new PropertyOptions[] {PropertyOptions.Multiple,
+            PropertyOptions.Protected}, OnParentVersion.Compute);
+
+        // [nt:unstructured]
+        // orderable
+        // - * (undefined) multiple
+        // - * (undefined)
+        // + * (nt:base) = nt:unstructured multiple version
+        assertNodeType("nt:unstructured", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Ordered);
+        assertProperty("nt:unstructured", "*[1]", "Undefined", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:unstructured", "*[2]", "Undefined", NO_DEFAULTS);
+        assertChild("nt:unstructured", "*[3]", "nt:base", "nt:unstructured", OnParentVersion.Version, ChildOptions.Multiple);
+
+        // [mix:referenceable]
+        // mixin
+        // - jcr:uuid (string) mandatory autocreated protected initialize
+        assertNodeType("mix:referenceable", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Mixin);
+        assertProperty("mix:referenceable",
+                       "jcr:uuid",
+                       "String",
+                       NO_DEFAULTS,
+                       OnParentVersion.Initialize,
+                       PropertyOptions.Mandatory,
+                       PropertyOptions.Autocreated,
+                       PropertyOptions.Protected);
+
+        // [mix:lockable]
+        // mixin
+        // - jcr:lockOwner (string) protected ignore
+        // - jcr:lockIsDeep (boolean) protected ignore
+        assertNodeType("mix:lockable", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Mixin);
+        assertProperty("mix:lockable", "jcr:lockOwner", "String", NO_DEFAULTS, OnParentVersion.Ignore, PropertyOptions.Protected);
+        assertProperty("mix:lockable",
+                       "jcr:lockIsDeep",
+                       "Boolean",
+                       NO_DEFAULTS,
+                       OnParentVersion.Ignore,
+                       PropertyOptions.Protected);
+
+        // [nt:propertyDefinition]
+        // - jcr:name (name)
+        // - jcr:autoCreated (boolean) mandatory
+        // - jcr:mandatory (boolean) mandatory
+        // - jcr:onParentVersion (string) mandatory
+        // < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+        // - jcr:protected (boolean) mandatory
+        // - jcr:requiredType (string) mandatory
+        // < 'STRING', 'BINARY', 'LONG', 'DOUBLE', 'BOOLEAN', 'DATE', 'NAME', 'PATH', 'REFERENCE', 'UNDEFINED'
+        // - jcr:valueConstraints (string) multiple
+        // - jcr:defaultValues (undefined) multiple
+        // - jcr:multiple (boolean) mandatory
+        assertNodeType("nt:propertyDefinition", NO_SUPERTYPES, NO_PRIMARY_NAME);
+        assertProperty("nt:propertyDefinition", "jcr:name", "Name", NO_DEFAULTS);
+        assertProperty("nt:propertyDefinition", "jcr:autoCreated", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition", "jcr:mandatory", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition",
+                       "jcr:onParentVersion",
+                       "String",
+                       NO_DEFAULTS,
+                       new PropertyOptions[] {PropertyOptions.Mandatory},
+                       null,
+                       new String[] {"COPY", "VERSION", "INITIALIZE", "COMPUTE", "IGNORE", "ABORT"});
+        assertProperty("nt:propertyDefinition", "jcr:protected", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition",
+                       "jcr:requiredType",
+                       "String",
+                       NO_DEFAULTS,
+                       new PropertyOptions[] {PropertyOptions.Mandatory},
+                       null,
+                       new String[] {"STRING", "BINARY", "LONG", "DOUBLE", "BOOLEAN", "DATE", "NAME", "PATH", "REFERENCE",
+                           "UNDEFINED"});
+        assertProperty("nt:propertyDefinition", "jcr:valueConstraints", "String", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:propertyDefinition", "jcr:defaultValues", "Undefined", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:propertyDefinition", "jcr:multiple", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+    }
+
+    @Test
+    public void shouldImportJcrBuiltinNodeTypesForJSR283() throws Exception {
+        importer.importFrom(openCndFile("jcr-builtins-283-early-draft.cnd"), problems);
+        if (problems.size() != 0) printProblems();
+        assertThat(problems.size(), is(0));
+
+        // [nt:base]
+        // - jcr:primaryType (name) mandatory autocreated protected compute
+        // - jcr:mixinTypes (name) protected multiple compute
+        assertNodeType("nt:base", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Abstract);
+        assertProperty("nt:base", "jcr:primaryType", "Name", NO_DEFAULTS, new PropertyOptions[] {PropertyOptions.Mandatory,
+            PropertyOptions.Autocreated, PropertyOptions.Protected}, OnParentVersion.Compute);
+        assertProperty("nt:base", "jcr:mixinTypes", "Name", NO_DEFAULTS, new PropertyOptions[] {PropertyOptions.Multiple,
+            PropertyOptions.Protected}, OnParentVersion.Compute);
+
+        // [nt:unstructured]
+        // orderable
+        // - * (undefined) multiple
+        // - * (undefined)
+        // + * (nt:base) = nt:unstructured multiple version
+        assertNodeType("nt:unstructured", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Ordered);
+        assertProperty("nt:unstructured", "*[1]", "Undefined", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:unstructured", "*[2]", "Undefined", NO_DEFAULTS);
+        assertChild("nt:unstructured", "*[3]", "nt:base", "nt:unstructured", OnParentVersion.Version, ChildOptions.Multiple);
+
+        // [mix:referenceable]
+        // mixin
+        // - jcr:uuid (string) mandatory autocreated protected initialize
+        assertNodeType("mix:referenceable", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Mixin);
+        assertProperty("mix:referenceable",
+                       "jcr:uuid",
+                       "String",
+                       NO_DEFAULTS,
+                       OnParentVersion.Initialize,
+                       PropertyOptions.Mandatory,
+                       PropertyOptions.Autocreated,
+                       PropertyOptions.Protected);
+
+        // [mix:lockable]
+        // mixin
+        // - jcr:lockOwner (string) protected ignore
+        // - jcr:lockIsDeep (boolean) protected ignore
+        assertNodeType("mix:lockable", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Mixin);
+        assertProperty("mix:lockable", "jcr:lockOwner", "String", NO_DEFAULTS, OnParentVersion.Ignore, PropertyOptions.Protected);
+        assertProperty("mix:lockable",
+                       "jcr:lockIsDeep",
+                       "Boolean",
+                       NO_DEFAULTS,
+                       OnParentVersion.Ignore,
+                       PropertyOptions.Protected);
+
+        // [nt:propertyDefinition]
+        // - jcr:name (name)
+        // - jcr:autoCreated (boolean) mandatory
+        // - jcr:mandatory (boolean) mandatory
+        // - jcr:onParentVersion (string) mandatory
+        // < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+        // - jcr:protected (boolean) mandatory
+        // - jcr:requiredType (string) mandatory
+        // < 'STRING', 'BINARY', 'LONG', 'DOUBLE', 'BOOLEAN', 'DATE', 'NAME', 'PATH', 'REFERENCE', 'UNDEFINED'
+        // - jcr:valueConstraints (string) multiple
+        // - jcr:defaultValues (undefined) multiple
+        // - jcr:multiple (boolean) mandatory
+        assertNodeType("nt:propertyDefinition", NO_SUPERTYPES, NO_PRIMARY_NAME);
+        assertProperty("nt:propertyDefinition", "jcr:name", "Name", NO_DEFAULTS);
+        assertProperty("nt:propertyDefinition", "jcr:autoCreated", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition", "jcr:mandatory", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition",
+                       "jcr:onParentVersion",
+                       "String",
+                       NO_DEFAULTS,
+                       new PropertyOptions[] {PropertyOptions.Mandatory},
+                       null,
+                       new String[] {"COPY", "VERSION", "INITIALIZE", "COMPUTE", "IGNORE", "ABORT"});
+        assertProperty("nt:propertyDefinition", "jcr:protected", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+        assertProperty("nt:propertyDefinition",
+                       "jcr:requiredType",
+                       "String",
+                       NO_DEFAULTS,
+                       new PropertyOptions[] {PropertyOptions.Mandatory},
+                       null,
+                       new String[] {"STRING", "BINARY", "LONG", "DOUBLE", "BOOLEAN", "DATE", "NAME", "PATH", "REFERENCE",
+                           "UNDEFINED"});
+        assertProperty("nt:propertyDefinition", "jcr:valueConstraints", "String", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:propertyDefinition", "jcr:defaultValues", "Undefined", NO_DEFAULTS, PropertyOptions.Multiple);
+        assertProperty("nt:propertyDefinition", "jcr:multiple", "Boolean", NO_DEFAULTS, PropertyOptions.Mandatory);
+    }
+
+    @Test
+    public void shouldImportCndThatIsEmpty() throws Exception {
+        importer.importFrom(openCndFile("empty.cnd"), problems);
+        if (problems.size() != 0) printProblems();
+        assertThat(problems.size(), is(0));
+    }
+
+    @Test
+    public void shouldImportCndForImageSequencer() throws Exception {
+        importer.importFrom(openCndFile("images.cnd"), problems);
+        if (problems.size() != 0) printProblems();
+        assertThat(problems.size(), is(0));
+    }
+
+    @Test
+    public void shouldImportCndForMp3Sequencer() throws Exception {
+        importer.importFrom(openCndFile("mp3.cnd"), problems);
+        if (problems.size() != 0) printProblems();
+        assertThat(problems.size(), is(0));
+    }
+
+    @Test
+    public void shouldNotImportFileThatIsNotAValidCnd() throws Exception {
+        importer.importFrom(openCndFile("invalid.cnd"), problems);
+        assertThat(problems.size(), is(1));
+    }
+
+    public static final String[] NO_DEFAULTS = {};
+    public static final String[] NO_SUPERTYPES = {};
+    public static final String[] NO_VALUE_CONSTRAINTS = {};
+    public static final String NO_PRIMARY_NAME = null;
+
+    public static enum PropertyOptions {
+        Mandatory,
+        Autocreated,
+        Protected,
+        Multiple,
+        FullTextSearchable,
+        QueryOrderable
+    }
+
+    public static enum ChildOptions {
+        Mandatory,
+        Autocreated,
+        Protected,
+        Multiple
+    }
+
+    public static enum NodeOptions {
+        Abstract,
+        Mixin,
+        Ordered,
+        Queryable
+    }
+
+    public static enum OnParentVersion {
+        Copy,
+        Version,
+        Initialize,
+        Compute,
+        Ignore,
+        Abort
+    }
+
+    protected void assertNodeType( String name,
+                                   String[] superTypes,
+                                   String primaryItemName,
+                                   NodeOptions... nodeOptions ) {
+        Set<NodeOptions> options = new HashSet<NodeOptions>();
+        for (NodeOptions option : nodeOptions)
+            options.add(option);
+
+        Node nodeType = node(name);
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_MIXIN, options.contains(NodeOptions.Mixin)));
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_ABSTRACT, options.contains(NodeOptions.Abstract)));
+        assertThat(nodeType, hasProperty(JcrLexicon.HAS_ORDERABLE_CHILD_NODES, options.contains(NodeOptions.Ordered)));
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_QUERYABLE, !options.contains(NodeOptions.Queryable)));
+        assertThat(nodeType, hasProperty(JcrLexicon.PRIMARY_ITEM_NAME, name(primaryItemName)));
+        if (superTypes.length != 0) {
+            Name[] superTypeNames = new Name[superTypes.length];
+            for (int i = 0; i != superTypes.length; ++i) {
+                String requiredType = superTypes[i];
+                superTypeNames[i] = name(requiredType);
+            }
+            assertThat(nodeType, hasProperty(JcrLexicon.SUPERTYPES, (Object[])superTypeNames));
+        } else {
+            assertThat(nodeType.getProperty(JcrLexicon.SUPERTYPES), is(nullValue()));
+        }
+    }
+
+    protected void assertProperty( String nodeTypeName,
+                                   String propertyName,
+                                   String requiredType,
+                                   String[] defaultValues,
+                                   PropertyOptions... propertyOptions ) {
+        assertProperty(nodeTypeName, propertyName, requiredType, defaultValues, propertyOptions, null);
+    }
+
+    protected void assertProperty( String nodeTypeName,
+                                   String propertyName,
+                                   String requiredType,
+                                   String[] defaultValues,
+                                   OnParentVersion onParentVersion,
+                                   PropertyOptions... propertyOptions ) {
+        assertProperty(nodeTypeName, propertyName, requiredType, defaultValues, propertyOptions, onParentVersion);
+    }
+
+    protected void assertProperty( String nodeTypeName,
+                                   String propertyName,
+                                   String requiredType,
+                                   String[] defaultValues,
+                                   PropertyOptions[] propertyOptions,
+                                   OnParentVersion onParentVersioning,
+                                   String... valueConstraints ) {
+        Set<PropertyOptions> options = new HashSet<PropertyOptions>();
+        for (PropertyOptions option : propertyOptions)
+            options.add(option);
+
+        Node nodeType = node(nodeTypeName + "/" + propertyName);
+        assertThat(nodeType, hasProperty(JcrLexicon.REQUIRED_TYPE, requiredType.toUpperCase()));
+        assertThat(nodeType, hasProperty(JcrLexicon.MULTIPLE, options.contains(PropertyOptions.Multiple)));
+        assertThat(nodeType, hasProperty(JcrLexicon.MANDATORY, options.contains(PropertyOptions.Mandatory)));
+        assertThat(nodeType, hasProperty(JcrLexicon.AUTO_CREATED, options.contains(PropertyOptions.Autocreated)));
+        assertThat(nodeType, hasProperty(JcrLexicon.PROTECTED, options.contains(PropertyOptions.Protected)));
+        if (onParentVersioning != null) {
+            switch (onParentVersioning) {
+                case Abort:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "ABORT"));
+                    break;
+                case Compute:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "COMPUTE"));
+                    break;
+                case Copy:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "COPY"));
+                    break;
+                case Ignore:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "IGNORE"));
+                    break;
+                case Initialize:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "INITIALIZE"));
+                    break;
+                case Version:
+                    assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "VERSION"));
+                    break;
+            }
+        } else {
+            assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "COPY")); // it's copy by default
+        }
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_FULL_TEXT_SEARCHABLE,
+                                         !options.contains(PropertyOptions.FullTextSearchable)));
+        assertThat(nodeType, hasProperty(JcrLexicon.IS_QUERY_ORDERABLE, !options.contains(PropertyOptions.QueryOrderable)));
+        if (valueConstraints.length != 0) {
+            assertThat(nodeType, hasProperty(JcrLexicon.VALUE_CONSTRAINTS, (Object[])valueConstraints));
+        } else {
+            assertThat(nodeType.getProperty(JcrLexicon.VALUE_CONSTRAINTS), is(nullValue()));
+        }
+        if (defaultValues.length != 0) {
+            assertThat(nodeType, hasProperty(JcrLexicon.DEFAULT_VALUES, (Object[])defaultValues));
+        } else {
+            assertThat(nodeType.getProperty(JcrLexicon.DEFAULT_VALUES), is(nullValue()));
+        }
+    }
+
+    protected void assertChild( String nodeTypeName,
+                                String childName,
+                                String requiredType,
+                                String defaultPrimaryType,
+                                ChildOptions[] childOptions,
+                                OnParentVersion onParentVersioning ) {
+        assertChild(nodeTypeName, childName, new String[] {requiredType}, defaultPrimaryType, childOptions, onParentVersioning);
+    }
+
+    protected void assertChild( String nodeTypeName,
+                                String childName,
+                                String requiredType,
+                                String defaultPrimaryType,
+                                OnParentVersion onParentVersioning,
+                                ChildOptions... childOptions ) {
+        assertChild(nodeTypeName, childName, new String[] {requiredType}, defaultPrimaryType, childOptions, onParentVersioning);
+    }
+
+    protected void assertChild( String nodeTypeName,
+                                String childName,
+                                String[] requiredTypes,
+                                String defaultPrimaryType,
+                                ChildOptions[] childOptions,
+                                OnParentVersion onParentVersioning ) {
+        Set<ChildOptions> options = new HashSet<ChildOptions>();
+        for (ChildOptions option : childOptions)
+            options.add(option);
+
+        Node nodeType = node(nodeTypeName + "/" + childName);
+        assertThat(nodeType, hasProperty(JcrLexicon.DEFAULT_PRIMARY_TYPE, name(defaultPrimaryType)));
+        assertThat(nodeType, hasProperty(JcrLexicon.SAME_NAME_SIBLINGS, options.contains(ChildOptions.Multiple)));
+        assertThat(nodeType, hasProperty(JcrLexicon.MANDATORY, options.contains(ChildOptions.Mandatory)));
+        assertThat(nodeType, hasProperty(JcrLexicon.AUTO_CREATED, options.contains(ChildOptions.Autocreated)));
+        assertThat(nodeType, hasProperty(JcrLexicon.PROTECTED, options.contains(ChildOptions.Protected)));
+        switch (onParentVersioning) {
+            case Abort:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "ABORT"));
+                break;
+            case Compute:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "COMPUTE"));
+                break;
+            case Copy:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "COPY"));
+                break;
+            case Ignore:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "IGNORE"));
+                break;
+            case Initialize:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "INITIALIZE"));
+                break;
+            case Version:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, "VERSION"));
+                break;
+            default:
+                assertThat(nodeType, hasProperty(JcrLexicon.ON_PARENT_VERSION, is(nullValue())));
+        }
+        if (requiredTypes.length != 0) {
+            Name[] requiredTypeNames = new Name[requiredTypes.length];
+            for (int i = 0; i != requiredTypes.length; ++i) {
+                String requiredType = requiredTypes[i];
+                requiredTypeNames[i] = name(requiredType);
+            }
+            assertThat(nodeType, hasProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES, (Object[])requiredTypeNames));
+        } else {
+            assertThat(nodeType.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES), is(nullValue()));
+        }
+    }
+}

Added: trunk/dna-cnd/src/test/resources/cnd/empty.cnd
===================================================================

Added: trunk/dna-cnd/src/test/resources/cnd/images.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/images.cnd	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/cnd/images.cnd	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+ 
+//------------------------------------------------------------------------------
+// N A M E S P A C E S
+//------------------------------------------------------------------------------
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+<image='http://jboss.org/dna/images/1.0'>
+
+// ----------------------------------------------------------
+// JCR Pre-defined Mixin Types that are not loaded by default
+// ----------------------------------------------------------
+
+[mix:mimeType] mixin 
+  - jcr:mimeType (string)  
+  - jcr:encoding (string)
+
+
+//------------------------------------------------------------------------------
+// N O D E T Y P E S
+//------------------------------------------------------------------------------
+
+/**
+ * Mixin that defines the metadata for an image
+ */
+[image:metadata] > nt:unstructured, mix:mimeType
+  - image:formatName (string) mandatory
+    < 'JPEG', 'GIF', 'PNG', 'BMP', 'PCX', 'IFF', 'RAS', 'PBM', 'PGM', 'PPM', 'PSD'
+  - image:width (long)
+  - image:height (long)
+  - image:bitsPerPixel (long)
+  - image:progressive (boolean)
+  - image:numberOfImages (long)
+  - image:physicalWidthDpi (long)
+  - image:physicalHeightDpi (long)
+  - image:physicalWidthInches (long)
+  - image:physicalHeightInches (long)
+
+
+ 
\ No newline at end of file

Added: trunk/dna-cnd/src/test/resources/cnd/invalid.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/invalid.cnd	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/cnd/invalid.cnd	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1 @@
+This is not a valid cnd file.
\ No newline at end of file

Added: trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+<rep='internal'>
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+
+//------------------------------------------------------------------------------
+// B A S E  T Y P E S
+//------------------------------------------------------------------------------
+
+[nt:base]
+  - jcr:primaryType (name) mandatory autocreated protected compute
+  - jcr:mixinTypes (name) protected multiple compute
+
+[nt:unstructured]
+  orderable
+  - * (undefined) multiple
+  - * (undefined)
+  + * (nt:base) = nt:unstructured multiple version
+
+[mix:referenceable]
+  mixin
+  - jcr:uuid (string) mandatory autocreated protected initialize
+
+[mix:lockable]
+  mixin
+  - jcr:lockOwner (string) protected ignore
+  - jcr:lockIsDeep (boolean) protected ignore
+
+//------------------------------------------------------------------------------
+// V E R S I O N I N G
+//------------------------------------------------------------------------------
+
+[mix:versionable] > mix:referenceable
+  mixin
+  - jcr:versionHistory (reference) mandatory protected
+    < 'nt:versionHistory'
+  - jcr:baseVersion (reference) mandatory protected ignore
+    < 'nt:version'
+  - jcr:isCheckedOut (boolean) = 'true' mandatory autocreated protected ignore
+  - jcr:predecessors (reference) mandatory protected multiple
+    < 'nt:version'
+  - jcr:mergeFailed (reference) protected multiple abort
+
+[nt:versionHistory] > mix:referenceable
+  - jcr:versionableUuid (string) mandatory autocreated protected abort
+  + jcr:rootVersion (nt:version) = nt:version mandatory autocreated protected abort
+  + jcr:versionLabels (nt:versionLabels) = nt:versionLabels mandatory autocreated protected abort
+  + * (nt:version) = nt:version protected abort
+
+[nt:versionLabels]
+  - * (reference) protected abort
+    < 'nt:version'
+
+[nt:version] > mix:referenceable
+  - jcr:created (date) mandatory autocreated protected abort
+  - jcr:predecessors (reference) protected multiple abort
+    < 'nt:version'
+  - jcr:successors (reference) protected multiple abort
+    < 'nt:version'
+  + jcr:frozenNode (nt:frozenNode) protected abort
+
+[nt:frozenNode] > mix:referenceable
+  orderable
+  - jcr:frozenPrimaryType (name) mandatory autocreated protected abort
+  - jcr:frozenMixinTypes (name) protected multiple abort
+  - jcr:frozenUuid (string) mandatory autocreated protected abort
+  - * (undefined) protected abort
+  - * (undefined) protected multiple abort
+  + * (nt:base) protected multiple abort
+
+[nt:versionedChild]
+  - jcr:childVersionHistory (reference) mandatory autocreated protected abort
+    < 'nt:versionHistory'
+
+//------------------------------------------------------------------------------
+// N O D E T Y P E S
+//------------------------------------------------------------------------------
+
+[nt:nodeType]
+  - jcr:nodeTypeName (name) mandatory
+  - jcr:supertypes (name) multiple
+  - jcr:isMixin (boolean) mandatory
+  - jcr:hasOrderableChildNodes (boolean) mandatory
+  - jcr:primaryItemName (name)
+  + jcr:propertyDefinition (nt:propertyDefinition) = nt:propertyDefinition multiple version
+  + jcr:childNodeDefinition (nt:childNodeDefinition) = nt:childNodeDefinition multiple version
+
+[nt:propertyDefinition]
+  - jcr:name (name)
+  - jcr:autoCreated (boolean) mandatory
+  - jcr:mandatory (boolean) mandatory
+  - jcr:onParentVersion (string) mandatory
+    < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+  - jcr:protected (boolean) mandatory
+  - jcr:requiredType (string) mandatory
+    < 'STRING', 'BINARY', 'LONG', 'DOUBLE', 'BOOLEAN', 'DATE', 'NAME', 'PATH', 'REFERENCE', 'UNDEFINED'
+  - jcr:valueConstraints (string) multiple
+  - jcr:defaultValues (undefined) multiple
+  - jcr:multiple (boolean) mandatory
+
+[nt:childNodeDefinition]
+  - jcr:name (name)
+  - jcr:autoCreated (boolean) mandatory
+  - jcr:mandatory (boolean) mandatory
+  - jcr:onParentVersion (string) mandatory
+    < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+  - jcr:protected (boolean) mandatory
+  - jcr:requiredPrimaryTypes (name) = 'nt:base' mandatory multiple
+  - jcr:defaultPrimaryType (name)
+  - jcr:sameNameSiblings (boolean) mandatory
+
+//------------------------------------------------------------------------------
+// M I S C
+//------------------------------------------------------------------------------
+
+[nt:hierarchyNode]
+  - jcr:created (date) autocreated protected initialize
+
+[nt:folder] > nt:hierarchyNode
+  + * (nt:hierarchyNode) version
+
+[nt:file] > nt:hierarchyNode
+  + jcr:content (nt:base) primary mandatory
+
+[nt:linkedFile] > nt:hierarchyNode
+  - jcr:content (reference) primary mandatory
+
+[nt:resource] > mix:referenceable
+  - jcr:encoding (string)
+  - jcr:mimeType (string) mandatory
+  - jcr:data (binary) primary mandatory
+  - jcr:lastModified (date) mandatory ignore
+
+[nt:query]
+  - jcr:statement (string)
+  - jcr:language (string)
+
+//------------------------------------------------------------------------------
+// J A C K R A B B I T   I N T E R N A L S
+//------------------------------------------------------------------------------
+
+[rep:nodeTypes]
+  + * (nt:nodeType) = nt:nodeType protected abort
+
+[rep:root] > nt:unstructured
+  orderable
+  + jcr:system (rep:system) = rep:system mandatory ignore
+
+[rep:system]
+  orderable
+  + jcr:versionStorage (rep:versionStorage) = rep:versionStorage mandatory protected abort
+  + jcr:nodeTypes (rep:nodeTypes) = rep:nodeTypes mandatory protected abort
+  + * (nt:base) = nt:unstructured multiple ignore
+
+[rep:versionStorage]
+  + * (nt:versionHistory) = nt:versionHistory protected multiple abort
+  + * (rep:versionStorage) = rep:versionStorage protected multiple abort

Added: trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-283-early-draft.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-283-early-draft.cnd	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-283-early-draft.cnd	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,218 @@
+<jcr=http://www.jcp.org/jcr/1.0>
+<nt=http://www.jcp.org/jcr/nt/1.0>
+<mix=http://www.jcp.org/jcr/mix/1.0>
+<sv=http://www.jcp.org/jcr/sv/1.0>
+
+// ------------------------------------------------------------------------
+//                             Pre-defined Node Types
+// ------------------------------------------------------------------------
+
+[nt:base] abstract 
+  - jcr:primaryType (name) mandatory autocreated 
+      protected compute 
+  - jcr:mixinTypes (name) protected multiple compute
+  
+[nt:unstructured] 
+  orderable 
+  - * (undefined) multiple 
+  - * (undefined) 
+  + * (nt:base) = nt:unstructured multiple version 
+
+[nt:hierarchyNode] > mix:created abstract 
+
+[nt:file] > nt:hierarchyNode 
+  + jcr:content (nt:base) primary mandatory
+
+[nt:linkedFile] > nt:hierarchyNode 
+  - jcr:content (reference) primary mandatory
+
+[nt:folder] > nt:hierarchyNode 
+  + * (nt:hierarchyNode) version
+  
+[nt:resource] > mix:referenceable, 
+                mix:mimeTyped, 
+                mix:lastModified 
+  - jcr:data (binary) primary mandatory
+
+[nt:nodeType] 
+  - jcr:nodeTypeName (name) mandatory 
+  - jcr:supertypes (name) multiple 
+  - jcr:isAbstract (boolean) mandatory 
+  - jcr:isMixin (boolean) mandatory 
+  - jcr:hasOrderableChildNodes (boolean) mandatory 
+  - jcr:primaryItemName (name) 
+  + jcr:propertyDefinition (nt:propertyDefinition) 
+      = nt:propertyDefinition multiple version 
+  + jcr:childNodeDefinition (nt:childNodeDefinition) 
+      = nt:childNodeDefinition multiple version
+
+[nt:propertyDefinition] 
+  - jcr:name (name) 
+  - jcr:autoCreated (boolean) mandatory 
+  - jcr:mandatory (boolean) mandatory 
+  - jcr:onParentVersion (string) mandatory 
+      < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 
+        'IGNORE', 'ABORT' 
+  - jcr:protected (boolean) mandatory 
+  - jcr:requiredType (string) mandatory 
+    < 'STRING', 'BINARY', 'LONG', 'DOUBLE', 'BOOLEAN', 
+      'DATE', 'NAME', 'PATH', 'REFERENCE', 'UNDEFINED' 
+  - jcr:valueConstraints (string) multiple 
+  - jcr:defaultValues (undefined) multiple 
+  - jcr:multiple (boolean) mandatory
+
+[nt:childNodeDefinition] 
+  - jcr:name (name) 
+  - jcr:autoCreated (boolean) mandatory 
+  - jcr:mandatory (boolean) mandatory 
+  - jcr:onParentVersion (string) mandatory 
+      < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 
+        'IGNORE', 'ABORT' 
+  - jcr:protected (boolean) mandatory 
+  - jcr:requiredPrimaryTypes (name) = 'nt:base' mandatory
+      multiple 
+  - jcr:defaultPrimaryType (name) 
+  - jcr:sameNameSiblings (boolean) mandatory 
+
+[nt:versionHistory] > mix:referenceable 
+  - jcr:versionableUuid (string) mandatory autocreated 
+      protected abort 
+  - jcr:copiedFrom (reference) protected abort 
+      < 'nt:version' 
+  + jcr:rootVersion (nt:version) = nt:version mandatory 
+      autocreated protected abort 
+  + jcr:versionLabels (nt:versionLabels) 
+      = nt:versionLabels mandatory autocreated protected 
+      abort 
+  + * (nt:version) = nt:version protected abort
+
+[nt:versionLabels] 
+  - * (reference) protected abort 
+      < 'nt:version'
+
+[nt:version] > mix:referenceable 
+  - jcr:created (date) mandatory autocreated protected 
+      abort 
+  - jcr:predecessors (reference) protected multiple abort 
+      < 'nt:version' 
+  - jcr:successors (reference) protected multiple abort 
+      < 'nt:version' 
+  - jcr:activity (reference) protected abort 
+      < 'nt:activity' 
+  + jcr:frozenNode (nt:frozenNode) protected abort
+
+[nt:frozenNode] > mix:referenceable 
+  orderable 
+  - jcr:frozenPrimaryType (name) mandatory autocreated 
+      protected abort 
+  - jcr:frozenMixinTypes (name) protected multiple abort 
+  - jcr:frozenUuid (string) mandatory autocreated 
+      protected abort 
+  - * (undefined) protected abort 
+  - * (undefined) protected multiple abort 
+  + * (nt:base) protected multiple abort
+
+[nt:versionedChild] 
+  - jcr:childVersionHistory (reference) mandatory 
+      autocreated protected abort 
+      < 'nt:versionHistory'
+
+[nt:query] 
+  - jcr:statement (string) 
+  - jcr:language (string)
+
+[nt:activity] > mix:referenceable 
+  - jcr:title (string) 
+    mandatory autocreated protected
+
+[nt:configuration] > mix:versionable 
+  - jcr:root (reference) 
+    mandatory autocreated protected
+
+[nt:address] 
+  - jcr:protocol (string) 
+  - jcr:host (string)  
+  - jcr:port (string) 
+  - jcr:repository (string) 
+  - jcr:workspace (string) 
+  - jcr:path (path) 
+  - jcr:id (weakreference)
+
+[nt:naturalText] 
+  - jcr:text (string) 
+  - jcr:messageId (string)
+
+
+// ------------------------------------------------------------------------
+//                             Pre-defined Mixins
+// ------------------------------------------------------------------------
+
+[mix:lockable] 
+  mixin 
+  - jcr:lockOwner (string) protected ignore 
+  - jcr:lockIsDeep (boolean) protected ignore 
+
+[mix:referenceable] 
+  mixin 
+  - jcr:uuid (string) mandatory autocreated protected 
+      initialize 
+
+[mix:simpleVersionable] > mix:referenceable 
+  mixin 
+  - jcr:isCheckedOut (boolean) = 'true' mandatory 
+      autocreated protected ignore 
+  - jcr:versionLabels (name) mandatory protected multiple
+
+[mix:versionable] > mix:simpleVersionable 
+  mixin 
+  - jcr:versionHistory (reference) mandatory protected 
+      < 'nt:versionHistory' 
+  - jcr:baseVersion (reference) mandatory protected 
+      ignore 
+      < 'nt:version' 
+  - jcr:predecessors (reference) mandatory protected 
+      multiple 
+      < 'nt:version' 
+  - jcr:mergeFailed (reference) protected multiple abort 
+  - jcr:activity (reference) 
+      < 'nt:version' 
+  - jcr:configuration (reference) 
+      < 'nt:configuration'
+
+[mix:lifecycle] 
+  mixin 
+  - jcr:lifecyclePolicy (reference) protected initialize 
+  - jcr:currentLifecycleState (string) protected 
+initialize
+
+[mix:managedRetention] > mix:referenceable    
+ mixin 
+  - jcr:hold (string) protected multiple 
+  - jcr:isDeep (boolean) protected multiple 
+  - jcr:retentionPolicy (reference) protected
+
+[mix:shareable] > mix:referenceable mixin
+
+[mix:title] mixin 
+  - jcr:title (string) 
+  - jcr:description (string)  
+
+[mix:created] mixin 
+  - jcr:created (date)  
+  - jcr:createdBy (string)
+
+[mix:lastModified] mixin 
+  - jcr:lastModified (date) 
+  - jcr:lastModifiedBy (string)
+
+[mix:language] mixin 
+  - jcr:language (string) 
+
+[mix:mimeType] mixin 
+  - jcr:mimeType (string)  
+  - jcr:encoding (string)
+
+
+
+
+    
\ No newline at end of file

Added: trunk/dna-cnd/src/test/resources/cnd/mp3.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/mp3.cnd	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/cnd/mp3.cnd	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+ 
+//------------------------------------------------------------------------------
+// N A M E S P A C E S
+//------------------------------------------------------------------------------
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+<mp3='http://jboss.org/dna/mp3/1.0'>
+
+// ----------------------------------------------------------
+// JCR Pre-defined Mixin Types that are not loaded by default
+// ----------------------------------------------------------
+
+[mix:mimeType] mixin 
+  - jcr:mimeType (string)  
+  - jcr:encoding (string)
+
+
+//------------------------------------------------------------------------------
+// N O D E T Y P E S
+//------------------------------------------------------------------------------
+
+[mp3:metadata] > nt:unstructured, mix:mimeTyped
+  - mp3:title (string)
+  - mp3:author (string)
+  - mp3:album (string)
+  - mp3:year (long)
+  - mp3:comment (string)
+ 
+ 
\ No newline at end of file

Added: trunk/dna-cnd/src/test/resources/log4j.properties
===================================================================
--- trunk/dna-cnd/src/test/resources/log4j.properties	                        (rev 0)
+++ trunk/dna-cnd/src/test/resources/log4j.properties	2009-04-20 19:07:48 UTC (rev 840)
@@ -0,0 +1,13 @@
+# 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.dna.cnd=INFO
+

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-04-17 14:23:57 UTC (rev 839)
+++ trunk/pom.xml	2009-04-20 19:07:48 UTC (rev 840)
@@ -114,7 +114,8 @@
 		<module>dna-common</module>
 		<module>dna-graph</module>
 		<module>dna-repository</module>
-		<module>dna-jcr</module>
+		<module>dna-cnd</module>
+		<!--module>dna-jcr</module-->
 		<module>extensions/dna-classloader-maven</module>
 		<module>extensions/dna-sequencer-images</module>
 		<module>extensions/dna-sequencer-mp3</module>



From dna-commits at lists.jboss.org  Mon Apr 20 16:21:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 20 Apr 2009 16:21:21 -0400
Subject: [dna-commits] DNA SVN: r841 - in trunk:
	dna-graph/src/test/java/org/jboss/dna/graph/sequencer and 15
	other directories.
Message-ID: <E1LvzzV-00013I-PW@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-20 16:21:21 -0400 (Mon, 20 Apr 2009)
New Revision: 841

Added:
   trunk/extensions/dna-sequencer-cnd/src/main/java/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencerI18n.java
   trunk/extensions/dna-sequencer-cnd/src/main/resources/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/cnd/
   trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/cnd/CndSequencerI18n.properties
   trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerI18nTest.java
   trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerTest.java
Removed:
   trunk/extensions/dna-sequencer-cnd/src/main/antlr/
   trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParseFilesTest.java
   trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParserTest.java
Modified:
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/sequencer/MockSequencerContext.java
   trunk/extensions/dna-sequencer-cnd/.classpath
   trunk/extensions/dna-sequencer-cnd/.project
   trunk/extensions/dna-sequencer-cnd/pom.xml
   trunk/pom.xml
Log:
DNA-363 Define node types via CND files

Changed the DNA CND Sequencer to use the new CND importer.

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/sequencer/MockSequencerContext.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/sequencer/MockSequencerContext.java	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/sequencer/MockSequencerContext.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -26,7 +26,6 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.collection.SimpleProblems;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.sequencer.SequencerContext;
 
 /**
  * @author John Verhaeg
@@ -39,4 +38,15 @@
         getNamespaceRegistry().register("dnadtd", "http://www.jboss.org/dna/dtd/1.0");
         getNamespaceRegistry().register("dnaxml", "http://www.jboss.org/dna/xml/1.0");
     }
+
+    public MockSequencerContext( String inputPath ) {
+        this(new ExecutionContext(), inputPath);
+    }
+
+    public MockSequencerContext( ExecutionContext context,
+                                 String inputPath ) {
+        super(context, context.getValueFactories().getPathFactory().create(inputPath), null, null, new SimpleProblems());
+        getNamespaceRegistry().register("dnadtd", "http://www.jboss.org/dna/dtd/1.0");
+        getNamespaceRegistry().register("dnaxml", "http://www.jboss.org/dna/xml/1.0");
+    }
 }

Modified: trunk/extensions/dna-sequencer-cnd/.classpath
===================================================================
--- trunk/extensions/dna-sequencer-cnd/.classpath	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/extensions/dna-sequencer-cnd/.classpath	2009-04-20 20:21:21 UTC (rev 841)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
-	<classpathentry kind="src" path="target/generated-sources/antlr"/>
-	<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/antlr"/>
-	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="output" path="target/classes"/>
+    <classpathentry kind="src" path="src/main/java"/>
+    <classpathentry kind="src" path="src/main/resources"/>
+    <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+    <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+    <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+    <classpathentry kind="output" path="target/classes"/>
 </classpath>

Modified: trunk/extensions/dna-sequencer-cnd/.project
===================================================================
--- trunk/extensions/dna-sequencer-cnd/.project	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/extensions/dna-sequencer-cnd/.project	2009-04-20 20:21:21 UTC (rev 841)
@@ -15,16 +15,6 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
-		<buildCommand>
-			<name>org.antlr.eclipse.core.warningcleanerbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.antlr.eclipse.core.smapbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>

Modified: trunk/extensions/dna-sequencer-cnd/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-cnd/pom.xml	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/extensions/dna-sequencer-cnd/pom.xml	2009-04-20 20:21:21 UTC (rev 841)
@@ -19,14 +19,27 @@
       <groupId>org.jboss.dna</groupId>
       <artifactId>dna-graph</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.antlr</groupId>
-      <artifactId>antlr-runtime</artifactId>
-      <version>3.0.1</version>
-    </dependency>
+		<dependency>
+			<groupId>org.jboss.dna</groupId>
+			<artifactId>dna-cnd</artifactId>
+		</dependency>
     
     <!-- Testing -->
     <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-graph</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
     </dependency>
@@ -35,20 +48,4 @@
       <artifactId>hamcrest-library</artifactId>
     </dependency>
   </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>antlr3-maven-plugin</artifactId>
-        <version>1.0</version>
-        <executions>
-          <execution>
-            <goals>
-              <goal>antlr</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
 </project>
\ No newline at end of file

Added: trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java	                        (rev 0)
+++ trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -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.
+ *
+ * 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.sequencer.cnd;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import org.jboss.dna.cnd.CndImporter;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.io.Destination;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.sequencer.SequencerContext;
+import org.jboss.dna.graph.sequencer.SequencerOutput;
+import org.jboss.dna.graph.sequencer.StreamSequencer;
+
+/**
+ * 
+ */
+public class CndSequencer implements StreamSequencer {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.sequencer.StreamSequencer#sequence(java.io.InputStream,
+     *      org.jboss.dna.graph.sequencer.SequencerOutput, org.jboss.dna.graph.sequencer.SequencerContext)
+     */
+    public void sequence( InputStream stream,
+                          SequencerOutput output,
+                          SequencerContext context ) {
+        // Create the destination that forwards to the sequencer output ...
+        Destination destination = new OutputDestination(output, context);
+        // Use the CND importer ...
+        Path root = context.getValueFactories().getPathFactory().createRootPath();
+        CndImporter importer = new CndImporter(destination, root);
+        String resourceName = context.getInputPath().getString(context.getNamespaceRegistry());
+        try {
+            importer.importFrom(stream, context.getProblems(), resourceName);
+        } catch (IOException e) {
+            context.getProblems().addError(e, CndSequencerI18n.errorSequencingCndContent, e.getLocalizedMessage());
+        }
+    }
+
+    protected class OutputDestination implements Destination {
+        private final SequencerOutput output;
+        private final SequencerContext context;
+
+        protected OutputDestination( SequencerOutput output,
+                                     SequencerContext context ) {
+            this.output = output;
+            this.context = context;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.io.Destination#getExecutionContext()
+         */
+        public ExecutionContext getExecutionContext() {
+            return context;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.io.Destination#create(org.jboss.dna.graph.property.Path, java.util.List)
+         */
+        public void create( Path path,
+                            List<Property> properties ) {
+            for (Property property : properties) {
+                output.setProperty(path, property.getName(), property.getValues());
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.io.Destination#create(org.jboss.dna.graph.property.Path,
+         *      org.jboss.dna.graph.property.Property, org.jboss.dna.graph.property.Property[])
+         */
+        public void create( Path path,
+                            Property firstProperty,
+                            Property... additionalProperties ) {
+            output.setProperty(path, firstProperty.getName(), firstProperty.getValues());
+            for (Property property : additionalProperties) {
+                output.setProperty(path, property.getName(), property.getValues());
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.io.Destination#submit()
+         */
+        public void submit() {
+            // nothing to call on the sequencer output ...
+        }
+    }
+
+}

Added: trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencerI18n.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencerI18n.java	                        (rev 0)
+++ trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencerI18n.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -0,0 +1,57 @@
+/*
+ * 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.sequencer.cnd;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ */
+public final class CndSequencerI18n {
+
+    public static I18n sequencerTaskName;
+    public static I18n errorSequencingCndContent;
+
+    static {
+        try {
+            I18n.initialize(CndSequencerI18n.class);
+        } catch (final Exception err) {
+            System.err.println(err);
+        }
+    }
+
+    public static Set<Locale> getLocalizationProblemLocales() {
+        return I18n.getLocalizationProblemLocales(CndSequencerI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems() {
+        return I18n.getLocalizationProblems(CndSequencerI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems( Locale locale ) {
+        return I18n.getLocalizationProblems(CndSequencerI18n.class, locale);
+    }
+}

Added: trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/cnd/CndSequencerI18n.properties
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/cnd/CndSequencerI18n.properties	                        (rev 0)
+++ trunk/extensions/dna-sequencer-cnd/src/main/resources/org/jboss/dna/sequencer/cnd/CndSequencerI18n.properties	2009-04-20 20:21:21 UTC (rev 841)
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+sequencerTaskName = Processing Compact Node Definition (CND) contents
+errorSequencingCndContent = Error while sequencing the CND content: {0}

Deleted: trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParseFilesTest.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParseFilesTest.java	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParseFilesTest.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -1,367 +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.sequencer.cnd;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import org.antlr.runtime.ANTLRInputStream;
-import org.antlr.runtime.CommonTokenStream;
-import org.antlr.runtime.RuleReturnScope;
-import org.antlr.runtime.tree.Tree;
-import org.junit.After;
-import org.junit.Test;
-
-public class CndParseFilesTest {
-
-    /*
-     * AST looks like this:
-     * 
-     * root (nil)
-     *      COMMENTS (*)
-     *           comment-1
-     *           comment-2
-     *           ...
-     *      NAMESPACES (*)
-     *           namespace_mapping-1
-     *           namespace_mapping-2
-     *           ...
-     *      NODE_TYPE_DEFINITIONS (*)
-     *           NODE_TYPE_DEFINITION
-     *                JCR_NAME
-     *                     node_type_definition_name-1
-     *                SUPERTYPES (*)
-     *                     supertype-1
-     *                     supertype-2
-     *                     ...
-     *                NODE_TYPE_OPTIONS (*)
-     *                     option-1
-     *                     option-2
-     *                     ...
-     *                PROPERTIES (*)
-     *                     PROPERTY_DEFINITION
-     *                          JCR_NAME
-     *                               property_name-1
-     *                          PROPERTY_TYPE (*)
-     *                               property type
-     *                          ATTRIBUTES (*)
-     *                               attribute-1
-     *                               attribute-2
-     *                               ...
-     *                     PROPERTY_DEFINITION
-     *                          JCR_NAME
-     *                               property_name-2
-     *                          ...
-     *                     ...
-     *                CHILD_NODE_DEFINTIONS (*)
-     *                     CHILD_NODE_DEFINTION
-     *                          JCR_NAME
-     *                              child_node_name-1
-     *                          REQUIRED_TYPES (*)
-     *                              required_type-1
-     *                              required_type-2
-     *                              ...
-     *                          DEFAULT_TYPE (*)
-     *                              default_type
-     *                          ATTRIBUTES (*)
-     *                              attribute-1
-     *                              attribute-2
-     *                              ...
-     *                     CHILD_NODE_DEFINTION
-     *                          JCR_NAME
-     *                              child_node_name-2
-     *                          ...
-     *                     ...
-     *           NODE_TYPE_DEFINITION
-     *                JCR_NAME
-     *                     node_type_definition_name-2
-     *                ...
-     *           ...
-     *           
-     *  Note: (*) indicates node only appears if a child node exists
-     */
-
-    // =============================================================================================================================
-    // Constants
-    // =============================================================================================================================
-    private static final String TEST_DATA_PATH = "./src/test/resources/";
-
-    private static final String BUILTIN_NODETYPES_CND = TEST_DATA_PATH + "builtin_nodetypes.cnd";
-    private static final String EMPTY_CND = TEST_DATA_PATH + "empty.cnd";
-    private static final String IMAGES_CND = TEST_DATA_PATH + "images.cnd";
-    private static final String INVALID_CND = TEST_DATA_PATH + "invalid.cnd";
-    private static final String MP3_CND = TEST_DATA_PATH + "mp3.cnd";
-
-    private static final int COMMENTS_INDEX = 0;
-    private static final int NAMESPACES_INDEX = 1;
-    private static final int NODE_TYPE_DEFINITIONS_INDEX = 2;
-    private static final int PROPERTIES_INDEX = 2;
-
-    // =============================================================================================================================
-    // Fields
-    // =============================================================================================================================
-
-    private InputStream stream;
-
-    // =============================================================================================================================
-    // Methods
-    // =============================================================================================================================
-
-    @After
-    public void afterEach() throws IOException {
-        if (this.stream != null) {
-            try {
-                this.stream.close();
-            } finally {
-                this.stream = null;
-            }
-        }
-    }
-
-    private void assertCommentsCount( Tree root,
-                                      int count ) {
-        Tree comments = root.getChild(COMMENTS_INDEX);
-        assertThat("Tree type was not CndParser.COMMENTS", comments.getType(), is(CndParser.COMMENTS));
-
-        // make sure right number of comments
-        assertThat("Incorrect number of comments", comments.getChildCount(), is(count));
-    }
-
-    private void assertNamespacesCount( Tree root,
-                                        int count ) {
-        Tree namespaces = getNamespaces(root);
-        assertThat("Incorrect number of namespaces", namespaces.getChildCount(), is(count));
-    }
-
-    private void assertNodeTypeDefinitionsCount( Tree root,
-                                                 int count ) {
-        Tree nodeTypeDefinitions = getNodeTypeDefinitions(root);
-        assertThat("Incorrect number of node type definitions", nodeTypeDefinitions.getChildCount(), is(count));
-    }
-
-    private void assertNodeTypeDefinitionsPropertiesCount( Tree root,
-                                                           int nodeTypeDefinitionIndex,
-                                                           int count ) {
-        Tree nodeTypeDefinitions = getNodeTypeDefinitions(root);
-
-        Tree nodeType = nodeTypeDefinitions.getChild(nodeTypeDefinitionIndex);
-        assertThat("Tree type was not CndParser.NODE_TYPE_DEFINITION", nodeType.getType(), is(CndParser.NODE_TYPE_DEFINITION));
-
-        Tree properties = nodeType.getChild(PROPERTIES_INDEX); // properties container node
-        assertThat("Tree type was not CndParser.PROPERTIES", properties.getType(), is(CndParser.PROPERTIES));
-
-        // make sure right number of properties
-        assertThat("Incorrect number of node type properties for first node type", properties.getChildCount(), is(count));
-    }
-
-    private CndParser createParser( File file ) throws IOException {
-        afterEach(); // make sure previous stream is closed
-        assertThat('\'' + file.getAbsolutePath() + "' does not exist", file.exists(), is(true));
-
-        this.stream = new FileInputStream(file);
-        CndLexer lexer = new CndLexer(new ANTLRInputStream(this.stream));
-        CommonTokenStream tokens = new CommonTokenStream(lexer);
-
-        return new CndParser(tokens);
-    }
-
-    /**
-     * @param root the AST root
-     * @return the NAMESPACES tree node
-     */
-    private Tree getNamespaces( Tree root ) {
-        Tree namespaces = root.getChild(NAMESPACES_INDEX);
-
-        if (namespaces != null) {
-            assertThat("Tree type was not CndParser.NAMESPACES", namespaces.getType(), is(CndParser.NAMESPACES));
-        }
-
-        return namespaces;
-    }
-
-    /**
-     * @param root the AST root
-     * @return the NODE_TYPE_DEFINITIONS tree node
-     */
-    private Tree getNodeTypeDefinitions( Tree root ) {
-        Tree nodeTypeDefinitions = root.getChild(NODE_TYPE_DEFINITIONS_INDEX);
-
-        if (nodeTypeDefinitions != null) {
-            assertThat("Tree type was not CndParser.NODE_TYPE_DEFINITIONS",
-                       nodeTypeDefinitions.getType(),
-                       is(CndParser.NODE_TYPE_DEFINITIONS));
-        }
-
-        return nodeTypeDefinitions;
-    }
-
-    /**
-     * @param fileName the name of the file to parse
-     * @return the root node of the AST
-     * @throws Exception if root is null
-     */
-    private Tree parse( String fileName ) throws Exception {
-        return parse(new File(fileName).getCanonicalFile());
-    }
-
-    /**
-     * @param file the file to parse
-     * @return the root node of the AST
-     * @throws Exception if root is null
-     */
-    private Tree parse( File file ) throws Exception {
-        CndParser parser = createParser(file);
-        RuleReturnScope result = parser.cnd();
-        Tree root = (Tree)result.getTree();
-        assertThat(root, notNullValue());
-
-        return root;
-    }
-
-    // =============================================================================================================================
-    // Tests
-    // =============================================================================================================================
-
-    @Test
-    public void shouldParseEmptyCnd() throws Exception {
-        Tree root = parse(EMPTY_CND);
-
-        // make sure no children
-        assertThat(root.getChildCount(), is(0));
-
-        // make sure nothing got parsed
-        assertThat(root.getTokenStartIndex(), is(-1));
-    }
-
-    @Test
-    public void shouldNotParseInvalidCnd() throws Exception {
-        Tree root = parse(INVALID_CND);
-
-        // make sure nothing got parsed
-        assertThat(root.getTokenStopIndex(), is(0));
-    }
-
-    // @Test
-    public void shouldParseBuiltinNodeTypesCndComments() throws Exception {
-        Tree root = parse(BUILTIN_NODETYPES_CND);
-        assertCommentsCount(root, 16);
-    }
-
-    @Test
-    public void shouldParseBuiltinNodeTypesCndNamespaces() throws Exception {
-        Tree root = parse(BUILTIN_NODETYPES_CND);
-        assertNamespacesCount(root, 4);
-    }
-
-    // @Test
-    public void shouldParseBuiltinNodeTypesCndNodeTypeDefinitions() throws Exception {
-        Tree root = parse(BUILTIN_NODETYPES_CND);
-
-        // make sure correct number of node type definitions
-        assertNodeTypeDefinitionsCount(root, 23);
-
-        // make sure right number of node type definitions for each node type
-        assertNodeTypeDefinitionsPropertiesCount(root, 0, 2); // [nt:base]
-        assertNodeTypeDefinitionsPropertiesCount(root, 1, 2); // [nt:unstructured]
-        assertNodeTypeDefinitionsPropertiesCount(root, 2, 1); // [mix:referenceable]
-        assertNodeTypeDefinitionsPropertiesCount(root, 3, 2); // [mix:lockable]
-        assertNodeTypeDefinitionsPropertiesCount(root, 4, 5); // [mix:versionable]
-        assertNodeTypeDefinitionsPropertiesCount(root, 5, 1); // [nt:versionHistory]
-        assertNodeTypeDefinitionsPropertiesCount(root, 6, 1); // [nt:versionLabels]
-        assertNodeTypeDefinitionsPropertiesCount(root, 7, 3); // [nt:version]
-        assertNodeTypeDefinitionsPropertiesCount(root, 8, 5); // [nt:frozenNode]
-        assertNodeTypeDefinitionsPropertiesCount(root, 9, 1); // [nt:versionedChild]
-        assertNodeTypeDefinitionsPropertiesCount(root, 10, 5); // [nt:nodeType]
-        assertNodeTypeDefinitionsPropertiesCount(root, 11, 9); // [nt:propertyDefinition]
-        assertNodeTypeDefinitionsPropertiesCount(root, 12, 8); // [nt:childNodeDefinition]
-        assertNodeTypeDefinitionsPropertiesCount(root, 13, 1); // [nt:hierarchyNode]
-        assertNodeTypeDefinitionsPropertiesCount(root, 14, 0); // [nt:folder]
-        assertNodeTypeDefinitionsPropertiesCount(root, 15, 0); // [nt:file]
-        assertNodeTypeDefinitionsPropertiesCount(root, 16, 1); // [nt:linkedFile]
-        assertNodeTypeDefinitionsPropertiesCount(root, 17, 4); // [nt:resource]
-        assertNodeTypeDefinitionsPropertiesCount(root, 18, 2); // [nt:query]
-        assertNodeTypeDefinitionsPropertiesCount(root, 19, 0); // [rep:nodeTypes]
-        assertNodeTypeDefinitionsPropertiesCount(root, 20, 0); // [rep:root]
-        assertNodeTypeDefinitionsPropertiesCount(root, 21, 0); // [rep:system]
-        assertNodeTypeDefinitionsPropertiesCount(root, 22, 0); // [rep:versionStorage]
-    }
-
-    @Test
-    public void shouldParseImagesCndComments() throws Exception {
-        Tree root = parse(IMAGES_CND);
-        assertCommentsCount(root, 11);
-    }
-
-    @Test
-    public void shouldParseImagesCndNamespaces() throws Exception {
-        Tree root = parse(IMAGES_CND);
-        assertNamespacesCount(root, 4);
-    }
-
-    @Test
-    public void shouldParseImagesCndNodeTypeDefinitions() throws Exception {
-        Tree root = parse(IMAGES_CND);
-
-        // make sure correct number of node type definitions
-        assertNodeTypeDefinitionsCount(root, 2);
-
-        // make sure right number of node type definitions for each node type
-        assertNodeTypeDefinitionsPropertiesCount(root, 0, 2); // [mix:mimeType]
-        assertNodeTypeDefinitionsPropertiesCount(root, 1, 10); // [image:metadata]
-    }
-
-    @Test
-    public void shouldParseMp3CndComments() throws Exception {
-        Tree root = parse(MP3_CND);
-
-        // this will be the COMMENTS imaginary container token
-        Tree comments = root.getChild(COMMENTS_INDEX);
-        assertThat("Tree type was not CndParser.COMMENTS", comments.getType(), is(CndParser.COMMENTS));
-
-        // make sure right number of comments
-        assertThat("Incorrect number of comments", comments.getChildCount(), is(10));
-    }
-
-    @Test
-    public void shouldParseMp3CndNamespaces() throws Exception {
-        Tree root = parse(MP3_CND);
-        assertNamespacesCount(root, 4);
-    }
-
-    @Test
-    public void shouldParseMp3CndNodeTypeDefinitions() throws Exception {
-        Tree root = parse(MP3_CND);
-
-        // make sure correct number of node type definitions
-        assertNodeTypeDefinitionsCount(root, 2);
-
-        // make sure right number of node type definitions for each node type
-        assertNodeTypeDefinitionsPropertiesCount(root, 0, 2); // [mix:mimeType]
-        assertNodeTypeDefinitionsPropertiesCount(root, 1, 5); // [mp3:metadata]
-    }
-}

Deleted: trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParserTest.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParserTest.java	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndParserTest.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -1,768 +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.sequencer.cnd;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertThat;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import org.antlr.runtime.ANTLRInputStream;
-import org.antlr.runtime.CommonTokenStream;
-import org.antlr.runtime.RuleReturnScope;
-import org.antlr.runtime.tree.RewriteEmptyStreamException;
-import org.junit.After;
-import org.junit.Test;
-
-public class CndParserTest {
-
-    // =============================================================================================================================
-    // Fields
-    // =============================================================================================================================
-
-    private InputStream stream;
-
-    // =============================================================================================================================
-    // Methods
-    // =============================================================================================================================
-
-    @After
-    public void afterEach() throws IOException {
-        if (this.stream != null) {
-            try {
-                this.stream.close();
-            } finally {
-                this.stream = null;
-            }
-        }
-    }
-
-    private CndParser createParser( String input ) throws IOException {
-        afterEach(); // make sure previous stream is closed
-        this.stream = new ByteArrayInputStream(input.getBytes("UTF-8"));
-        CndLexer lexer = new CndLexer(new ANTLRInputStream(this.stream));
-        CommonTokenStream tokens = new CommonTokenStream(lexer);
-        return new CndParser(tokens);
-    }
-
-    // =============================================================================================================================
-    // Comment Parsing Tests
-    // =============================================================================================================================
-
-    private void parseComment( String option,
-                               boolean success ) throws Exception {
-        CndParser parser = createParser(option);
-        RuleReturnScope scope = parser.comment();
-
-        if (success) {
-            assertThat("Value '" + option + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(option, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + option + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseSingleLineComment() throws Exception {
-        parseComment("// This is a single line comment", true);
-    }
-
-    @Test
-    public void shouldParseMultiLineComment() throws Exception {
-        parseComment("/* This\nis\na\nmulti-line\ncomment */", true);
-    }
-
-    @Test
-    public void shouldNotParseNonComment() throws Exception {
-        parseComment("This is not a valid comment", false);
-    }
-
-    @Test
-    public void shouldNotParseMalformedComment() throws Exception {
-        parseComment("/* This comment is missing ending delimeters", false);
-    }
-
-    // =============================================================================================================================
-    // Node Type Name Parsing Tests
-    // =============================================================================================================================
-
-    private void parseNodeTypeName( String name,
-                                    boolean success ) throws Exception {
-        CndParser parser = createParser(name);
-        RuleReturnScope scope = parser.node_type_name();
-
-        if (success) {
-            assertThat("Value '" + name + "' did not parse and should have", scope.getTree(), notNullValue());
-
-            // strip off brackets
-            assertThat(name.substring(1, name.length() - 1), is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + name + "' should not parse", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseUnquotedNodeTypeName() throws Exception {
-        parseNodeTypeName("[typeName]", true);
-    }
-
-    @Test
-    public void shouldParseQuotedNodeTypeName() throws Exception {
-        parseNodeTypeName("['quotedName']", true);
-    }
-
-    @Test
-    public void shouldNotParseNodeTypeNameWithSpaces() throws Exception {
-        parseNodeTypeName("[no spaces allowed]", false);
-    }
-
-    @Test
-    public void shouldNotParseNodeTypeNameWithMissingEndBracket() throws Exception {
-        parseNodeTypeName("[missingClosingBracket", false);
-    }
-
-    @Test( expected = RewriteEmptyStreamException.class )
-    public void shouldNotParseNodeTypeNameWithMissingBracketsButNoName() throws Exception {
-        parseNodeTypeName("[]", false);
-    }
-
-    // =============================================================================================================================
-    // Node Type Abstract Option Parsing Tests
-    // =============================================================================================================================
-
-    private void parseAbstractOption( String option,
-                                      boolean success ) throws Exception {
-        CndParser parser = createParser(option);
-        RuleReturnScope scope = parser.abs_opt();
-
-        if (success) {
-            assertThat("Value '" + option + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(option, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + option + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseAbstractOption() throws Exception {
-        parseAbstractOption("abstract", true);
-        parseAbstractOption("abs", true);
-        parseAbstractOption("a", true);
-    }
-
-    @Test
-    public void shouldNotParseInvalidAbstractOption() throws Exception {
-        parseAbstractOption("abstractoption", false);
-        parseAbstractOption("A", false);
-    }
-
-    // =============================================================================================================================
-    // Node Type Orderable Option Parsing Tests
-    // =============================================================================================================================
-
-    private void parseOrderableOption( String option,
-                                       boolean success ) throws Exception {
-        CndParser parser = createParser(option);
-        RuleReturnScope scope = parser.orderable_opt();
-
-        if (success) {
-            assertThat("Value '" + option + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(option, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + option + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseOrderableOption() throws Exception {
-        parseOrderableOption("orderable", true);
-        parseOrderableOption("ord", true);
-        parseOrderableOption("o", true);
-    }
-
-    @Test
-    public void shouldNotParseInvalidOrderableOption() throws Exception {
-        parseOrderableOption("orderableoption", false);
-        parseOrderableOption("O", false);
-    }
-
-    // =============================================================================================================================
-    // Node Type Mixin Option Parsing Tests
-    // =============================================================================================================================
-
-    private void parseMixinOption( String option,
-                                   boolean success ) throws Exception {
-        CndParser parser = createParser(option);
-        RuleReturnScope scope = parser.mixin_opt();
-
-        if (success) {
-            assertThat("Value '" + option + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(option, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + option + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseMixinOption() throws Exception {
-        parseMixinOption("mixin", true);
-        parseMixinOption("mix", true);
-        parseMixinOption("m", true);
-    }
-
-    @Test
-    public void shouldNotParseInvalidMixinOption() throws Exception {
-        parseMixinOption("mixinoption", false);
-        parseMixinOption("M", false);
-    }
-
-    // =============================================================================================================================
-    // Node Type Orderable Mixin Options Parsing Tests
-    // =============================================================================================================================
-
-    private void parseNodeTypeOrderableMixinOptions( String options,
-                                                     boolean success,
-                                                     int numChildren ) throws Exception {
-        CndParser parser = createParser(options);
-        RuleReturnScope scope = parser.ord_mix_opt();
-
-        if (success) {
-            assertThat("Value '" + options + "' did not parse and should have", scope.getTree(), notNullValue());
-
-            if (numChildren == 0) {
-                assertThat(options, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-            } else {
-                assertThat(numChildren, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getChildCount()));
-            }
-        } else {
-            assertThat("Value '" + options + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseNodeTypeOrderableMixinOptions() throws Exception {
-        parseNodeTypeOrderableMixinOptions("orderable", true, 0);
-        parseNodeTypeOrderableMixinOptions("orderable mixin", true, 2);
-        parseNodeTypeOrderableMixinOptions("orderable mix", true, 2);
-        parseNodeTypeOrderableMixinOptions("orderable m", true, 2);
-
-        parseNodeTypeOrderableMixinOptions("ord", true, 0);
-        parseNodeTypeOrderableMixinOptions("ord mixin", true, 2);
-        parseNodeTypeOrderableMixinOptions("ord mix", true, 2);
-        parseNodeTypeOrderableMixinOptions("ord m", true, 2);
-
-        parseNodeTypeOrderableMixinOptions("o", true, 0);
-        parseNodeTypeOrderableMixinOptions("o mixin", true, 2);
-        parseNodeTypeOrderableMixinOptions("o mix", true, 2);
-        parseNodeTypeOrderableMixinOptions("o m", true, 2);
-
-        parseNodeTypeOrderableMixinOptions("mixin", true, 0);
-        parseNodeTypeOrderableMixinOptions("mixin orderable", true, 2);
-        parseNodeTypeOrderableMixinOptions("mixin ord", true, 2);
-        parseNodeTypeOrderableMixinOptions("mixin o", true, 2);
-
-        parseNodeTypeOrderableMixinOptions("mix", true, 0);
-        parseNodeTypeOrderableMixinOptions("mix orderable", true, 2);
-        parseNodeTypeOrderableMixinOptions("mix ord", true, 2);
-        parseNodeTypeOrderableMixinOptions("mix o", true, 2);
-
-        parseNodeTypeOrderableMixinOptions("m", true, 0);
-        parseNodeTypeOrderableMixinOptions("m orderable", true, 2);
-        parseNodeTypeOrderableMixinOptions("m ord", true, 2);
-        parseNodeTypeOrderableMixinOptions("m o", true, 2);
-    }
-
-    // =============================================================================================================================
-    // Node Type Options Parsing Tests
-    // =============================================================================================================================
-
-    private void parseNodeTypeOptions( String options,
-                                       boolean success,
-                                       int numChildren ) throws Exception {
-        CndParser parser = createParser(options);
-        RuleReturnScope scope = parser.node_type_options();
-
-        if (success) {
-            assertThat("Value '" + options + "' did not parse and should have", scope.getTree(), notNullValue());
-
-            if (numChildren == 0) {
-                assertThat(options, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-            } else {
-                assertThat(numChildren, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getChildCount()));
-            }
-        } else {
-            assertThat("Value '" + options + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseNodeTypeOptions() throws Exception {
-        parseNodeTypeOptions("abstract", true, 0);
-        parseNodeTypeOptions("abs", true, 0);
-        parseNodeTypeOptions("a", true, 0);
-
-        parseNodeTypeOptions("abstract orderable", true, 2);
-        parseNodeTypeOptions("abstract ord", true, 2);
-        parseNodeTypeOptions("abstract o", true, 2);
-
-        parseNodeTypeOptions("abs orderable", true, 2);
-        parseNodeTypeOptions("abs ord", true, 2);
-        parseNodeTypeOptions("abs o", true, 2);
-
-        parseNodeTypeOptions("a orderable", true, 2);
-        parseNodeTypeOptions("a ord", true, 2);
-        parseNodeTypeOptions("a o", true, 2);
-
-        parseNodeTypeOptions("abstract mixin", true, 2);
-        parseNodeTypeOptions("abstract mix", true, 2);
-        parseNodeTypeOptions("abstract m", true, 2);
-
-        parseNodeTypeOptions("abs mixin", true, 2);
-        parseNodeTypeOptions("abs mix", true, 2);
-        parseNodeTypeOptions("abs m", true, 2);
-
-        parseNodeTypeOptions("a mixin", true, 2);
-        parseNodeTypeOptions("a mix", true, 2);
-        parseNodeTypeOptions("a m", true, 2);
-
-        parseNodeTypeOptions("abstract orderable mixin", true, 3);
-        parseNodeTypeOptions("abstract orderable mix", true, 3);
-        parseNodeTypeOptions("abstract orderable m", true, 3);
-
-        parseNodeTypeOptions("abstract ord mixin", true, 3);
-        parseNodeTypeOptions("abstract ord mix", true, 3);
-        parseNodeTypeOptions("abstract ord m", true, 3);
-
-        parseNodeTypeOptions("abstract o mixin", true, 3);
-        parseNodeTypeOptions("abstract o mix", true, 3);
-        parseNodeTypeOptions("abstract o m", true, 3);
-
-        parseNodeTypeOptions("abs orderable mixin", true, 3);
-        parseNodeTypeOptions("abs orderable mix", true, 3);
-        parseNodeTypeOptions("abs orderable m", true, 3);
-
-        parseNodeTypeOptions("abs ord mixin", true, 3);
-        parseNodeTypeOptions("abs ord mix", true, 3);
-        parseNodeTypeOptions("abs ord m", true, 3);
-
-        parseNodeTypeOptions("abs o mixin", true, 3);
-        parseNodeTypeOptions("abs o mix", true, 3);
-        parseNodeTypeOptions("abs o m", true, 3);
-
-        parseNodeTypeOptions("a orderable mixin", true, 3);
-        parseNodeTypeOptions("a orderable mix", true, 3);
-        parseNodeTypeOptions("a orderable m", true, 3);
-
-        parseNodeTypeOptions("a ord mixin", true, 3);
-        parseNodeTypeOptions("a ord mix", true, 3);
-        parseNodeTypeOptions("a ord m", true, 3);
-
-        parseNodeTypeOptions("a o mixin", true, 3);
-        parseNodeTypeOptions("a o mix", true, 3);
-        parseNodeTypeOptions("a o m", true, 3);
-
-        parseNodeTypeOptions("abstract mixin orderable", true, 3);
-        parseNodeTypeOptions("abstract mixin ord", true, 3);
-        parseNodeTypeOptions("abstract mixin o", true, 3);
-
-        parseNodeTypeOptions("abstract mix orderable", true, 3);
-        parseNodeTypeOptions("abstract mix ord", true, 3);
-        parseNodeTypeOptions("abstract mix o", true, 3);
-
-        parseNodeTypeOptions("abstract m orderable", true, 3);
-        parseNodeTypeOptions("abstract m ord", true, 3);
-        parseNodeTypeOptions("abstract m o", true, 3);
-
-        parseNodeTypeOptions("abs mixin orderable", true, 3);
-        parseNodeTypeOptions("abs mixin ord", true, 3);
-        parseNodeTypeOptions("abs mixin o", true, 3);
-
-        parseNodeTypeOptions("abs mix orderable", true, 3);
-        parseNodeTypeOptions("abs mix ord", true, 3);
-        parseNodeTypeOptions("abs mix o", true, 3);
-
-        parseNodeTypeOptions("abs m orderable", true, 3);
-        parseNodeTypeOptions("abs m ord", true, 3);
-        parseNodeTypeOptions("abs m o", true, 3);
-
-        parseNodeTypeOptions("a mixin orderable", true, 3);
-        parseNodeTypeOptions("a mixin ord", true, 3);
-        parseNodeTypeOptions("a mixin o", true, 3);
-
-        parseNodeTypeOptions("a mix orderable", true, 3);
-        parseNodeTypeOptions("a mix ord", true, 3);
-        parseNodeTypeOptions("a mix o", true, 3);
-
-        parseNodeTypeOptions("a m orderable", true, 3);
-        parseNodeTypeOptions("a m ord", true, 3);
-        parseNodeTypeOptions("a m o", true, 3);
-
-        parseNodeTypeOptions("orderable abstract", true, 2);
-        parseNodeTypeOptions("ord abstract", true, 2);
-        parseNodeTypeOptions("o abstract", true, 2);
-
-        parseNodeTypeOptions("orderable abs", true, 2);
-        parseNodeTypeOptions("ord abs", true, 2);
-        parseNodeTypeOptions("o abs", true, 2);
-
-        parseNodeTypeOptions("orderable a", true, 2);
-        parseNodeTypeOptions("ord a", true, 2);
-        parseNodeTypeOptions("o a", true, 2);
-
-        parseNodeTypeOptions("mixin abstract", true, 2);
-        parseNodeTypeOptions("mix abstract", true, 2);
-        parseNodeTypeOptions("m abstract", true, 2);
-
-        parseNodeTypeOptions("mixin abs", true, 2);
-        parseNodeTypeOptions("mix abs", true, 2);
-        parseNodeTypeOptions("m abs", true, 2);
-
-        parseNodeTypeOptions("mixin a", true, 2);
-        parseNodeTypeOptions("mix a", true, 2);
-        parseNodeTypeOptions("m a", true, 2);
-
-        parseNodeTypeOptions("orderable mixin abstract", true, 3);
-        parseNodeTypeOptions("orderable mix abstract", true, 3);
-        parseNodeTypeOptions("orderable m abstract", true, 3);
-
-        parseNodeTypeOptions("ord mixin abstract", true, 3);
-        parseNodeTypeOptions("ord mix abstract", true, 3);
-        parseNodeTypeOptions("ord m abstract", true, 3);
-
-        parseNodeTypeOptions("o mixin abstract", true, 3);
-        parseNodeTypeOptions("o mix abstract", true, 3);
-        parseNodeTypeOptions("o m abstract", true, 3);
-
-        parseNodeTypeOptions("orderable mixin abs", true, 3);
-        parseNodeTypeOptions("orderable mix abs", true, 3);
-        parseNodeTypeOptions("orderable m abs", true, 3);
-
-        parseNodeTypeOptions("ord mixin abs", true, 3);
-        parseNodeTypeOptions("ord mix abs", true, 3);
-        parseNodeTypeOptions("ord m abs", true, 3);
-
-        parseNodeTypeOptions("o mixin abs", true, 3);
-        parseNodeTypeOptions("o mix abs", true, 3);
-        parseNodeTypeOptions("o m abs", true, 3);
-
-        parseNodeTypeOptions("orderable mixin a", true, 3);
-        parseNodeTypeOptions("orderable mix a", true, 3);
-        parseNodeTypeOptions("orderable m a", true, 3);
-
-        parseNodeTypeOptions("ord mixin a", true, 3);
-        parseNodeTypeOptions("ord mix a", true, 3);
-        parseNodeTypeOptions("ord m a", true, 3);
-
-        parseNodeTypeOptions("o mixin a", true, 3);
-        parseNodeTypeOptions("o mix a", true, 3);
-        parseNodeTypeOptions("o m a", true, 3);
-
-        parseNodeTypeOptions("mixin orderable abstract", true, 3);
-        parseNodeTypeOptions("mixin ord abstract", true, 3);
-        parseNodeTypeOptions("mixin o abstract", true, 3);
-
-        parseNodeTypeOptions("mix orderable abstract", true, 3);
-        parseNodeTypeOptions("mix ord abstract", true, 3);
-        parseNodeTypeOptions("mix o abstract", true, 3);
-
-        parseNodeTypeOptions("m orderable abstract", true, 3);
-        parseNodeTypeOptions("m ord abstract", true, 3);
-        parseNodeTypeOptions("m o abstract", true, 3);
-
-        parseNodeTypeOptions("mixin orderable abs", true, 3);
-        parseNodeTypeOptions("mixin ord abs", true, 3);
-        parseNodeTypeOptions("mixin o abs", true, 3);
-
-        parseNodeTypeOptions("mix orderable abs", true, 3);
-        parseNodeTypeOptions("mix ord abs", true, 3);
-        parseNodeTypeOptions("mix o abs", true, 3);
-
-        parseNodeTypeOptions("m orderable abs", true, 3);
-        parseNodeTypeOptions("m ord abs", true, 3);
-        parseNodeTypeOptions("m o abs", true, 3);
-
-        parseNodeTypeOptions("mixin orderable a", true, 3);
-        parseNodeTypeOptions("mixin ord a", true, 3);
-        parseNodeTypeOptions("mixin o a", true, 3);
-
-        parseNodeTypeOptions("mix orderable a", true, 3);
-        parseNodeTypeOptions("mix ord a", true, 3);
-        parseNodeTypeOptions("mix o a", true, 3);
-
-        parseNodeTypeOptions("m orderable a", true, 3);
-        parseNodeTypeOptions("m ord a", true, 3);
-        parseNodeTypeOptions("m o a", true, 3);
-
-        parseNodeTypeOptions("orderable", true, 0);
-        parseNodeTypeOptions("ord", true, 0);
-        parseNodeTypeOptions("o", true, 0);
-
-        parseNodeTypeOptions("mixin", true, 0);
-        parseNodeTypeOptions("mix", true, 0);
-        parseNodeTypeOptions("m", true, 0);
-
-        parseNodeTypeOptions("orderable mixin", true, 2);
-        parseNodeTypeOptions("orderable mix", true, 2);
-        parseNodeTypeOptions("orderable m", true, 2);
-
-        parseNodeTypeOptions("ord mixin", true, 2);
-        parseNodeTypeOptions("ord mix", true, 2);
-        parseNodeTypeOptions("ord m", true, 2);
-
-        parseNodeTypeOptions("o mixin", true, 2);
-        parseNodeTypeOptions("o mix", true, 2);
-        parseNodeTypeOptions("o m", true, 2);
-
-        parseNodeTypeOptions("mixin orderable", true, 2);
-        parseNodeTypeOptions("mixin ord", true, 2);
-        parseNodeTypeOptions("mixin o", true, 2);
-
-        parseNodeTypeOptions("mix orderable", true, 2);
-        parseNodeTypeOptions("mix ord", true, 2);
-        parseNodeTypeOptions("mix o", true, 2);
-
-        parseNodeTypeOptions("m orderable", true, 2);
-        parseNodeTypeOptions("m ord", true, 2);
-        parseNodeTypeOptions("m o", true, 2);
-    }
-
-    // =============================================================================================================================
-    // Property Type Parsing Tests
-    // =============================================================================================================================
-
-    private void parsePropertyType( String propertyType,
-                                    boolean success ) throws Exception {
-        CndParser parser = createParser(propertyType);
-        RuleReturnScope scope = parser.property_type();
-
-        if (success) {
-            assertThat("Value '" + propertyType + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(propertyType, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + propertyType + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParseStringPropertyType() throws Exception {
-        parsePropertyType("STRING", true);
-        parsePropertyType("String", true);
-        parsePropertyType("string", true);
-    }
-
-    @Test
-    public void shouldParseBinaryPropertyType() throws Exception {
-        parsePropertyType("BINARY", true);
-        parsePropertyType("Binary", true);
-        parsePropertyType("binary", true);
-    }
-
-    @Test
-    public void shouldParseLongPropertyType() throws Exception {
-        parsePropertyType("LONG", true);
-        parsePropertyType("Long", true);
-        parsePropertyType("long", true);
-    }
-
-    @Test
-    public void shouldParseDoublePropertyType() throws Exception {
-        parsePropertyType("DOUBLE", true);
-        parsePropertyType("Double", true);
-        parsePropertyType("double", true);
-    }
-
-    @Test
-    public void shouldParseDecimalPropertyType() throws Exception {
-        parsePropertyType("DECIMAL", true);
-        parsePropertyType("Decimal", true);
-        parsePropertyType("decimal", true);
-    }
-
-    @Test
-    public void shouldParseBooleanPropertyType() throws Exception {
-        parsePropertyType("BOOLEAN", true);
-        parsePropertyType("Boolean", true);
-        parsePropertyType("boolean", true);
-    }
-
-    @Test
-    public void shouldParseDatePropertyType() throws Exception {
-        parsePropertyType("DATE", true);
-        parsePropertyType("Date", true);
-        parsePropertyType("date", true);
-    }
-
-    @Test
-    public void shouldParseNamePropertyType() throws Exception {
-        parsePropertyType("NAME", true);
-        parsePropertyType("Name", true);
-        parsePropertyType("name", true);
-    }
-
-    @Test
-    public void shouldParsePathPropertyType() throws Exception {
-        parsePropertyType("PATH", true);
-        parsePropertyType("Path", true);
-        parsePropertyType("path", true);
-    }
-
-    @Test
-    public void shouldParseReferencePropertyType() throws Exception {
-        parsePropertyType("REFERENCE", true);
-        parsePropertyType("Reference", true);
-        parsePropertyType("reference", true);
-    }
-
-    @Test
-    public void shouldParseWeakReferencePropertyType() throws Exception {
-        parsePropertyType("WEAKREFERENCE", true);
-        parsePropertyType("WeakReference", true);
-        parsePropertyType("weakreference", true);
-    }
-
-    @Test
-    public void shouldParseUriPropertyType() throws Exception {
-        parsePropertyType("URI", true);
-        parsePropertyType("Uri", true);
-        parsePropertyType("uri", true);
-    }
-
-    @Test
-    public void shouldParseUndefinedPropertyType() throws Exception {
-        parsePropertyType("UNDEFINED", true);
-        parsePropertyType("Undefined", true);
-        parsePropertyType("undefined", true);
-    }
-
-    @Test
-    public void shouldNotParseInvalidPropertyType() throws Exception {
-        parsePropertyType("B", false);
-        parsePropertyType("b", false);
-        parsePropertyType("binarytype", false);
-    }
-
-    // =============================================================================================================================
-    // Attributes Parsing Tests
-    // =============================================================================================================================
-
-    private void parseAttribute( String attribute,
-                                 boolean success ) throws Exception {
-        CndParser parser = createParser(attribute);
-        RuleReturnScope scope = parser.attributes();
-
-        if (success) {
-            assertThat("Value '" + attribute + "' did not parse and should have", scope.getTree(), notNullValue());
-            assertThat(attribute, is(((org.antlr.runtime.tree.CommonTree)scope.getTree()).getText()));
-        } else {
-            assertThat("Value '" + attribute + "' should not parse and did", scope.getTree(), nullValue());
-        }
-    }
-
-    @Test
-    public void shouldParsePrimaryAttribute() throws Exception {
-        parseAttribute("primary", true);
-        parseAttribute("pri", true);
-        parseAttribute("!", true);
-    }
-
-    @Test
-    public void shouldParseAutoCreatedAttribute() throws Exception {
-        parseAttribute("autocreated", true);
-        parseAttribute("aut", true);
-        parseAttribute("a", true);
-    }
-
-    @Test
-    public void shouldParseMandatoryAttribute() throws Exception {
-        parseAttribute("mandatory", true);
-        parseAttribute("man", true);
-        parseAttribute("m", true);
-    }
-
-    @Test
-    public void shouldParseProtectedAttribute() throws Exception {
-        parseAttribute("protected", true);
-        parseAttribute("pro", true);
-        parseAttribute("p", true);
-    }
-
-    @Test
-    public void shouldParseMultipleAttribute() throws Exception {
-        parseAttribute("multiple", true);
-        parseAttribute("mul", true);
-        parseAttribute("*", true);
-    }
-
-    @Test
-    public void shouldParseCopyAttribute() throws Exception {
-        parseAttribute("COPY", true);
-        parseAttribute("Copy", true);
-        parseAttribute("copy", true);
-    }
-
-    @Test
-    public void shouldParseVersionAttribute() throws Exception {
-        parseAttribute("VERSION", true);
-        parseAttribute("Version", true);
-        parseAttribute("version", true);
-    }
-
-    @Test
-    public void shouldParseInitializeAttribute() throws Exception {
-        parseAttribute("INITIALIZE", true);
-        parseAttribute("Initialize", true);
-        parseAttribute("initialize", true);
-    }
-
-    @Test
-    public void shouldParseComputeAttribute() throws Exception {
-        parseAttribute("COMPUTE", true);
-        parseAttribute("Compute", true);
-        parseAttribute("compute", true);
-    }
-
-    @Test
-    public void shouldParseIgnoreAttribute() throws Exception {
-        parseAttribute("IGNORE", true);
-        parseAttribute("Ignore", true);
-        parseAttribute("ignore", true);
-    }
-
-    @Test
-    public void shouldParseAbortAttribute() throws Exception {
-        parseAttribute("ABORT", true);
-        parseAttribute("Abort", true);
-        parseAttribute("abort", true);
-    }
-
-    @Test
-    public void shouldNotParseInvalidAttribute() throws Exception {
-        parsePropertyType("P", false);
-        parsePropertyType("A", false);
-        parsePropertyType("PRIMARY", false);
-    }
-}

Added: trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerI18nTest.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerI18nTest.java	                        (rev 0)
+++ trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerI18nTest.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -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.sequencer.cnd;
+
+import org.jboss.dna.common.AbstractI18nTest;
+
+/**
+ * @author John Verhaeg
+ */
+public class CndSequencerI18nTest extends AbstractI18nTest {
+
+    public CndSequencerI18nTest() {
+        super(CndSequencerI18n.class);
+    }
+}

Added: trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerTest.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerTest.java	                        (rev 0)
+++ trunk/extensions/dna-sequencer-cnd/src/test/java/org/jboss/dna/sequencer/cnd/CndSequencerTest.java	2009-04-20 20:21:21 UTC (rev 841)
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * 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.sequencer.cnd;
+
+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.io.InputStream;
+import java.net.URL;
+import org.jboss.dna.graph.sequencer.MockSequencerContext;
+import org.jboss.dna.graph.sequencer.MockSequencerOutput;
+import org.jboss.dna.graph.sequencer.SequencerContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class CndSequencerTest {
+    private CndSequencer sequencer;
+    private InputStream content;
+    private MockSequencerOutput output;
+    private URL cndEmpty;
+    private URL cndImages;
+    private URL cndMp3;
+    private URL cndBuiltIns;
+    private SequencerContext context;
+
+    @Before
+    public void beforeEach() {
+        sequencer = new CndSequencer();
+        context = new MockSequencerContext("/a/mySequencer.cnd");
+        context.getNamespaceRegistry().register("jcr", "http://www.jcp.org/jcr/1.0");
+        context.getNamespaceRegistry().register("nt", "http://www.jcp.org/jcr/nt/1.0");
+        context.getNamespaceRegistry().register("mix", "http://www.jcp.org/jcr/mix/1.0");
+        output = new MockSequencerOutput(context);
+        cndEmpty = this.getClass().getClassLoader().getResource("empty.cnd");
+        cndImages = this.getClass().getClassLoader().getResource("images.cnd");
+        cndMp3 = this.getClass().getClassLoader().getResource("mp3.cnd");
+        cndBuiltIns = this.getClass().getClassLoader().getResource("builtin_nodetypes.cnd");
+    }
+
+    @After
+    public void afterEach() throws Exception {
+        if (content != null) {
+            try {
+                content.close();
+            } finally {
+                content = null;
+            }
+        }
+    }
+
+    @Test
+    public void shouldGenerateNodeTypesForCndFileWithJSR170BuiltIns() throws IOException {
+        URL url = this.cndBuiltIns;
+        assertThat(url, is(notNullValue()));
+        content = url.openStream();
+        assertThat(content, is(notNullValue()));
+        sequencer.sequence(content, output, context);
+        // assertThat(output.getPropertyValues("image:metadata", "jcr:primaryType"), is(new Object[] {"image:metadata"}));
+        // assertThat(output.getPropertyValues("image:metadata", "jcr:mimeType"), is(new Object[] {"image/jpeg"}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:formatName"), is(new Object[] {"JPEG"}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:width"), is(new Object[] {48}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:height"), is(new Object[] {48}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:bitsPerPixel"), is(new Object[] {24}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:progressive"), is(new Object[] {false}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:numberOfImages"), is(new Object[] {1}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:physicalWidthDpi"), is(new Object[] {72}));
+        // assertThat(output.getPropertyValues("image:metadata", "image:physicalHeightDpi"), is(new Object[] {72}));
+        // assertThat(((Float)(output.getPropertyValues("image:metadata", "image:physicalWidthInches")[0])).doubleValue(),
+        // is(closeTo(0.666667d, 0.0001d)));
+        // assertThat(((Float)(output.getPropertyValues("image:metadata", "image:physicalHeightInches")[0])).doubleValue(),
+        // is(closeTo(0.666667d, 0.0001d)));
+    }
+
+    @Test
+    public void shouldGenerateNodeTypesForEmptyCndFile() throws IOException {
+        URL url = this.cndEmpty;
+        assertThat(url, is(notNullValue()));
+        content = url.openStream();
+        assertThat(content, is(notNullValue()));
+        sequencer.sequence(content, output, context);
+    }
+
+    @Test
+    public void shouldGenerateNodeTypesForCndFileWithImageNodeTypes() throws IOException {
+        URL url = this.cndImages;
+        assertThat(url, is(notNullValue()));
+        content = url.openStream();
+        assertThat(content, is(notNullValue()));
+        sequencer.sequence(content, output, context);
+    }
+
+    @Test
+    public void shouldGenerateNodeTypesForCndFileWithMp3NodeTypes() throws IOException {
+        URL url = this.cndMp3;
+        assertThat(url, is(notNullValue()));
+        content = url.openStream();
+        assertThat(content, is(notNullValue()));
+        sequencer.sequence(content, output, context);
+    }
+
+}

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-04-20 19:07:48 UTC (rev 840)
+++ trunk/pom.xml	2009-04-20 20:21:21 UTC (rev 841)
@@ -121,7 +121,7 @@
 		<module>extensions/dna-sequencer-mp3</module>
 		<module>extensions/dna-sequencer-jbpm-jpdl</module>
 		<!--module>extensions/dna-sequencer-esbMessage</module-->
-		<module>extensions/dna-sequencer-cnd</module>
+		<!--module>extensions/dna-sequencer-cnd</module-->
 		<module>extensions/dna-sequencer-java</module>
 		<module>extensions/dna-sequencer-msoffice</module>
 		<module>extensions/dna-sequencer-xml</module>
@@ -388,6 +388,11 @@
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-cnd</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-repository</artifactId>
 				<version>${pom.version}</version>
 			</dependency>



From dna-commits at lists.jboss.org  Wed Apr 22 04:49:23 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 22 Apr 2009 04:49:23 -0400
Subject: [dna-commits] DNA SVN: r842 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector and 12
	other directories.
Message-ID: <E1LwY8x-0004o4-Mo@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-22 04:49:23 -0400 (Wed, 22 Apr 2009)
New Revision: 842

Added:
   trunk/extensions/dna-connector-jbosscache/src/test/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorWritableTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.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/MoveBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
   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/test/AbstractConnectorTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
   trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheSource.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheWorkspaces.java
   trunk/extensions/dna-connector-jbosscache/src/main/resources/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.properties
   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/basic/BasicRequestProcessor.java
   trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java
Log:
DNA-375 Enhance MoveBranchRequest to take a new name, analogous to how CopyBranchRequest works

Changed the connector API's MoveBranchRequest to have a new desiredName() field, and changed the writable connectors to implement these new semantics.  Also expose this through the Graph API.  Added several tests to the connector unit test framework to test for these additional move behaviors.

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -436,10 +436,11 @@
         return new MoveAction<Conjunction<Graph>>(this.nextGraph, from) {
             @Override
             protected Conjunction<Graph> submit( Locations from,
-                                                 Location into ) {
+                                                 Location into,
+                                                 Name newName ) {
                 String workspaceName = getCurrentWorkspaceName();
                 do {
-                    requests.moveBranch(from.getLocation(), into, workspaceName);
+                    requests.moveBranch(from.getLocation(), into, workspaceName, newName);
                 } while ((from = from.next()) != null);
                 return and();
             }
@@ -1066,7 +1067,10 @@
             public SetValuesTo<Conjunction<Graph>> on( final Location location ) {
                 return new SetValuesTo<Conjunction<Graph>>() {
                     public Conjunction<Graph> to( Node value ) {
-                        return to(value.getLocation());
+                        Reference ref = (Reference)convertReferenceValue(value);
+                        Property property = getContext().getPropertyFactory().create(propertyName, ref);
+                        requests.setProperty(location, getCurrentWorkspaceName(), property);
+                        return nextGraph;
                     }
 
                     public Conjunction<Graph> to( Location value ) {
@@ -2023,10 +2027,11 @@
             return new MoveAction<BatchConjunction>(this.nextRequests, from) {
                 @Override
                 protected BatchConjunction submit( Locations from,
-                                                   Location into ) {
+                                                   Location into,
+                                                   Name newName ) {
                     String workspaceName = getCurrentWorkspaceName();
                     do {
-                        requestQueue.moveBranch(from.getLocation(), into, workspaceName);
+                        requestQueue.moveBranch(from.getLocation(), into, workspaceName, newName);
                     } while ((from = from.next()) != null);
                     return and();
                 }
@@ -3343,6 +3348,18 @@
             Node node = (Node)value;
             UUID uuid = node.getLocation().getUuid();
             if (uuid == null) {
+                // Look for a property ...
+                Property uuidProperty = node.getProperty(DnaLexicon.UUID);
+                if (uuidProperty != null) {
+                    uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
+                } else {
+                    uuidProperty = node.getProperty(JcrLexicon.UUID);
+                    if (uuidProperty != null) {
+                        uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
+                    }
+                }
+            }
+            if (uuid == null) {
                 String nodeString = node.getLocation().getString(getContext().getNamespaceRegistry());
                 String msg = GraphI18n.unableToCreateReferenceToNodeWithoutUuid.text(nodeString);
                 throw new IllegalArgumentException(msg);
@@ -3559,6 +3576,30 @@
     }
 
     /**
+     * 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> {
+        /**
+         * Finish the request by specifying the new name.
+         * 
+         * @param newName the new name
+         * @return the interface for additional requests or actions
+         */
+        Next as( String newName );
+
+        /**
+         * Finish the request by specifying the new name.
+         * 
+         * @param newName the new name
+         * @return the interface for additional requests or actions
+         */
+        Next as( Name newName );
+    }
+
+    /**
      * 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
@@ -3632,7 +3673,7 @@
      * @param <Next> The interface that is to be returned when this request is completed
      * @author Randall Hauch
      */
-    public interface Move<Next> extends Into<Next>, And<Move<Next>> {
+    public interface Move<Next> extends AsName<Into<Next>>, Into<Next>, And<Move<Next>> {
     }
 
     /**
@@ -5190,11 +5231,16 @@
         /*package*/Path createPath( String path ) {
             return Graph.this.getContext().getValueFactories().getPathFactory().create(path);
         }
+
+        /*package*/Name createName( String name ) {
+            return Graph.this.getContext().getValueFactories().getNameFactory().create(name);
+        }
     }
 
     @NotThreadSafe
     protected abstract class MoveAction<T> extends AbstractAction<T> implements Move<T> {
         private final Locations from;
+        private Name newName;
 
         /*package*/MoveAction( T afterConjunction,
                                 Location from ) {
@@ -5238,39 +5284,55 @@
             return this;
         }
 
+        public Into<T> as( Name newName ) {
+            this.newName = newName;
+            return this;
+        }
+
         /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.Graph.AsName#as(java.lang.String)
+         */
+        public Into<T> as( String newName ) {
+            return as(createName(newName));
+        }
+
+        /**
          * Submit any requests to move the targets into the supplied parent location
          * 
          * @param from the location(s) that are being moved; never null
          * @param into the parent location
+         * @param newName the new name for the node being moved; may be null
          * @return this object, for method chaining
          */
         protected abstract T submit( Locations from,
-                                     Location into );
+                                     Location into,
+                                     Name newName );
 
         public T into( Location into ) {
-            return submit(from, into);
+            return submit(from, into, newName);
         }
 
         public T into( Path into ) {
-            return submit(from, Location.create(into));
+            return submit(from, Location.create(into), newName);
         }
 
         public T into( UUID into ) {
-            return submit(from, Location.create(into));
+            return submit(from, Location.create(into), newName);
         }
 
         public T into( Property firstIdProperty,
                        Property... additionalIdProperties ) {
-            return submit(from, Location.create(firstIdProperty, additionalIdProperties));
+            return submit(from, Location.create(firstIdProperty, additionalIdProperties), newName);
         }
 
         public T into( Property into ) {
-            return submit(from, Location.create(into));
+            return submit(from, Location.create(into), newName);
         }
 
         public T into( String into ) {
-            return submit(from, Location.create(createPath(into)));
+            return submit(from, Location.create(createPath(into)), newName);
         }
     }
 

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -55,32 +55,41 @@
      */
     public static final boolean DEFAULT_SUPPORT_CREATING_WORKSPACES = false;
 
+    /**
+     * The default support for creating workspaces is {@value} .
+     */
+    public static final boolean DEFAULT_SUPPORT_REFERENCES = true;
+
     private boolean sameNameSiblings;
     private boolean updates;
     private boolean events;
     private boolean creatingWorkspaces;
+    private boolean references;
 
     /**
      * 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_CREATING_WORKSPACES, DEFAULT_SUPPORT_REFERENCES);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
                                          boolean supportsUpdates ) {
-        this(supportsSameNameSiblings, supportsUpdates, DEFAULT_SUPPORT_EVENTS, DEFAULT_SUPPORT_CREATING_WORKSPACES);
+        this(supportsSameNameSiblings, supportsUpdates, DEFAULT_SUPPORT_EVENTS, DEFAULT_SUPPORT_CREATING_WORKSPACES,
+             DEFAULT_SUPPORT_REFERENCES);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
                                          boolean supportsUpdates,
                                          boolean supportsEvents,
-                                         boolean supportsCreatingWorkspaces ) {
+                                         boolean supportsCreatingWorkspaces,
+                                         boolean supportsReferences ) {
         this.sameNameSiblings = supportsSameNameSiblings;
         this.updates = supportsUpdates;
         this.events = supportsEvents;
         this.creatingWorkspaces = supportsCreatingWorkspaces;
+        this.references = supportsReferences;
     }
 
     /**
@@ -104,6 +113,15 @@
     }
 
     /**
+     * Return whether the source supports references by identifiers.
+     * 
+     * @return true if references are supported, or false otherwise
+     */
+    public boolean supportsReferences() {
+        return references;
+    }
+
+    /**
      * Return whether the source supports events through {@link RepositorySourceListener}s.
      * 
      * @return true if events are supported, or false if the source is not capable of generating events

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -342,11 +342,13 @@
          * 
          * @param context
          * @param node the node to be moved; may not be the {@link Workspace#getRoot() root}
+         * @param desiredNewName the new name for the node, if it is to be changed; may be null
          * @param newWorkspace the workspace containing the new parent node
          * @param newParent the new parent; may not be the {@link Workspace#getRoot() root}
          */
         public void moveNode( ExecutionContext context,
                               InMemoryNode node,
+                              Name desiredNewName,
                               Workspace newWorkspace,
                               InMemoryNode newParent ) {
             assert context != null;
@@ -355,16 +357,22 @@
             assert newWorkspace.getRoot().equals(newParent) != true;
             assert this.getRoot().equals(node) != true;
             InMemoryNode oldParent = node.getParent();
+            Name oldName = node.getName().getName();
             if (oldParent != null) {
                 if (oldParent.equals(newParent)) return;
                 boolean removed = oldParent.getChildren().remove(node);
                 assert removed == true;
                 node.setParent(null);
-                correctSameNameSiblingIndexes(context, oldParent, node.getName().getName());
+                correctSameNameSiblingIndexes(context, oldParent, oldName);
             }
             node.setParent(newParent);
+            Name newName = oldName;
+            if (desiredNewName != null) {
+                newName = desiredNewName;
+                node.setName(context.getValueFactories().getPathFactory().createSegment(desiredNewName, 1));
+            }
             newParent.getChildren().add(node);
-            correctSameNameSiblingIndexes(context, newParent, node.getName().getName());
+            correctSameNameSiblingIndexes(context, newParent, newName);
 
             // If the node was moved to a new workspace...
             if (!this.equals(newWorkspace)) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -71,7 +71,8 @@
      */
     public static final int DEFAULT_RETRY_LIMIT = 0;
 
-    protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true, false, true);
+    protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true, false, true,
+                                                                                                        true);
 
     protected static final String ROOT_NODE_UUID = "rootNodeUuid";
     protected static final String SOURCE_NAME = "sourceName";

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -192,11 +192,11 @@
         InMemoryNode node = getTargetNode(workspace, request, request.from());
         if (node == null) return;
         // Look up the new parent, which must exist ...
-        Path newPath = request.into().getPath();
-        Path newParentPath = newPath.getParent();
+        Path newParentPath = request.into().getPath();
         InMemoryNode newParent = workspace.getNode(newParentPath);
-        node.setParent(newParent);
-        newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName());
+        workspace.moveNode(getExecutionContext(), node, request.desiredName(), workspace, newParent);
+        assert node.getParent() == newParent;
+        Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName().getName());
         Location oldLocation = getActualLocation(request.from().getPath(), node);
         Location newLocation = Location.create(newPath, node.getUuid());
         request.setActualLocations(oldLocation, newLocation);

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -619,6 +619,23 @@
      * @param from the location of the top node in the existing branch that is to be moved
      * @param into the location of the existing node into which the branch should be moved
      * @param workspaceName the name of the workspace
+     * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
+     * @return this builder for method chaining; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public BatchRequestBuilder moveBranch( Location from,
+                                           Location into,
+                                           String workspaceName,
+                                           Name newNameForNode ) {
+        return add(new MoveBranchRequest(from, into, workspaceName, newNameForNode, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
      * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
      *        location
      * @return this builder for method chaining; never null

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -27,6 +27,7 @@
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 
 /**
@@ -43,6 +44,7 @@
     private final Location from;
     private final Location into;
     private final String workspaceName;
+    private final Name desiredNameForNode;
     private final NodeConflictBehavior conflictBehavior;
     private Location actualOldLocation;
     private Location actualNewLocation;
@@ -58,7 +60,7 @@
     public MoveBranchRequest( Location from,
                               Location into,
                               String workspaceName ) {
-        this(from, into, workspaceName, DEFAULT_CONFLICT_BEHAVIOR);
+        this(from, into, workspaceName, null, DEFAULT_CONFLICT_BEHAVIOR);
     }
 
     /**
@@ -67,6 +69,22 @@
      * @param from the location of the top node in the existing branch that is to be moved
      * @param into the location of the existing node into which the branch should be moved
      * @param workspaceName the name of the workspace
+     * @param newNameForMovedNode the new name for the node being moved, or null if the name of the original should be used
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest( Location from,
+                              Location into,
+                              String workspaceName,
+                              Name newNameForMovedNode ) {
+        this(from, into, workspaceName, newNameForMovedNode, DEFAULT_CONFLICT_BEHAVIOR);
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
      * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
      *        location
      * @throws IllegalArgumentException if any of the parameters are null
@@ -75,6 +93,25 @@
                               Location into,
                               String workspaceName,
                               NodeConflictBehavior conflictBehavior ) {
+        this(from, into, workspaceName, null, conflictBehavior);
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @param newNameForMovedNode the new name for the node being moved, or null if the name of the original should be used
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest( Location from,
+                              Location into,
+                              String workspaceName,
+                              Name newNameForMovedNode,
+                              NodeConflictBehavior conflictBehavior ) {
         CheckArg.isNotNull(from, "from");
         CheckArg.isNotNull(into, "into");
         CheckArg.isNotNull(workspaceName, "workspaceName");
@@ -82,6 +119,7 @@
         this.from = from;
         this.into = into;
         this.workspaceName = workspaceName;
+        this.desiredNameForNode = newNameForMovedNode;
         this.conflictBehavior = conflictBehavior;
     }
 
@@ -113,6 +151,15 @@
     }
 
     /**
+     * Get the name of the copy if it is to be different than that of the original.
+     * 
+     * @return the desired name of the copy, or null if the name of the original is to be used
+     */
+    public Name desiredName() {
+        return desiredNameForNode;
+    }
+
+    /**
      * Get the expected behavior when copying the branch and the {@link #into() destination} already has a node with the same
      * name.
      * 
@@ -150,7 +197,9 @@
      */
     public boolean hasNoEffect() {
         if (into.hasPath() && into.hasIdProperties() == false && from.hasPath()) {
-            return from.getPath().getParent().equals(into.getPath());
+            if (!from.getPath().getParent().equals(into.getPath())) return false;
+            if (desiredName() != null && !desiredName().equals(from.getPath().getLastSegment().getName())) return false;
+            return true;
         }
         // Can't be determined for certain
         return false;
@@ -160,8 +209,8 @@
      * Sets the actual and complete location of the node being renamed and its new location. This method must be called when
      * processing the request, and the actual location must have a {@link Location#getPath() path}.
      * 
-     * @param oldLocation the actual location of the node before being renamed
-     * @param newLocation the actual location of the new copy of the node
+     * @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
@@ -169,20 +218,25 @@
      */
     public void setActualLocations( Location oldLocation,
                                     Location newLocation ) {
+        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 (!into.isSame(newLocation, false)) { // not same if actual is null
-            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
-        }
-        assert oldLocation != null;
-        assert newLocation != null;
         if (!oldLocation.hasPath()) {
             throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(oldLocation));
         }
         if (!newLocation.hasPath()) {
             throw new IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(newLocation));
         }
+        if (into().hasPath() && !newLocation.getPath().getParent().isSameAs(into.getPath())) {
+            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.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));
+        }
         this.actualNewLocation = newLocation;
     }
 
@@ -249,6 +303,10 @@
      */
     @Override
     public String toString() {
-        return "move branch " + from() + " in the \"" + workspaceName + "\" workspace into " + into();
+        if (desiredName() != null) {
+            return "move branch " + from() + " in the \"" + inWorkspace() + "\" workspace into " + into() + " with name "
+                   + desiredName();
+        }
+        return "move branch " + from() + " in the \"" + inWorkspace() + "\" workspace into " + into();
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -466,6 +466,23 @@
      * @param from the location of the top node in the existing branch that is to be moved
      * @param into the location of the existing node into which the branch should be moved
      * @param workspaceName the name of the workspace
+     * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest moveBranch( Location from,
+                                         Location into,
+                                         String workspaceName,
+                                         Name newNameForNode ) {
+        return process(new MoveBranchRequest(from, into, workspaceName, newNameForNode, MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR));
+    }
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @param newNameForNode the new name for the node being moved, or null if the name of the original should be used
      * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
      *        location
      * @return the request; never null
@@ -474,8 +491,28 @@
     public MoveBranchRequest moveBranch( Location from,
                                          Location into,
                                          String workspaceName,
+                                         Name newNameForNode,
                                          NodeConflictBehavior conflictBehavior ) {
         if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
+        return process(new MoveBranchRequest(from, into, workspaceName, newNameForNode, conflictBehavior));
+    }
+
+    /**
+     * Create a request to move a branch from one location into another.
+     * 
+     * @param from the location of the top node in the existing branch that is to be moved
+     * @param into the location of the existing node into which the branch should be moved
+     * @param workspaceName the name of the workspace
+     * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+     *        location
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public MoveBranchRequest moveBranch( Location from,
+                                         Location into,
+                                         String workspaceName,
+                                         NodeConflictBehavior conflictBehavior ) {
+        if (conflictBehavior == null) conflictBehavior = MoveBranchRequest.DEFAULT_CONFLICT_BEHAVIOR;
         return process(new MoveBranchRequest(from, into, workspaceName, conflictBehavior));
     }
 

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -93,6 +93,8 @@
     private ExecutionContext context;
     private Path validPath;
     private String validPathString;
+    private Name validName;
+    private String validNameString;
     private UUID validUuid;
     private Property validIdProperty1;
     private Property validIdProperty2;
@@ -120,6 +122,8 @@
         validPathString = "/a/b/c";
         validUuid = UUID.randomUUID();
         validPath = createPath(validPathString);
+        validNameString = "theName";
+        validName = createName(validNameString);
         Name idProperty1Name = createName("id1");
         Name idProperty2Name = createName("id2");
         validIdProperty1 = context.getPropertyFactory().create(idProperty1Name, "1");
@@ -978,6 +982,15 @@
     }
 
     @Test
+    public void shouldMoveAndRenameNodesThroughMultipleMoveRequests() {
+        graph.move(validPath).as(validName).into(validIdProperty1, validIdProperty2).and().move(validUuid).into(validPathString);
+        assertThat(numberOfExecutions, is(2));
+        assertNextRequestIsMove(Location.create(validPath), Location.create(validIdProperty1, validIdProperty2));
+        assertNextRequestIsMove(Location.create(validUuid), Location.create(createPath(validPathString)));
+        assertNoMoreRequests();
+    }
+
+    @Test
     public void shouldIgnoreIncompleteRequests() {
         graph.move(validPath); // missing 'into(...)'
         assertNoMoreRequests();
@@ -1081,8 +1094,33 @@
 
         @Override
         public void process( MoveBranchRequest request ) {
-            // Just update the actual location
-            request.setActualLocations(actualLocationOf(request.from()), actualLocationOf(request.into()));
+            // Just update the actual location ...
+            Name newName = request.desiredName();
+            if (newName == null && request.from().hasPath()) newName = request.from().getPath().getLastSegment().getName();
+            if (newName == null) newName = context.getValueFactories().getNameFactory().create("d");
+            // Figure out the new name and path (if needed)...
+            Path newPath = null;
+            if (request.into().hasPath()) {
+                newPath = context.getValueFactories().getPathFactory().create(request.into().getPath(), newName);
+            } else if (request.from().hasPath()) {
+                newPath = context.getValueFactories().getPathFactory().create("/a/b/c");
+                newPath = context.getValueFactories().getPathFactory().create(newPath, newName);
+            } else {
+                newPath = context.getValueFactories().getPathFactory().create("/a/b/c");
+                newPath = context.getValueFactories().getPathFactory().create(newPath, newName);
+            }
+            // Figure out the old name and path ...
+            Path oldPath = null;
+            if (request.from().hasPath()) {
+                oldPath = request.from().getPath();
+            } else {
+                oldPath = context.getValueFactories().getPathFactory().create("/x/y/z");
+                oldPath = context.getValueFactories().getPathFactory().create(oldPath, newName);
+            }
+            Location fromLocation = request.from().hasIdProperties() ? Location.create(oldPath, request.from().getIdProperties()) : Location.create(oldPath);
+            Location intoLocation = request.into().hasIdProperties() ? Location.create(newPath, request.into().getIdProperties()) : Location.create(newPath);
+
+            request.setActualLocations(fromLocation, intoLocation);
         }
 
         @Override

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -239,7 +239,7 @@
         assertThat(workspace.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
         assertThat(workspace.getNode(pathFactory.create("/d/b")), is(sameInstance(node_b2)));
 
-        workspace.moveNode(context, node_b, workspace, node_d);
+        workspace.moveNode(context, node_b, null, workspace, node_d);
 
         assertThat(workspace.getNode(pathFactory.create("/")), is(sameInstance(workspace.getRoot())));
         assertThat(workspace.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
@@ -249,7 +249,7 @@
         assertThat(workspace.getNode(pathFactory.create("/d/b[2]")), is(sameInstance(node_b)));
         assertThat(workspace.getNode(pathFactory.create("/d/b[2]/c")), is(sameInstance(node_c)));
 
-        workspace.moveNode(context, node_b, workspace, node_e);
+        workspace.moveNode(context, node_b, null, workspace, node_e);
 
         assertThat(workspace.getNode(pathFactory.create("/")), is(sameInstance(workspace.getRoot())));
         assertThat(workspace.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
@@ -302,7 +302,7 @@
         assertThat(new_workspace.getNode(pathFactory.create("/d/b")), is(sameInstance(new_node_b2)));
 
         // Move 'workspace::/a/b' into 'newWorkspace::/d'
-        workspace.moveNode(context, node_b, new_workspace, new_node_d);
+        workspace.moveNode(context, node_b, null, new_workspace, new_node_d);
 
         assertThat(workspace.getNodesByUuid().size(), is(5));
         assertThat(workspace.getNode(pathFactory.create("/")), is(sameInstance(workspace.getRoot())));
@@ -579,7 +579,9 @@
 
     @Test
     public void shouldCreateRepositoryStructure() {
-        workspace.createNode(context, "/a").setProperty(context, "name", "value").setProperty(context, "desc", "Some description");
+        workspace.createNode(context, "/a")
+                 .setProperty(context, "name", "value")
+                 .setProperty(context, "desc", "Some description");
         workspace.createNode(context, "/a/b").setProperty(context, "name", "value2").setProperty(context,
                                                                                                  "desc",
                                                                                                  "Some description 2");

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -38,8 +38,10 @@
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.Subgraph;
@@ -84,6 +86,7 @@
     private static List<RepositoryConnection> openConnections;
     private static boolean running;
     private static Location rootLocation;
+    private static UUID rootUuid;
 
     public void startRepository() throws Exception {
         if (!running) {
@@ -141,6 +144,7 @@
             } finally {
                 running = false;
                 rootLocation = null;
+                rootUuid = null;
             }
         }
     }
@@ -292,11 +296,24 @@
     }
 
     protected UUID getRootNodeUuid() {
-        if (rootLocation == null) {
+        if (rootUuid == null) {
             Node root = graph.getNodeAt("/");
             rootLocation = root.getLocation();
+            rootUuid = rootLocation.getUuid();
+            if (rootUuid == null) {
+                Property uuid = root.getProperty(DnaLexicon.UUID);
+                if (uuid != null) {
+                    rootUuid = context.getValueFactories().getUuidFactory().create(uuid.getFirstValue());
+                }
+            }
+            if (rootUuid == null) {
+                Property uuid = root.getProperty(JcrLexicon.UUID);
+                if (uuid != null) {
+                    rootUuid = context.getValueFactories().getUuidFactory().create(uuid.getFirstValue());
+                }
+            }
         }
-        return rootLocation.getUuid();
+        return rootUuid;
     }
 
     protected String string( Object value ) {

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -105,9 +105,19 @@
 
     @Test
     public void shouldAddChildrenAndSettingProperties() {
-        graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB", "valueB").and("propC", "valueC").and().create("/b").with("propD",
-                                                                                                                                                  "valueD").and("propE",
-                                                                                                                                                                "valueE").execute();
+        graph.batch()
+             .set("propA")
+             .to("valueA")
+             .on("/")
+             .and()
+             .create("/a")
+             .with("propB", "valueB")
+             .and("propC", "valueC")
+             .and()
+             .create("/b")
+             .with("propD", "valueD")
+             .and("propE", "valueE")
+             .execute();
         // Now look up the root node ...
         Node root = graph.getNodeAt("/");
         assertThat(root, is(notNullValue()));
@@ -608,50 +618,54 @@
         boolean batch = true;
         createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
 
-        // Create some references between nodes that aren't involved with the copy ...
-        graph.set("refProp").on("/node1").to(graph.getNodeAt("/node1/node3"));
-        graph.set("refProp").on("/node1/node1").to(graph.getNodeAt("/node3/node2")); // will soon be /node3/node2[1]
+        if (source.getCapabilities().supportsReferences()) {
+            // Create some references between nodes that aren't involved with the copy ...
+            graph.set("refProp").on("/node1").to(graph.getNodeAt("/node1/node3"));
+            graph.set("refProp").on("/node1/node1").to(graph.getNodeAt("/node3/node2")); // will soon be /node3/node2[1]
 
-        // Create some "inward" references from nodes that are NOT being copied to nodes that are being copied ...
-        graph.set("refProp").on("/node1/node2").to(graph.getNodeAt("/node2/node2"));
-        graph.set("refProp").on("/node1/node3").to(graph.getNodeAt("/node2/node2"));
+            // Create some "inward" references from nodes that are NOT being copied to nodes that are being copied ...
+            graph.set("refProp").on("/node1/node2").to(graph.getNodeAt("/node2/node2"));
+            graph.set("refProp").on("/node1/node3").to(graph.getNodeAt("/node2/node2"));
 
-        // Create some "outward" references from nodes that are being copied to nodes that are NOT being copied ...
-        graph.set("refProp").on("/node2/node1").to(graph.getNodeAt("/node1/node1"));
-        graph.set("refProp").on("/node2/node3").to(graph.getNodeAt("/node1/node2"));
+            // Create some "outward" references from nodes that are being copied to nodes that are NOT being copied ...
+            graph.set("refProp").on("/node2/node1").to(graph.getNodeAt("/node1/node1"));
+            graph.set("refProp").on("/node2/node3").to(graph.getNodeAt("/node1/node2"));
 
-        // Create some "internal" references between nodes that are being copied ...
-        graph.set("refProp").on("/node2/node2").to(graph.getNodeAt("/node2/node2/node1"));
-        graph.set("refProp").on("/node2/node3/node1").to(graph.getNodeAt("/node2/node2/node1"));
+            // Create some "internal" references between nodes that are being copied ...
+            graph.set("refProp").on("/node2/node2").to(graph.getNodeAt("/node2/node2/node1"));
+            graph.set("refProp").on("/node2/node3/node1").to(graph.getNodeAt("/node2/node2/node1"));
 
-        // Verify the references are there ...
-        assertReference("/node1", "refProp", "/node1/node3");
-        assertReference("/node1/node1", "refProp", "/node3/node2");
-        assertReference("/node1/node2", "refProp", "/node2/node2");
-        assertReference("/node1/node3", "refProp", "/node2/node2");
-        assertReference("/node2/node1", "refProp", "/node1/node1");
-        assertReference("/node2/node3", "refProp", "/node1/node2");
-        assertReference("/node2/node2", "refProp", "/node2/node2/node1");
-        assertReference("/node2/node3/node1", "refProp", "/node2/node2/node1");
+            // Verify the references are there ...
+            assertReference("/node1", "refProp", "/node1/node3");
+            assertReference("/node1/node1", "refProp", "/node3/node2");
+            assertReference("/node1/node2", "refProp", "/node2/node2");
+            assertReference("/node1/node3", "refProp", "/node2/node2");
+            assertReference("/node2/node1", "refProp", "/node1/node1");
+            assertReference("/node2/node3", "refProp", "/node1/node2");
+            assertReference("/node2/node2", "refProp", "/node2/node2/node1");
+            assertReference("/node2/node3/node1", "refProp", "/node2/node2/node1");
+        }
 
         // Copy a branches ...
         graph.copy("/node2").into("/node3");
 
-        // Verify the references are still there ...
-        assertReference("/node1", "refProp", "/node1/node3");
-        assertReference("/node1/node1", "refProp", "/node3/node2[1]");
-        assertReference("/node1/node2", "refProp", "/node2/node2");
-        assertReference("/node1/node3", "refProp", "/node2/node2");
-        assertReference("/node2/node1", "refProp", "/node1/node1");
-        assertReference("/node2/node3", "refProp", "/node1/node2");
-        assertReference("/node2/node2", "refProp", "/node2/node2/node1");
-        assertReference("/node2/node3/node1", "refProp", "/node2/node2/node1");
+        if (source.getCapabilities().supportsReferences()) {
+            // Verify the references are still there ...
+            assertReference("/node1", "refProp", "/node1/node3");
+            assertReference("/node1/node1", "refProp", "/node3/node2[1]");
+            assertReference("/node1/node2", "refProp", "/node2/node2");
+            assertReference("/node1/node3", "refProp", "/node2/node2");
+            assertReference("/node2/node1", "refProp", "/node1/node1");
+            assertReference("/node2/node3", "refProp", "/node1/node2");
+            assertReference("/node2/node2", "refProp", "/node2/node2/node1");
+            assertReference("/node2/node3/node1", "refProp", "/node2/node2/node1");
 
-        // And verify that we have a few new (outward and internal) references in the copy ...
-        assertReference("/node3/node2[2]/node1", "refProp", "/node1/node1"); // outward
-        assertReference("/node3/node2[2]/node3", "refProp", "/node1/node2"); // outward
-        assertReference("/node3/node2[2]/node2", "refProp", "/node3/node2[2]/node2/node1"); // internal
-        assertReference("/node3/node2[2]/node3/node1", "refProp", "/node3/node2[2]/node2/node1"); // internal
+            // And verify that we have a few new (outward and internal) references in the copy ...
+            assertReference("/node3/node2[2]/node1", "refProp", "/node1/node1"); // outward
+            assertReference("/node3/node2[2]/node3", "refProp", "/node1/node2"); // outward
+            assertReference("/node3/node2[2]/node2", "refProp", "/node3/node2[2]/node2/node1"); // internal
+            assertReference("/node3/node2[2]/node3/node1", "refProp", "/node3/node2[2]/node2/node1"); // internal
+        }
 
         // Now assert the structure ...
         assertThat(graph.getChildren().of("/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
@@ -730,9 +744,11 @@
                                                                          "The quick brown fox jumped over the moon. What? "));
         assertThat(subgraph.getNode("node2[2]/node1/node3"), hasProperty("property3",
                                                                          "The quick brown fox jumped over the moon. What? "));
-        assertThat(subgraph.getNode("node2[2]/node2").getChildren(), hasChildren(segment("node1"),
-                                                                                 segment("node2"),
-                                                                                 segment("node3")));
+        if (source.getCapabilities().supportsReferences()) {
+            assertThat(subgraph.getNode("node2[2]/node2").getChildren(), hasChildren(segment("node1"),
+                                                                                     segment("node2"),
+                                                                                     segment("node3")));
+        }
         assertThat(subgraph.getNode("node2[2]/node2"), hasProperty("property1",
                                                                    "The quick brown fox jumped over the moon. What? "));
         assertThat(subgraph.getNode("node2[2]/node2"), hasProperty("property2",
@@ -760,9 +776,11 @@
                                                                          "The quick brown fox jumped over the moon. What? "));
         assertThat(subgraph.getNode("node2[2]/node2/node3"), hasProperty("property3",
                                                                          "The quick brown fox jumped over the moon. What? "));
-        assertThat(subgraph.getNode("node2[2]/node3").getChildren(), hasChildren(segment("node1"),
-                                                                                 segment("node2"),
-                                                                                 segment("node3")));
+        if (source.getCapabilities().supportsReferences()) {
+            assertThat(subgraph.getNode("node2[2]/node3").getChildren(), hasChildren(segment("node1"),
+                                                                                     segment("node2"),
+                                                                                     segment("node3")));
+        }
         assertThat(subgraph.getNode("node2[2]/node3"), hasProperty("property1",
                                                                    "The quick brown fox jumped over the moon. What? "));
         assertThat(subgraph.getNode("node2[2]/node3"), hasProperty("property2",
@@ -805,4 +823,567 @@
         }
     }
 
+    @Test
+    public void shouldMoveNodes() {
+        // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+        // /
+        // /node1
+        // /node1/node1
+        // /node1/node1/node1
+        // /node1/node1/node2
+        // /node1/node1/node3
+        // /node1/node2
+        // /node1/node2/node1
+        // /node1/node2/node2
+        // /node1/node2/node3
+        // /node1/node3
+        // /node1/node3/node1
+        // /node1/node3/node2
+        // /node1/node3/node3
+        // /node2
+        // /node2/node1
+        // /node2/node1/node1
+        // /node2/node1/node2
+        // /node2/node1/node3
+        // /node2/node2
+        // /node2/node2/node1
+        // /node2/node2/node2
+        // /node2/node2/node3
+        // /node2/node3
+        // /node2/node3/node1
+        // /node2/node3/node2
+        // /node2/node3/node3
+        // /node3
+        // /node3/node1
+        // /node3/node1/node1
+        // /node3/node1/node2
+        // /node3/node1/node3
+        // /node3/node2
+        // /node3/node2/node1
+        // /node3/node2/node2
+        // /node3/node2/node3
+        // /node3/node3
+        // /node3/node3/node1
+        // /node3/node3/node2
+        // /node3/node3/node3
+        // /secondBranch1
+        // /secondBranch1/secondBranch1
+        // /secondBranch1/secondBranch2
+        // /secondBranch2
+        // /secondBranch2/secondBranch1
+        // /secondBranch2/secondBranch2
+
+        String initialPath = "";
+        int depth = 3;
+        int numChildrenPerNode = 3;
+        int numPropertiesPerNode = 3;
+        Stopwatch sw = new Stopwatch();
+        boolean batch = true;
+        createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
+
+        // Delete two branches ...
+        graph.move("/node2").into("/node3");
+
+        // Now assert the structure ...
+        assertThat(graph.getChildren().of("/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3/node1"), hasChildren());
+
+        assertThat(graph.getChildren().of("/node3"), hasChildren(segment("node1"),
+                                                                 segment("node2"),
+                                                                 segment("node3"),
+                                                                 segment("node2[2]")));
+        assertThat(graph.getChildren().of("/node3/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3/node1"), hasChildren());
+        assertThat(graph.getChildren().of("/node3/node2[2]"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node2[2]/node1"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node2[2]/node2"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node2[2]/node3"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node2[2]/node1/node1"), hasChildren());
+
+        Subgraph subgraph = graph.getSubgraphOfDepth(4).at("/node3");
+        assertThat(subgraph, is(notNullValue()));
+        assertThat(subgraph.getNode(".").getChildren(), hasChildren(segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("."), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("node2[2]"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1").getChildren(), hasChildren(segment("node1"),
+                                                                                 segment("node2"),
+                                                                                 segment("node3")));
+        assertThat(subgraph.getNode("node2[2]/node1"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node1/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node1/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node1/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node1/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node2/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node2/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node2/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node2/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node3/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node3/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node2[2]/node3/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[2]/node3/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+    }
+
+    @Test
+    public void shouldMoveAndRenameNodes() {
+        // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+        // /
+        // /node1
+        // /node1/node1
+        // /node1/node1/node1
+        // /node1/node1/node2
+        // /node1/node1/node3
+        // /node1/node2
+        // /node1/node2/node1
+        // /node1/node2/node2
+        // /node1/node2/node3
+        // /node1/node3
+        // /node1/node3/node1
+        // /node1/node3/node2
+        // /node1/node3/node3
+        // /node2
+        // /node2/node1
+        // /node2/node1/node1
+        // /node2/node1/node2
+        // /node2/node1/node3
+        // /node2/node2
+        // /node2/node2/node1
+        // /node2/node2/node2
+        // /node2/node2/node3
+        // /node2/node3
+        // /node2/node3/node1
+        // /node2/node3/node2
+        // /node2/node3/node3
+        // /node3
+        // /node3/node1
+        // /node3/node1/node1
+        // /node3/node1/node2
+        // /node3/node1/node3
+        // /node3/node2
+        // /node3/node2/node1
+        // /node3/node2/node2
+        // /node3/node2/node3
+        // /node3/node3
+        // /node3/node3/node1
+        // /node3/node3/node2
+        // /node3/node3/node3
+        // /secondBranch1
+        // /secondBranch1/secondBranch1
+        // /secondBranch1/secondBranch2
+        // /secondBranch2
+        // /secondBranch2/secondBranch1
+        // /secondBranch2/secondBranch2
+
+        String initialPath = "";
+        int depth = 3;
+        int numChildrenPerNode = 3;
+        int numPropertiesPerNode = 3;
+        Stopwatch sw = new Stopwatch();
+        boolean batch = true;
+        createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
+
+        // Delete two branches ...
+        graph.move("/node2").as("node3").into("/node3");
+
+        // Now assert the structure ...
+        assertThat(graph.getChildren().of("/"), hasChildren(segment("node1"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3/node1"), hasChildren());
+
+        assertThat(graph.getChildren().of("/node3"), hasChildren(segment("node1"),
+                                                                 segment("node2"),
+                                                                 segment("node3"),
+                                                                 segment("node3[2]")));
+        assertThat(graph.getChildren().of("/node3/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3/node1"), hasChildren());
+        assertThat(graph.getChildren().of("/node3/node3[2]"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3[2]/node1"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3[2]/node2"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3[2]/node3"), hasChildren(segment("node1"),
+                                                                                segment("node2"),
+                                                                                segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3[2]/node1/node1"), hasChildren());
+
+        Subgraph subgraph = graph.getSubgraphOfDepth(4).at("/node3");
+        assertThat(subgraph, is(notNullValue()));
+        assertThat(subgraph.getNode(".").getChildren(), hasChildren(segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("."), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("node3[2]"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1").getChildren(), hasChildren(segment("node1"),
+                                                                                 segment("node2"),
+                                                                                 segment("node3")));
+        assertThat(subgraph.getNode("node3[2]/node1"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node1/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node1/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node1/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node1/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node2/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node2/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node2/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node2/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3"), hasProperty("property1",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3"), hasProperty("property2",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3"), hasProperty("property3",
+                                                                   "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node3/node1"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node1"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node1"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node3/node2"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node2"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node2"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3[2]/node3/node3"), hasProperty("property1",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node3"), hasProperty("property2",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3[2]/node3/node3"), hasProperty("property3",
+                                                                         "The quick brown fox jumped over the moon. What? "));
+    }
+
+    @Test
+    public void shouldMoveAndRenameNodesToNameWithoutSameNameSibling() {
+        // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+        // /
+        // /node1
+        // /node1/node1
+        // /node1/node1/node1
+        // /node1/node1/node2
+        // /node1/node1/node3
+        // /node1/node2
+        // /node1/node2/node1
+        // /node1/node2/node2
+        // /node1/node2/node3
+        // /node1/node3
+        // /node1/node3/node1
+        // /node1/node3/node2
+        // /node1/node3/node3
+        // /node2
+        // /node2/node1
+        // /node2/node1/node1
+        // /node2/node1/node2
+        // /node2/node1/node3
+        // /node2/node2
+        // /node2/node2/node1
+        // /node2/node2/node2
+        // /node2/node2/node3
+        // /node2/node3
+        // /node2/node3/node1
+        // /node2/node3/node2
+        // /node2/node3/node3
+        // /node3
+        // /node3/node1
+        // /node3/node1/node1
+        // /node3/node1/node2
+        // /node3/node1/node3
+        // /node3/node2
+        // /node3/node2/node1
+        // /node3/node2/node2
+        // /node3/node2/node3
+        // /node3/node3
+        // /node3/node3/node1
+        // /node3/node3/node2
+        // /node3/node3/node3
+        // /secondBranch1
+        // /secondBranch1/secondBranch1
+        // /secondBranch1/secondBranch2
+        // /secondBranch2
+        // /secondBranch2/secondBranch1
+        // /secondBranch2/secondBranch2
+
+        String initialPath = "";
+        int depth = 3;
+        int numChildrenPerNode = 3;
+        int numPropertiesPerNode = 3;
+        Stopwatch sw = new Stopwatch();
+        boolean batch = true;
+        createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
+
+        // Delete two branches ...
+        graph.move("/node2").as("nodeX").into("/node3");
+
+        // Now assert the structure ...
+        assertThat(graph.getChildren().of("/"), hasChildren(segment("node1"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node1"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node1/node3/node1"), hasChildren());
+
+        assertThat(graph.getChildren().of("/node3"), hasChildren(segment("node1"),
+                                                                 segment("node2"),
+                                                                 segment("node3"),
+                                                                 segment("nodeX")));
+        assertThat(graph.getChildren().of("/node3/node2"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/node3/node1"), hasChildren());
+        assertThat(graph.getChildren().of("/node3/nodeX"), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/nodeX/node1"),
+                   hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/nodeX/node2"),
+                   hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/nodeX/node3"),
+                   hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(graph.getChildren().of("/node3/nodeX/node1/node1"), hasChildren());
+
+        Subgraph subgraph = graph.getSubgraphOfDepth(4).at("/node3");
+        assertThat(subgraph, is(notNullValue()));
+        assertThat(subgraph.getNode(".").getChildren(), hasChildren(segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("."), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("."), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node2[1]"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("node3"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("node3"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX").getChildren(), hasChildren(segment("node1"), segment("node2"), segment("node3")));
+        assertThat(subgraph.getNode("nodeX"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1").getChildren(), hasChildren(segment("node1"),
+                                                                              segment("node2"),
+                                                                              segment("node3")));
+        assertThat(subgraph.getNode("nodeX/node1"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node1/node1"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node1"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node1"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node1/node2"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node2"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node2"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node1/node3"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node3"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node1/node3"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node2/node1"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node1"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node1"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node2/node2"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node2"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node2"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node2/node3"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node3"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node2/node3"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3"), hasProperty("property2", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3"), hasProperty("property3", "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node1").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node3/node1"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node1"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node1"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node2").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node3/node2"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node2"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node2"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node3").getChildren(), isEmpty());
+        assertThat(subgraph.getNode("nodeX/node3/node3"), hasProperty("property1",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node3"), hasProperty("property2",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+        assertThat(subgraph.getNode("nodeX/node3/node3"), hasProperty("property3",
+                                                                      "The quick brown fox jumped over the moon. What? "));
+    }
+
 }

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -88,6 +88,11 @@
      */
     public static final boolean DEFAULT_SUPPORTS_UPDATES = false;
 
+    /**
+     * This source supports creating references.
+     */
+    protected static final boolean SUPPORTS_REFERENCES = false;
+
     public static final int DEFAULT_RETRY_LIMIT = 0;
     public static final int DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS = 60 * 5; // 5 minutes
 
@@ -96,10 +101,12 @@
     private volatile int cacheTimeToLiveInMilliseconds = DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS * 1000;
     private volatile String defaultWorkspace;
     private volatile String[] predefinedWorkspaces = new String[] {};
-    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(SUPPORTS_SAME_NAME_SIBLINGS,
+    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(
+                                                                                                  SUPPORTS_SAME_NAME_SIBLINGS,
                                                                                                   DEFAULT_SUPPORTS_UPDATES,
                                                                                                   SUPPORTS_EVENTS,
-                                                                                                  DEFAULT_SUPPORTS_CREATING_WORKSPACES);
+                                                                                                  DEFAULT_SUPPORTS_CREATING_WORKSPACES,
+                                                                                                  SUPPORTS_REFERENCES);
     private transient CachePolicy cachePolicy;
     private transient CopyOnWriteArraySet<String> availableWorkspaceNames;
 
@@ -236,7 +243,8 @@
      */
     public synchronized void setCreatingWorkspacesAllowed( boolean allowWorkspaceCreation ) {
         capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), capabilities.supportsUpdates(),
-                                                        capabilities.supportsEvents(), allowWorkspaceCreation);
+                                                        capabilities.supportsEvents(), allowWorkspaceCreation,
+                                                        capabilities.supportsReferences());
     }
 
     /**

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -39,6 +39,7 @@
     public static I18n errorSerializingCachePolicyInSource;
     public static I18n objectFoundInJndiWasNotCache;
     public static I18n objectFoundInJndiWasNotCacheFactory;
+    public static I18n unableToDeleteBranch;
     public static I18n unableToCloneWorkspaces;
     public static I18n unableToCreateWorkspaces;
     public static I18n unableToCreateWorkspace;

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRequestProcessor.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -35,9 +35,11 @@
 import org.jboss.cache.Cache;
 import org.jboss.cache.Fqn;
 import org.jboss.cache.Node;
+import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 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.Path;
 import org.jboss.dna.graph.property.PathFactory;
@@ -256,8 +258,15 @@
         Node<Name, Object> node = getNode(request, cache, nodePath);
         if (node == null) return;
 
-        node.getParent().removeChild(node.getFqn().getLastElement());
-        request.setActualLocationOfNode(Location.create(nodePath));
+        Path.Segment nameOfRemovedNode = nodePath.getLastSegment();
+        Node<Name, Object> parent = node.getParent();
+        if (cache.removeNode(node.getFqn())) {
+            removeFromChildList(cache, parent, nameOfRemovedNode, getExecutionContext());
+            request.setActualLocationOfNode(Location.create(nodePath));
+        } else {
+            String msg = JBossCacheConnectorI18n.unableToDeleteBranch.text(getSourceName(), request.inWorkspace(), nodePath);
+            request.setError(new RepositorySourceException(msg));
+        }
     }
 
     @Override
@@ -275,12 +284,15 @@
         if (newParent == null) return;
 
         // Copy the branch and use the same UUIDs ...
-        Path.Segment newSegment = copyNode(cache, node, newParent, null, true, true, null, null, getExecutionContext());
+        Name desiredName = request.desiredName();
+        Path.Segment newSegment = copyNode(cache, node, newParent, desiredName, true, true, null, null, getExecutionContext());
 
         // Now delete the old node ...
         Node<Name, Object> oldParent = node.getParent();
         boolean removed = oldParent.removeChild(node.getFqn().getLastElement());
         assert removed;
+        Path.Segment nameOfRemovedNode = nodePath.getLastSegment();
+        removeFromChildList(cache, oldParent, nameOfRemovedNode, getExecutionContext());
 
         Path newPath = pathFactory.create(newParentPath, newSegment);
         request.setActualLocations(Location.create(nodePath), Location.create(newPath));
@@ -516,6 +528,7 @@
         // Copy the properties ...
         copy.clearData();
         copy.putAll(original.getData());
+        copy.remove(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST); // will be reset later ...
 
         // Generate a new UUID for the new node, overwriting any existing value from the original ...
         if (reuseOriginalUuids) uuidForCopyOfOriginal = uuidFactory.create(original.get(DnaLexicon.UUID));
@@ -635,6 +648,63 @@
         return null;
     }
 
+    /**
+     * Update the array of {@link Path.Segment path segments} for the children of the supplied node, based upon a node being
+     * removed. This array maintains the ordered list of children (since the {@link Cache} does not maintain the order). Invoking
+     * this method will change any existing children that a {@link Path.Segment#getName() name part} that matches the supplied
+     * <code>changedName</code> to have the appropriate {@link Path.Segment#getIndex() same-name sibling index}.
+     * 
+     * @param cache the cache in which the parent exists ...
+     * @param parent the parent node; may not be null
+     * @param removedNode the segment of the node that was removed, which signals to look for node with the same name; may not be
+     *        null
+     * @param context the execution context; may not be null
+     */
+    protected void removeFromChildList( Cache<Name, Object> cache,
+                                        Node<Name, Object> parent,
+                                        Path.Segment removedNode,
+                                        ExecutionContext context ) {
+        assert parent != null;
+        assert context != null;
+        Set<Node<Name, Object>> children = parent.getChildren();
+        if (children.isEmpty()) {
+            parent.put(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST, null); // replaces any existing value
+            return;
+        }
+
+        // Go through the children, looking for any children with the same name as the 'changedName'
+        Path.Segment[] childNames = (Path.Segment[])parent.get(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST);
+        assert childNames != null;
+        int snsIndex = removedNode.getIndex();
+        int index = 0;
+        Path.Segment[] newChildNames = new Path.Segment[childNames.length - 1];
+        for (Path.Segment childName : childNames) {
+            if (!childName.getName().equals(removedNode.getName())) {
+                newChildNames[index] = childName;
+                index++;
+            } else {
+                // The name matches ...
+                if (childName.getIndex() < snsIndex) {
+                    // Just copy ...
+                    newChildNames[index] = childName;
+                    index++;
+                } else if (childName.getIndex() == snsIndex) {
+                    // don't copy ...
+                } else {
+                    // Append an updated segment ...
+                    Path.Segment newSegment = context.getValueFactories()
+                                                     .getPathFactory()
+                                                     .createSegment(childName.getName(), childName.getIndex() - 1);
+                    newChildNames[index] = newSegment;
+                    // Replace the child with the correct FQN ...
+                    changeNodeName(cache, parent, childName, newSegment, context);
+                    index++;
+                }
+            }
+        }
+        parent.put(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST, newChildNames); // replaces any existing value
+    }
+
     protected boolean checkChildren( Node<Name, Object> parent ) {
         Path.Segment[] childNamesProperty = (Path.Segment[])parent.get(JBossCacheLexicon.CHILD_PATH_SEGMENT_LIST);
         Set<Object> childNames = parent.getChildrenNames();
@@ -649,10 +719,8 @@
                 names.add((Path.Segment)name);
             }
             Collections.sort(names);
-            // Logger.getLogger(getClass()).trace("Child list on {0} is: {1}",
-            // parent.getFqn(),
-            // StringUtil.readableString(childNamesProperty));
-            // Logger.getLogger(getClass()).trace("Children of {0} is: {1}", parent.getFqn(), StringUtil.readableString(names));
+            Logger.getLogger(getClass()).trace("Child list on {0} is: {1}", parent.getFqn(), childNamesProperty);
+            Logger.getLogger(getClass()).trace("Children of {0} is: {1}", parent.getFqn(), names);
         }
         return result;
     }

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheSource.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheSource.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheSource.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -112,7 +112,7 @@
     private volatile int retryLimit = DEFAULT_RETRY_LIMIT;
     private volatile String defaultWorkspace;
     private volatile String[] predefinedWorkspaces = new String[] {};
-    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(true, true, false, true);
+    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(true, true, false, true, false);
     private transient JBossCacheWorkspaces workspaces;
     private transient Context jndiContext;
 
@@ -417,7 +417,8 @@
      * @see #isCreatingWorkspacesAllowed()
      */
     public synchronized void setCreatingWorkspacesAllowed( boolean allowWorkspaceCreation ) {
-        capabilities = new RepositorySourceCapabilities(true, capabilities.supportsUpdates(), false, allowWorkspaceCreation);
+        capabilities = new RepositorySourceCapabilities(true, capabilities.supportsUpdates(), false, allowWorkspaceCreation,
+                                                        capabilities.supportsReferences());
     }
 
     /**

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheWorkspaces.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheWorkspaces.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheWorkspaces.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -241,7 +241,7 @@
         } catch (ConfigurationException error) {
             // The workspace name is probably not the name of a configuration ...
             I18n msg = JBossCacheConnectorI18n.workspaceNameWasNotValidConfiguration;
-            Logger.getLogger(getClass()).info(msg, workspaceName, error.getMessage());
+            Logger.getLogger(getClass()).debug(msg.text(workspaceName, error.getMessage()));
         }
 
         if (this.defaultCacheFactoryConfigurationName != null) {
@@ -253,7 +253,7 @@
                 if (this.workspaceNamesForConfigurationNameProblems.add(workspaceName)) {
                     // Log this problem only the first time ...
                     I18n msg = JBossCacheConnectorI18n.defaultCacheFactoryConfigurationNameWasNotValidConfiguration;
-                    Logger.getLogger(getClass()).warn(msg, workspaceName);
+                    Logger.getLogger(getClass()).debug(msg.text(workspaceName));
                 }
             }
         }

Modified: trunk/extensions/dna-connector-jbosscache/src/main/resources/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.properties
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/resources/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.properties	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-jbosscache/src/main/resources/org/jboss/dna/connector/jbosscache/JBossCacheConnectorI18n.properties	2009-04-22 08:49:23 UTC (rev 842)
@@ -29,6 +29,7 @@
 errorSerializingCachePolicyInSource = Error serializing a {0} instance owned by the {1} JBossCacheSource
 objectFoundInJndiWasNotCache = Object in JNDI at {0} found by JBossCacheSource {1} was expected to be a org.jboss.cache.Cache<org.jboss.dna.graph.property.Name,Object> but instead was {2}
 objectFoundInJndiWasNotCacheFactory = Object in JNDI at {0} found by JBossCacheSource {1} was expected to be a org.jboss.cache.CacheFactory<org.jboss.dna.graph.property.Name,Object> but instead was {2}
+unableToDeleteBranch = Error while deleting branch "{2}" from workspace "{1}" in the JBossCacheSource {0}
 unableToCloneWorkspaces = The JBossCacheSource {0} does not allow creating workspaces, so unable to clone workspace "{1}" into "{2}"
 unableToCreateWorkspaces = The JBossCacheSource {0} does not allow creating workspaces, so unable to create workspace "{1}"
 unableToCreateWorkspace = Unable to create new workspace "{1}" in the JBossCacheSource {0}

Added: trunk/extensions/dna-connector-jbosscache/src/test/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorWritableTest.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/test/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorWritableTest.java	                        (rev 0)
+++ trunk/extensions/dna-connector-jbosscache/src/test/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorWritableTest.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -0,0 +1,79 @@
+/*
+ * 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.jbosscache;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.IOException;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.test.WritableConnectorTest;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Randall Hauch
+ */
+public class JBossCacheConnectorWritableTest extends WritableConnectorTest {
+
+    private Context mockJndi;
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
+     */
+    @Override
+    protected RepositorySource setUpSource() throws NamingException {
+        String[] predefinedWorkspaceNames = new String[] {"default"};
+        JBossCacheSource source = new JBossCacheSource();
+        source.setName("Test Repository");
+        source.setPredefinedWorkspaceNames(predefinedWorkspaceNames);
+        source.setNameOfDefaultWorkspace(predefinedWorkspaceNames[0]);
+        source.setCreatingWorkspacesAllowed(true);
+
+        // Set up the mock JNDI ...
+        mockJndi = mock(Context.class);
+        stub(mockJndi.lookup(anyString())).toReturn(null);
+        source.setContext(mockJndi);
+
+        Graph graph = Graph.create(source, context);
+        graph.useWorkspace("default");
+
+        return source;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws SAXException
+     * @throws IOException
+     * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
+     */
+    @Override
+    protected void initializeContent( Graph graph ) throws IOException, SAXException {
+    }
+}


Property changes on: trunk/extensions/dna-connector-jbosscache/src/test/java/org/jboss/dna/connector/jbosscache/JBossCacheConnectorWritableTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -132,6 +132,10 @@
      */
     protected static final boolean SUPPORTS_SAME_NAME_SIBLINGS = true;
     /**
+     * This source supports creating references.
+     */
+    protected static final boolean SUPPORTS_REFERENCES = true;
+    /**
      * This source supports udpates by default, but each instance may be configured to {@link #setSupportsUpdates(boolean) be
      * read-only or updateable}.
      */
@@ -191,10 +195,12 @@
     private volatile boolean referentialIntegrityEnforced = DEFAULT_ENFORCE_REFERENTIAL_INTEGRITY;
     private volatile String defaultWorkspace = DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
     private volatile String[] predefinedWorkspaces = new String[] {};
-    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(SUPPORTS_SAME_NAME_SIBLINGS,
+    private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(
+                                                                                                  SUPPORTS_SAME_NAME_SIBLINGS,
                                                                                                   DEFAULT_SUPPORTS_UPDATES,
                                                                                                   SUPPORTS_EVENTS,
-                                                                                                  DEFAULT_SUPPORTS_CREATING_WORKSPACES);
+                                                                                                  DEFAULT_SUPPORTS_CREATING_WORKSPACES,
+                                                                                                  SUPPORTS_REFERENCES);
     private volatile String modelName;
     private transient Model model;
     private transient DataSource dataSource;
@@ -251,7 +257,8 @@
      */
     public synchronized void setSupportsUpdates( boolean supportsUpdates ) {
         capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), supportsUpdates,
-                                                        capabilities.supportsEvents(), capabilities.supportsCreatingWorkspaces());
+                                                        capabilities.supportsEvents(), capabilities.supportsCreatingWorkspaces(),
+                                                        capabilities.supportsReferences());
     }
 
     /**
@@ -379,7 +386,8 @@
      */
     public synchronized void setCreatingWorkspacesAllowed( boolean allowWorkspaceCreation ) {
         capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), capabilities.supportsUpdates(),
-                                                        capabilities.supportsEvents(), allowWorkspaceCreation);
+                                                        capabilities.supportsEvents(), allowWorkspaceCreation,
+                                                        capabilities.supportsReferences());
     }
 
     /**
@@ -848,7 +856,8 @@
                     Context context = new InitialContext();
                     dataSource = (DataSource)context.lookup(this.dataSourceJndiName);
                 } catch (Throwable t) {
-                    Logger.getLogger(getClass()).error(t, JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
+                    Logger.getLogger(getClass())
+                          .error(t, JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
                 }
             }
 

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-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -1368,41 +1368,67 @@
                     // Now we know that the new parent is not the existing parent ...
                     final int oldIndex = fromEntity.getIndexInParent();
 
+                    // Make sure the child name is set correctly ...
+                    String childOldLocalName = fromEntity.getChildName();
+                    String childLocalName = null;
+                    NamespaceEntity ns = null;
+                    Name childName = request.desiredName();
+                    if (childName != null) {
+                        childLocalName = request.desiredName().getLocalName();
+                        String childNsUri = childName.getNamespaceUri();
+                        ns = namespaces.get(childNsUri, true);
+                    } else {
+                        childName = oldPath.getLastSegment().getName();
+                        childLocalName = fromEntity.getChildName();
+                        ns = fromEntity.getChildNamespace();
+                    }
+
                     // Find the largest SNS index in the existing ChildEntity objects with the same name ...
-                    String childLocalName = fromEntity.getChildName();
-                    NamespaceEntity ns = fromEntity.getChildNamespace();
                     Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
                     query.setParameter("workspaceId", workspaceId);
-                    query.setParameter("parentUuidString", toUuidString);
+                    query.setParameter("parentUuid", toUuidString);
                     query.setParameter("ns", ns.getId());
                     query.setParameter("childName", childLocalName);
                     int nextSnsIndex = 1;
                     try {
-                        nextSnsIndex = (Integer)query.getSingleResult();
+                        Integer index = (Integer)query.getSingleResult();
+                        if (index != null) nextSnsIndex = index.intValue() + 1;
                     } catch (NoResultException e) {
                     }
 
                     // Find the largest child index in the existing ChildEntity objects ...
                     query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
                     query.setParameter("workspaceId", workspaceId);
-                    query.setParameter("parentUuidString", toUuidString);
+                    query.setParameter("parentUuid", toUuidString);
                     int nextIndexInParent = 1;
                     try {
-                        nextIndexInParent = (Integer)query.getSingleResult() + 1;
+                        Integer index = (Integer)query.getSingleResult();
+                        if (index != null) nextIndexInParent = index + 1;
                     } catch (NoResultException e) {
                     }
 
-                    // Move the child entity to be under the new parent ...
-                    fromEntity.setId(new ChildId(workspaceId, toUuidString, fromUuidString));
-                    fromEntity.setIndexInParent(nextIndexInParent);
-                    fromEntity.setSameNameSiblingIndex(nextSnsIndex);
+                    ChildId movedId = new ChildId(workspaceId, toUuidString, fromUuidString);
+                    if (fromEntity.getId().equals(movedId)) {
+                        // The node is being renamed, but not moved ...
+                        fromEntity.setChildName(childLocalName);
+                        fromEntity.setChildNamespace(ns);
+                        fromEntity.setIndexInParent(nextIndexInParent);
+                        fromEntity.setSameNameSiblingIndex(nextSnsIndex);
+                    } else {
+                        // We won't be able to move the entity to a different parent, because that would involve
+                        // changing the PK for the entity, which is not possible. Instead, we have to create a
+                        // new entity with the same identity information, then delete 'fromEntity'
+                        ChildEntity movedEntity = new ChildEntity(movedId, nextIndexInParent, ns, childLocalName, nextSnsIndex);
+                        movedEntity.setAllowsSameNameChildren(fromEntity.getAllowsSameNameChildren());
+                        entities.persist(movedEntity);
+                        entities.remove(fromEntity);
+                    }
 
                     // Flush the entities to the database ...
                     entities.flush();
 
                     // Determine the new location ...
                     Path newParentPath = actualIntoLocation.location.getPath();
-                    Name childName = oldPath.getLastSegment().getName();
                     Path newPath = pathFactory.create(newParentPath, childName, nextSnsIndex);
                     actualNewLocation = actualOldLocation.with(newPath);
 
@@ -1410,7 +1436,7 @@
                     ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities,
                                                                         workspaceId,
                                                                         oldParentUuid,
-                                                                        childLocalName,
+                                                                        childOldLocalName,
                                                                         ns.getId(),
                                                                         oldIndex);
 

Modified: trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java	2009-04-20 20:21:21 UTC (rev 841)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java	2009-04-22 08:49:23 UTC (rev 842)
@@ -89,6 +89,10 @@
      */
     protected static final boolean SUPPORTS_CREATING_WORKSPACES = false;
     /**
+     * This source supports creating references.
+     */
+    protected static final boolean SUPPORTS_REFERENCES = false;
+    /**
      * This source supports udpates by default, but each instance may be configured to {@link #setSupportsUpdates(boolean) be
      * read-only or updateable}.
      */
@@ -115,7 +119,8 @@
     private RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(SUPPORTS_SAME_NAME_SIBLINGS,
                                                                                          DEFAULT_SUPPORTS_UPDATES,
                                                                                          SUPPORTS_EVENTS,
-                                                                                         SUPPORTS_CREATING_WORKSPACES);
+                                                                                         SUPPORTS_CREATING_WORKSPACES,
+                                                                                         SUPPORTS_REFERENCES);
 
     private transient Context jndiContext;
     private transient RepositoryContext repositoryContext;
@@ -259,7 +264,8 @@
      */
     public synchronized void setSupportsUpdates( boolean supportsUpdates ) {
         capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), supportsUpdates,
-                                                        capabilities.supportsEvents(), capabilities.supportsCreatingWorkspaces());
+                                                        capabilities.supportsEvents(), capabilities.supportsCreatingWorkspaces(),
+                                                        capabilities.supportsReferences());
     }
 
     /**



From dna-commits at lists.jboss.org  Wed Apr 22 05:22:16 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 22 Apr 2009 05:22:16 -0400
Subject: [dna-commits] DNA SVN: r843 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LwYem-0007dF-B7@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-22 05:22:16 -0400 (Wed, 22 Apr 2009)
New Revision: 843

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
Log:
DNA-377 AbstractJcrNode.addNode(String relPath) Throws Wrong Exception if relPath Has Form PropertyName/NodeName

Applied the patch that handles a path of exactly 2 segments, so that the proper exception is thrown.

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-04-22 08:49:23 UTC (rev 842)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-22 09:22:16 UTC (rev 843)
@@ -963,23 +963,30 @@
                 editor = cache.getEditorFor(parentInfo.getUuid());
             } catch (PathNotFoundException e) {
                 // We're going to throw an exception ... the question is which one ...
-                if (parentPath.size() > 1) {
-                    // Per the TCK, if relPath references a property, then we have to throw a ConstraintViolationException
-                    // So, if we can't find the parent, try for the parent's parent and see if the last segment of the parent's
-                    // path contains a property ...
-                    Path grandparentPath = parentPath.getParent();
-                    assert grandparentPath != null;
-                    try {
-                        NodeInfo grandparentInfo = cache.findNodeInfo(nodeUuid, grandparentPath); // throws PathNotFoundException
-                        if (grandparentInfo.getProperty(parentPath.getLastSegment().getName()) != null) {
-                            // Need to throw a ConstraintViolationException since the request was to add a child to
-                            // a property ...
-                            throw new ConstraintViolationException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
-                        }
-                    } catch (PathNotFoundException e2) {
-                        // eat, since the original exception is what we want ...
+                try {
+                    NodeInfo grandparentInfo;
+                    if (parentPath.size() > 1) {
+                        // Per the TCK, if relPath references a property, then we have to throw a ConstraintViolationException
+                        // So, if we can't find the parent, try for the parent's parent and see if the last segment of the parent's
+                        // path contains a property ...
+                        Path grandparentPath = parentPath.getParent();
+                        assert grandparentPath != null;
+                        
+                        grandparentInfo = cache.findNodeInfo(nodeUuid, grandparentPath); // throws PathNotFoundException
                     }
+                    else {
+                        grandparentInfo = this.nodeInfo();
+                    }
+                    
+                    if (grandparentInfo.getProperty(parentPath.getLastSegment().getName()) != null) {
+                        // Need to throw a ConstraintViolationException since the request was to add a child to
+                        // a property ...
+                        throw new ConstraintViolationException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
+                    }
+                } catch (PathNotFoundException e2) {
+                    // eat, since the original exception is what we want ...
                 }
+
                 // Otherwise, just throw the PathNotFoundException ...
                 throw e;
             }



From dna-commits at lists.jboss.org  Wed Apr 22 05:41:25 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 22 Apr 2009 05:41:25 -0400
Subject: [dna-commits] DNA SVN: r844 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property/basic
	and 3 other directories.
Message-ID: <E1LwYxJ-0005ed-UX@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-22 05:41:25 -0400 (Wed, 22 Apr 2009)
New Revision: 844

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
Log:
DNA-364 Session.move Is Not Implemented

Applied the patch, which implements the spec and passes the TCK unit tests.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -196,9 +196,9 @@
         InMemoryNode newParent = workspace.getNode(newParentPath);
         workspace.moveNode(getExecutionContext(), node, request.desiredName(), workspace, newParent);
         assert node.getParent() == newParent;
-        Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName().getName());
+        Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(newParentPath, node.getName());
         Location oldLocation = getActualLocation(request.from().getPath(), node);
-        Location newLocation = Location.create(newPath, node.getUuid());
+        Location newLocation = Location.create(newPath, newParent.getUuid());
         request.setActualLocations(oldLocation, newLocation);
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -109,7 +109,7 @@
     @Override
     public Segment getSegment( int index ) {
         if (index == (size - 1)) return child;
-        return parent.getSegment(index - 1);
+        return parent.getSegment(index);
     }
 
     /**

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-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -265,7 +265,9 @@
      */
     public boolean changes( String workspace,
                             Path path ) {
-        return this.workspaceName.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
+        return this.workspaceName.equals(workspace) 
+            && (into.hasPath() && into.getPath().isAtOrBelow(path)
+                || from.hasPath() && from.getPath().isAtOrBelow(path)); 
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -99,7 +99,7 @@
                || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW;
 
         this.session = session;
-        this.currentNode = (AbstractJcrNode)session.getNode(parentPath);
+        this.currentNode = session.getNode(parentPath);
         this.uuidBehavior = uuidBehavior;
         this.saveMode = saveMode;
 
@@ -273,9 +273,9 @@
                         uuid = UUID.fromString(rawUuid.get(0).getString());
                     }
 
-                    AbstractJcrNode newNode = parentNode.addNode(currentNodeName,
-                                                                 currentProps.get(primaryTypeName).get(0).getString(),
-                                                                 uuid);
+                    AbstractJcrNode newNode = parentNode.addNode(currentNodeName, currentProps.get(primaryTypeName)
+                                                                                              .get(0)
+                                                                                              .getString(), uuid);
 
                     for (Map.Entry<String, List<Value>> entry : currentProps.entrySet()) {
                         if (entry.getKey().equals(primaryTypeName)) {
@@ -325,8 +325,9 @@
                         ByteArrayInputStream is = new ByteArrayInputStream(Base64.decode(s, Base64.URL_SAFE));
                         currentProps.get(currentPropName).add(session.getValueFactory().createValue(is));
                     } else {
-                        currentProps.get(currentPropName).add(session.getValueFactory().createValue(SYSTEM_VIEW_NAME_DECODER.decode(s),
-                                                                                                    currentPropType));
+                        currentProps.get(currentPropName).add(session.getValueFactory()
+                                                                     .createValue(SYSTEM_VIEW_NAME_DECODER.decode(s),
+                                                                                  currentPropType));
                     }
                 } catch (RepositoryException re) {
                     throw new EnclosingSAXException(re);

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-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -62,6 +62,7 @@
 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.ValueFactories;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
@@ -420,7 +421,7 @@
      * @throws PathNotFoundException if the path could not be found
      * @throws RepositoryException if there is a problem
      */
-    Node getNode( Path path ) throws RepositoryException, PathNotFoundException {
+    AbstractJcrNode getNode( Path path ) throws RepositoryException, PathNotFoundException {
         if (path.isRoot()) return cache.findJcrRootNode();
         return cache.findJcrNode(null, path.relativeTo(rootPath));
     }
@@ -678,12 +679,30 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#move(java.lang.String, java.lang.String)
      */
     public void move( String srcAbsPath,
-                      String destAbsPath ) {
-        throw new UnsupportedOperationException();
+                      String destAbsPath ) throws ItemExistsException, RepositoryException {
+        CheckArg.isNotNull(srcAbsPath, "srcAbsPath");
+        CheckArg.isNotNull(destAbsPath, "destAbsPath");
+
+        PathFactory pathFactory = executionContext.getValueFactories().getPathFactory();
+        Path destPath = pathFactory.create(destAbsPath);
+
+        Path.Segment newNodeName = destPath.getSegment(destPath.size() - 1);
+        // Doing a literal test here because the path factory will canonicalize "/node[1]" to "/node"
+        if (destAbsPath.endsWith("]")) {
+            throw new RepositoryException();
+        }
+
+        AbstractJcrNode sourceNode = getNode(pathFactory.create(srcAbsPath));
+        AbstractJcrNode newParentNode = getNode(destPath.getParent());
+
+        if (newParentNode.hasNode(newNodeName.getString(executionContext.getNamespaceRegistry()))) {
+            throw new ItemExistsException();
+        }
+
+        newParentNode.editor().moveToBeChild(sourceNode.nodeUuid, newNodeName.getName());
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -74,7 +74,7 @@
     private final String stringRepresentation;
 
     /**
-     * Create a new identifier for a propety definition.
+     * Create a new identifier for a property definition.
      * 
      * @param nodeTypeName the name of the node type; may not be null
      * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -445,6 +445,7 @@
              * newly created node will have it's parent's UUID in branchUuids, but not the new node's uuid. 
              */
             Set<UUID> uuidsUnderBranch = new HashSet<UUID>();
+            LinkedList<UUID> peersToCheck = new LinkedList<UUID>();
             for (UUID branchUuid : branchUuids) {
                 uuidsUnderBranch.add(branchUuid);
                 ChangedNodeInfo changedNode = changedNodes.get(branchUuid);
@@ -452,10 +453,22 @@
                     for (ChildNode childNode : changedNode.getChildren()) {
                         uuidsUnderBranch.add(childNode.getUuid());
                     }
+                    
+                    Collection<UUID> peers = changedNode.getPeers();
+                    if (peers != null) peersToCheck.addAll(peers);
                 }
 
             }
 
+            /*
+             * Need to check that any peers in a Session.move operation are both in the save
+             */
+            for (UUID peerUuid : peersToCheck) {
+                if (!uuidsUnderBranch.contains(peerUuid)) {
+                    throw new ConstraintViolationException();
+                }
+            }
+            
             // Now execute the branch ...
             Graph.Batch branchBatch = store.batch(new BatchRequestBuilder(branchRequests));
             try {
@@ -899,6 +912,7 @@
          * list of children. This method automatically disconnects the node from its current parent.
          * 
          * @param nodeUuid the UUID of the existing node; may not be null
+         * @param newNodeName
          * @return the representation of the newly-added child, which includes the {@link ChildNode#getSnsIndex()
          *         same-name-sibling index}
          * @throws ItemNotFoundException if the specified child node could be found in the session or workspace
@@ -906,7 +920,7 @@
          * @throws ConstraintViolationException if moving the node into this node violates this node's definition
          * @throws RepositoryException if any other error occurs while reading information from the repository
          */
-        public ChildNode moveToBeChild( UUID nodeUuid )
+        public ChildNode moveToBeChild( UUID nodeUuid, Name newNodeName )
             throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
 
             if (nodeUuid.equals(node.getUuid()) || isAncestor(nodeUuid)) {
@@ -942,8 +956,8 @@
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
-                                                                                                                 .getString());
+                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING,
+                                                  definition.getId().getString());
                     setProperty(DnaLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
@@ -959,14 +973,18 @@
 
             // Remove the node from the current parent and add it to this ...
             child = existingParentInfo.removeChild(nodeUuid, pathFactory);
-            ChildNode newChild = node.addChild(child.getName(), child.getUuid(), pathFactory);
+            ChildNode newChild = node.addChild(newNodeName, child.getUuid(), pathFactory);
 
             // Set the child's changed representation to point to this node as its parent ...
             existingNodeInfo.setParent(node.getUuid());
-
+            
+            // Set up the peer relationship between the two nodes that must be saved together
+            node.addPeer(existingParent);
+            existingParentInfo.addPeer(node.getUuid());
+            
             // Now, record the operation to do this ...
             operations.move(existingNodeEditor.currentLocation).into(currentLocation);
-
+            
             return newChild;
         }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-22 09:22:16 UTC (rev 843)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-22 09:41:25 UTC (rev 844)
@@ -86,6 +86,8 @@
      */
     private NodeDefinitionId changedDefinitionId;
 
+    private List<UUID> peers;
+
     /**
      * Create an immutable NodeInfo instance.
      * 
@@ -97,6 +99,35 @@
     }
 
     /**
+     * Returns the peer nodes for this changed node.
+     * <p>
+     * Peer nodes are nodes that must be saved with this node (e.g., the other changed node in a
+     * {@link javax.jcr.Session#move(String, String)} operation.
+     * </p>
+     * 
+     * @return a collection of the UUIDs for any other nodes that must be saved with this node; may be null
+     */
+    public final Collection<UUID> getPeers() {
+        return peers;
+    }
+
+    /**
+     * Adds a peer node to this change.
+     * <p>
+     * Peer nodes are nodes that must be saved with this node (e.g., the other changed node in a
+     * {@link javax.jcr.Session#move(String, String)} operation.
+     * </p>
+     * 
+     * @param peerUuid the UUID of the peer node
+     */
+    public void addPeer( UUID peerUuid ) {
+        if (peers == null) {
+            peers = new LinkedList<UUID>();
+        }
+        peers.add(peerUuid);
+    }
+
+    /**
      * Return the original node information. May be null if this is a new node.
      * 
      * @return the original node information



From dna-commits at lists.jboss.org  Wed Apr 22 09:38:14 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 22 Apr 2009 09:38:14 -0400
Subject: [dna-commits] DNA SVN: r845 - in trunk:
	dna-common/src/test/java/org/jboss/dna/common/component and 3
	other directories.
Message-ID: <E1LwceU-0005gc-8A@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-22 09:38:14 -0400 (Wed, 22 Apr 2009)
New Revision: 845

Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentConfig.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
   trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentConfigTest.java
   trunk/dna-common/src/test/java/org/jboss/dna/common/component/SampleComponentConfig.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/rules/RuleSet.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencerConfigTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencingServiceTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java
Log:
DNA-58 Create repository configuration and component

Applied the first patch ("") and a small part of the second (""), which enhanced ComponentConfig to allow properties that are set reflectively on the components when they are instantiated.  Subclasses of ComponentConfig were also modified, as were the test cases.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentConfig.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentConfig.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentConfig.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -25,7 +25,9 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.CommonI18n;
 import org.jboss.dna.common.util.CheckArg;
@@ -41,38 +43,73 @@
     private final String description;
     private final String componentClassname;
     private final List<String> classpath;
+    private final Map<String, Object> properties;
     private final long timestamp;
 
     /**
      * Create a component configuration.
+     * 
      * @param name the name of the configuration, which is considered to be a unique identifier
      * @param description the description
      * @param classname the name of the Java class used for the component
      * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
      * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
-     * Java classname
+     *         Java classname
      */
-    public ComponentConfig( String name, String description, String classname, String... classpath ) {
-        this(name, description, System.currentTimeMillis(), classname, classpath);
+    public ComponentConfig( String name,
+                            String description,
+                            String classname,
+                            String... classpath ) {
+        this(name, description, System.currentTimeMillis(), Collections.<String, Object>emptyMap(), classname, classpath);
     }
 
     /**
      * Create a component configuration.
+     * 
      * @param name the name of the configuration, which is considered to be a unique identifier
      * @param description the description
+     * @param properties the mapping of properties to values that should be set through reflection after a component is
+     *        instantiated with this configuration information
+     * @param classname the name of the Java class used for the component
+     * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
+     * @throws IllegalArgumentException if the name is null, empty or blank, or if the class name is null, empty or not a valid
+     *         Java class name
+     */
+    public ComponentConfig( String name,
+                            String description,
+                            Map<String, Object> properties,
+                            String classname,
+                            String... classpath ) {
+        this(name, description, System.currentTimeMillis(), properties, classname, classpath);
+    }
+
+    /**
+     * Create a component configuration.
+     * 
+     * @param name the name of the configuration, which is considered to be a unique identifier
+     * @param description the description
      * @param timestamp the timestamp that this component was last changed
+     * @param properties the mapping of properties to values that should be set through reflection after a component is
+     *        instantiated with this configuration information
      * @param classname the name of the Java class used for the component
      * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
      * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
-     * Java classname
+     *         Java classname
      */
-    public ComponentConfig( String name, String description, long timestamp, String classname, String... classpath ) {
+    public ComponentConfig( String name,
+                            String description,
+                            long timestamp,
+                            Map<String, Object> properties,
+                            String classname,
+                            String... classpath ) {
         CheckArg.isNotEmpty(name, "name");
         this.name = name.trim();
         this.description = description != null ? description.trim() : "";
         this.componentClassname = classname;
         this.classpath = buildList(classpath);
         this.timestamp = timestamp;
+        this.properties = properties != null ? Collections.unmodifiableMap(new HashMap<String, Object>(properties)) : Collections.<String, Object>emptyMap();
+
         // Check the classname is a valid classname ...
         if (!ClassUtil.isFullyQualifiedClassname(classname)) {
             throw new IllegalArgumentException(CommonI18n.componentClassnameNotValid.text(classname, name));
@@ -95,6 +132,7 @@
 
     /**
      * Get the name of this component.
+     * 
      * @return the component name; never null, empty or blank
      */
     public String getName() {
@@ -103,6 +141,7 @@
 
     /**
      * Get the description for this component
+     * 
      * @return the description
      */
     public String getDescription() {
@@ -111,6 +150,7 @@
 
     /**
      * Get the fully-qualified name of the Java class used for instances of this component
+     * 
      * @return the Java class name of this component; never null or empty and always a valid Java class name
      */
     public String getComponentClassname() {
@@ -119,6 +159,7 @@
 
     /**
      * Get the classpath defined in terms of strings compatible with a {@link ClassLoaderFactory}.
+     * 
      * @return the classpath; never null but possibly empty
      */
     public List<String> getComponentClasspath() {
@@ -127,6 +168,7 @@
 
     /**
      * Get the classpath defined as an array of strings compatible with a {@link ClassLoaderFactory}.
+     * 
      * @return the classpath as an array; never null but possibly empty
      */
     public String[] getComponentClasspathArray() {
@@ -135,6 +177,7 @@
 
     /**
      * Get the system timestamp when this configuration object was created.
+     * 
      * @return the timestamp
      */
     public long getTimestamp() {
@@ -142,6 +185,15 @@
     }
 
     /**
+     * Get the (unmodifiable) properties to be set through reflection on components of this type after instantiation
+     * 
+     * @return the properties to be set through reflection on components of this type after instantiation; never null
+     */
+    public Map<String, Object> getProperties() {
+        return this.properties;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public int compareTo( ComponentConfig that ) {
@@ -177,10 +229,11 @@
     /**
      * Determine whether this component has changed with respect to the supplied component. This method basically checks all
      * attributes, whereas {@link #equals(Object) equals} only checks the {@link #getClass() type} and {@link #getName()}.
+     * 
      * @param component the component to be compared with this one
      * @return true if this componet and the supplied component have some changes, or false if they are exactly equivalent
      * @throws IllegalArgumentException if the supplied component reference is null or is not the same {@link #getClass() type} as
-     * this object
+     *         this object
      */
     public boolean hasChanged( ComponentConfig component ) {
         CheckArg.isNotNull(component, "component");

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -26,6 +26,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
@@ -35,6 +36,7 @@
 import org.jboss.dna.common.CommonI18n;
 import org.jboss.dna.common.SystemFailureException;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Reflection;
 
 /**
  * Maintains the list of component instances for the system. This class does not actively update the component configurations, but
@@ -255,6 +257,14 @@
             if (newInstance instanceof Component) {
                 ((Component<ConfigType>)newInstance).setConfiguration(config);
             }
+            
+            if (config.getProperties() != null) {
+                for (Map.Entry<String, Object> entry : config.getProperties().entrySet()) {
+                    // Set the JavaBean-style property on the RepositorySource instance ...
+                    Reflection reflection = new Reflection(newInstance.getClass());
+                    reflection.invokeSetterMethodOnTarget(entry.getKey(), newInstance, entry.getValue());
+                }
+            }
         } catch (Throwable e) {
             throw new SystemFailureException(e);
         }

Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentConfigTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentConfigTest.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/component/ComponentConfigTest.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -26,6 +26,7 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
+import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -111,4 +112,12 @@
         assertThat(configA.getComponentClasspathArray().length, is(0));
     }
 
+    @Test
+    public void shouldSetValidProperty() {
+        configA = new ComponentConfig("configA", validDescription, System.currentTimeMillis(), Collections.<String, Object>singletonMap("name", "test name"), MockComponentA.class.getName(), (String[])null);
+        assertThat(configA.getComponentClasspath().size(), is(0));
+        assertThat(configA.getComponentClasspathArray().length, is(0));
+        assertThat(configA.getProperties().size(), is(1));
+        assertThat(configA.getProperties().get("name").toString(), is("test name"));
+    }
 }

Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/component/SampleComponentConfig.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/component/SampleComponentConfig.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/component/SampleComponentConfig.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -23,6 +23,8 @@
  */
 package org.jboss.dna.common.component;
 
+import java.util.Map;
+
 /**
  * @author Randall Hauch
  */
@@ -36,11 +38,12 @@
      * @param name
      * @param description
      * @param timestamp
+     * @param properties
      * @param classname
      * @param classpath
      */
-    public SampleComponentConfig( String name, String description, long timestamp, String classname, String[] classpath ) {
-        super(name, description, timestamp, classname, classpath);
+    public SampleComponentConfig( String name, String description, long timestamp, Map<String, Object> properties, String classname, String[] classpath ) {
+        super(name, description, timestamp, properties, classname, classpath);
 
     }
 

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/rules/RuleSet.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/rules/RuleSet.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/rules/RuleSet.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -26,7 +26,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import javax.rules.RuleServiceProvider;
@@ -39,6 +38,7 @@
 
 /**
  * A description of a set of rules compatible with a JSR-94 rule engine.
+ * 
  * @author Randall Hauch
  */
 @Immutable
@@ -47,10 +47,10 @@
     private final String providerUri;
     private final String ruleSetUri;
     private final String rules;
-    private final Map<String, Object> properties;
 
     /**
      * Create a JSR-94 rule set definition.
+     * 
      * @param name the name of the rule set, which is considered the unique identifier
      * @param description the description
      * @param classname the name of the Java class used for the component
@@ -59,12 +59,19 @@
      * @param ruleSetUri the URI of the JSR-94 {@link RuleExecutionSet} represented by this object; if null, the name is used
      * @param rules the string containing the rules in a provider-specific language
      * @param properties the provider-specific properties, whose values should be strings or byte arrays (the latter if the
-     * provider expects an {@link Reader} with the value)
+     *        provider expects an {@link Reader} with the value)
      * @throws IllegalArgumentException if any of the name, classname, provider URI, or rules parameters are null, empty or blank,
-     * or if the classname is not a valid Java classname
+     *         or if the classname is not a valid Java classname
      */
-    public RuleSet( String name, String description, String classname, String[] classpath, String providerUri, String ruleSetUri, String rules, Map<String, Object> properties ) {
-        super(name, description, System.currentTimeMillis(), classname, classpath);
+    public RuleSet( String name,
+                    String description,
+                    String classname,
+                    String[] classpath,
+                    String providerUri,
+                    String ruleSetUri,
+                    String rules,
+                    Map<String, Object> properties ) {
+        super(name, description, System.currentTimeMillis(), properties, classname, classpath);
         if (ruleSetUri == null) ruleSetUri = name.trim();
         CheckArg.isNotEmpty(ruleSetUri, "rule set URI");
         CheckArg.isNotEmpty(providerUri, "provider URI");
@@ -72,12 +79,11 @@
         this.providerUri = providerUri;
         this.ruleSetUri = ruleSetUri;
         this.rules = rules;
-        if (properties == null) properties = Collections.emptyMap();
-        this.properties = Collections.unmodifiableMap(properties);
     }
 
     /**
      * Get the URI of the JSR-94 {@link RuleServiceProvider} implementation that should be used.
+     * 
      * @return the URI of the JSR-94 implementation; never null, empty or blank
      */
     public String getProviderUri() {
@@ -86,6 +92,7 @@
 
     /**
      * Get the URI of this rule set. The value must be valid as defined by JSR-94 {@link RuleExecutionSet}.
+     * 
      * @return the rule set's URI; never null, empty or blank
      */
     public String getRuleSetUri() {
@@ -94,6 +101,7 @@
 
     /**
      * Get the rules defined in terms of the language reqired by the {@link #getProviderUri() provider}.
+     * 
      * @return the rules for this rule set
      */
     public String getRules() {
@@ -101,27 +109,18 @@
     }
 
     /**
-     * Get this rule set's properties as an unmodifiable map. Note that the values of these properties are either strings if the
-     * value is to be {@link #getExecutionSetProperties() passed} literally, or a byte array if the value is to be
-     * {@link #getExecutionSetProperties() passed} as an InputStream.
-     * @return the unmodifiable properties; never null but possible empty
-     */
-    public Map<String, Object> getProperties() {
-        return this.properties;
-    }
-
-    /**
      * Get the properties for this rule set that can be passed to an {@link RuleExecutionSetProvider}'s
      * {@link RuleExecutionSetProvider#createRuleExecutionSet(String, Map) createRuleExecutionSet} method.
      * <p>
      * This method converts any byte array value in the {@link #getProperties() properties} into an {@link Reader}. Since
      * {@link ByteArrayInputStream} is used, there is no need to close these stream.
      * </p>
+     * 
      * @return the properties; never null but possible empty
      */
     public Map<Object, Object> getExecutionSetProperties() {
         Map<Object, Object> props = new HashMap<Object, Object>();
-        for (Map.Entry<String, Object> entry : this.properties.entrySet()) {
+        for (Map.Entry<String, Object> entry : getProperties().entrySet()) {
             String key = entry.getKey();
             Object value = entry.getValue();
             if (value instanceof byte[]) {
@@ -149,6 +148,7 @@
      */
     @Override
     public RuleSet clone() {
-        return new RuleSet(this.getName(), this.getDescription(), this.getComponentClassname(), this.getComponentClasspathArray(), this.providerUri, this.ruleSetUri, this.rules, this.properties);
+        return new RuleSet(getName(), getDescription(), getComponentClassname(), getComponentClasspathArray(), this.providerUri,
+                           this.ruleSetUri, this.rules, getProperties());
     }
 }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -26,6 +26,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.component.ComponentConfig;
@@ -38,12 +39,12 @@
 
     private final Set<SequencerPathExpression> pathExpressions;
 
-    public SequencerConfig( String name, String description, String classname, String[] classpath, String... pathExpressions ) {
-        this(name, description, System.currentTimeMillis(), classname, classpath, pathExpressions);
+    public SequencerConfig( String name, String description, Map<String, Object> properties, String classname, String[] classpath, String... pathExpressions ) {
+        this(name, description, System.currentTimeMillis(), properties, classname, classpath, pathExpressions);
     }
 
-    public SequencerConfig( String name, String description, long timestamp, String classname, String[] classpath, String... pathExpressions ) {
-        super(name, description, timestamp, classname, classpath);
+    public SequencerConfig( String name, String description, long timestamp, Map<String, Object> properties, String classname, String[] classpath, String... pathExpressions ) {
+        super(name, description, timestamp, properties, classname, classpath);
         this.pathExpressions = buildPathExpressionSet(pathExpressions);
     }
 

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencerConfigTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencerConfigTest.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencerConfigTest.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -24,6 +24,7 @@
 
 package org.jboss.dna.repository.sequencer;
 
+import java.util.Collections;
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
 import org.jboss.dna.repository.sequencer.SequencerConfig;
@@ -51,47 +52,47 @@
         this.validClassname = MockSequencerA.class.getName();
         this.validPathExpressions = new String[] {"/a/b/c/d[e/@attribute] => ."};
         this.validMavenIds = new String[] {"com.acme:configA:1.0,com.acme:configB:1.0"};
-        this.configA = new SequencerConfig("configA", validDescription, MockSequencerA.class.getName(), validMavenIds,
+        this.configA = new SequencerConfig("configA", validDescription, Collections.<String, Object>emptyMap(), MockSequencerA.class.getName(), validMavenIds,
                                            validPathExpressions);
-        this.configB = new SequencerConfig("configB", validDescription, MockSequencerB.class.getName(), validMavenIds,
+        this.configB = new SequencerConfig("configB", validDescription, Collections.<String, Object>emptyMap(), MockSequencerB.class.getName(), validMavenIds,
                                            validPathExpressions);
-        this.configA2 = new SequencerConfig("conFigA", validDescription, MockSequencerA.class.getName(), validMavenIds,
+        this.configA2 = new SequencerConfig("conFigA", validDescription, Collections.<String, Object>emptyMap(), MockSequencerA.class.getName(), validMavenIds,
                                             validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowNullNameInConstructor() {
-        new SequencerConfig(null, validDescription, validClassname, validMavenIds, validPathExpressions);
+        new SequencerConfig(null, validDescription, Collections.<String, Object>emptyMap(), validClassname, validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowEmptyNameInConstructor() {
-        new SequencerConfig("", validDescription, validClassname, validMavenIds, validPathExpressions);
+        new SequencerConfig("", validDescription, Collections.<String, Object>emptyMap(), validClassname, validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowBlankNameInConstructor() {
-        new SequencerConfig("   \t", validDescription, validClassname, validMavenIds, validPathExpressions);
+        new SequencerConfig("   \t", validDescription, Collections.<String, Object>emptyMap(), validClassname, validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowNullClassNameInConstructor() {
-        new SequencerConfig(validName, validDescription, null, validMavenIds, validPathExpressions);
+        new SequencerConfig(validName, validDescription, Collections.<String, Object>emptyMap(), null, validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowEmptyClassNameInConstructor() {
-        new SequencerConfig(validName, validDescription, "", validMavenIds, validPathExpressions);
+        new SequencerConfig(validName, validDescription, Collections.<String, Object>emptyMap(), "", validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowBlankClassNameInConstructor() {
-        new SequencerConfig(validName, validDescription, "   \t", validMavenIds, validPathExpressions);
+        new SequencerConfig(validName, validDescription, Collections.<String, Object>emptyMap(), "   \t", validMavenIds, validPathExpressions);
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowInvalidClassNameInConstructor() {
-        new SequencerConfig(validName, validDescription, "12.this is not a valid classname", validMavenIds, validPathExpressions);
+        new SequencerConfig(validName, validDescription, Collections.<String, Object>emptyMap(), "12.this is not a valid classname", validMavenIds, validPathExpressions);
     }
 
     @Test
@@ -118,7 +119,7 @@
 
     @Test
     public void shouldHaveNonNullPathExpressionCollectionWhenThereAreNoPathExpressions() {
-        configA = new SequencerConfig("configA", validDescription, validClassname, validMavenIds);
+        configA = new SequencerConfig("configA", validDescription, Collections.<String, Object>emptyMap(), validClassname, validMavenIds);
         assertThat(configA.getPathExpressions().size(), is(0));
     }
 
@@ -130,7 +131,7 @@
 
     @Test
     public void shouldGetNonNullSequencerClasspathWhenEmpty() {
-        configA = new SequencerConfig("configA", validDescription, validClassname, null, validPathExpressions);
+        configA = new SequencerConfig("configA", validDescription, Collections.<String, Object>emptyMap(), validClassname, null, validPathExpressions);
         assertThat(configA.getComponentClasspath().size(), is(0));
         assertThat(configA.getComponentClasspathArray().length, is(0));
     }

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencingServiceTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencingServiceTest.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/SequencingServiceTest.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.hasItem;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -306,7 +307,7 @@
         String classname = MockSequencerA.class.getName();
         String[] classpath = null;
         String[] pathExpressions = {"/testnodeC/testnodeD/@description => ."};
-        SequencerConfig configA = new SequencerConfig(name, desc, classname, classpath, pathExpressions);
+        SequencerConfig configA = new SequencerConfig(name, desc, Collections.<String, Object>emptyMap(), classname, classpath, pathExpressions);
         sequencingService.addSequencer(configA);
 
         // Start the repository and get a session ...

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java	2009-04-22 09:41:25 UTC (rev 844)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java	2009-04-22 13:38:14 UTC (rev 845)
@@ -69,7 +69,7 @@
     private StreamSequencer streamSequencer;
     private StreamSequencerAdapter sequencer;
     private String[] validExpressions = {"/a/* => /output"};
-    private SequencerConfig validConfig = new SequencerConfig("name", "desc", "something.class", null, validExpressions);
+    private SequencerConfig validConfig = new SequencerConfig("name", "desc", Collections.<String, Object>emptyMap(), "something.class", null, validExpressions);
     private JcrTools tools;
     private Session session;
     private SequencerOutputMap sequencerOutput;



From dna-commits at lists.jboss.org  Wed Apr 22 10:05:41 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 22 Apr 2009 10:05:41 -0400
Subject: [dna-commits] DNA SVN: r846 - in trunk/dna-repository/src:
	main/java/org/jboss/dna/repository/mimetype and 3 other directories.
Message-ID: <E1Lwd53-0006Ws-N5@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-22 10:05:41 -0400 (Wed, 22 Apr 2009)
New Revision: 846

Added:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfigurationException.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java
Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/mimetype/MimeTypeDetectorConfig.java
   trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/mimetype/AbstractMimeTypeTest.java
Log:
DNA-58 Create repository configuration and component

Applied the second patch, which added DnaConfiguration and DnaEngine classes (along with test cases), and the MimeTypeDetectorConfig class (and its test cases).

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -0,0 +1,917 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.component.ClassLoaderFactory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Reflection;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.PathExpression;
+import org.jboss.dna.graph.sequencer.StreamSequencer;
+import org.jboss.dna.repository.mimetype.MimeTypeDetectorConfig;
+import org.jboss.dna.repository.sequencer.Sequencer;
+import org.jboss.dna.repository.sequencer.SequencerConfig;
+import org.jboss.dna.repository.sequencer.StreamSequencerAdapter;
+
+/**
+ */
+ at Immutable
+public class DnaConfiguration {
+
+    protected RepositorySource configurationSource;
+    protected String configurationSourceDescription;
+    protected Graph configuration;
+    private final ExecutionContext context;
+
+    /**
+     * Mapping of repository names to configured repositories
+     */
+    protected final Map<String, DnaRepositoryDetails> repositories;
+    protected final Map<String, DnaMimeTypeDetectorDetails> mimeTypeDetectors;
+    protected final Map<String, DnaSequencerDetails> sequencers;
+
+    /**
+     * Create a new configuration for DNA.
+     */
+    public DnaConfiguration() {
+        this(new ExecutionContext());
+    }
+
+    /**
+     * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+     * 
+     * @param context the new context, or null if a default-constructed execution context should be used
+     */
+    public DnaConfiguration( ExecutionContext context ) {
+        this.context = context;
+        this.repositories = new HashMap<String, DnaRepositoryDetails>();
+        this.mimeTypeDetectors = new HashMap<String, DnaMimeTypeDetectorDetails>();
+        this.sequencers = new HashMap<String, DnaSequencerDetails>();
+
+        // Set up the default configuration repository ...
+        this.configurationSource = createDefaultConfigurationSource();
+
+    }
+
+    private DnaConfiguration( DnaConfiguration source ) {
+        this.configuration = source.configuration;
+        this.configurationSource = source.configurationSource;
+        this.context = source.context;
+        this.configurationSourceDescription = source.configurationSourceDescription;
+        this.repositories = new HashMap<String, DnaRepositoryDetails>(source.repositories);
+        this.mimeTypeDetectors = new HashMap<String, DnaMimeTypeDetectorDetails>(source.mimeTypeDetectors);
+        this.sequencers = new HashMap<String, DnaSequencerDetails>(source.sequencers);
+    }
+
+    private DnaConfiguration with( String repositoryName,
+                                   DnaRepositoryDetails details ) {
+        DnaConfiguration newConfig = new DnaConfiguration(this);
+        newConfig.repositories.put(repositoryName, details);
+
+        return newConfig;
+    }
+
+    protected ExecutionContext getExecutionContext() {
+        return this.context;
+    }
+
+    protected Graph getConfiguration() {
+        if (this.configuration == null) {
+            this.configuration = Graph.create(configurationSource, context);
+        }
+        return this.configuration;
+    }
+
+    /**
+     * Method that is used to set up the default configuration repository source. By default, this method sets up the
+     * {@link InMemoryRepositorySource} loaded from the classpath.
+     * 
+     * @return the default repository source
+     */
+    protected RepositorySource createDefaultConfigurationSource() {
+        this.withConfigurationRepository()
+            .usingClass(InMemoryRepositorySource.class.getName())
+            .loadedFromClasspath()
+            .describedAs("Configuration Repository")
+            .with("name")
+            .setTo("Configuration");
+        return configurationSource;
+    }
+
+    /**
+     * Specify that this configuration should use a particular {@link RepositorySource} for its configuration repository. By
+     * default each configuration uses an internal transient repository for its configuration, but using this method will make the
+     * configuration use a different repository (that is perhaps shared with other processes).
+     * 
+     * @return the interface for choosing the class, which returns the interface used to configure the repository source that will
+     *         be used for the configuration repository; never null
+     */
+    public ChooseClass<RepositorySource, RepositoryDetails> withConfigurationRepository() {
+        return addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME);
+    }
+
+    /**
+     * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name, and
+     * if the name of an existing repository is used, this will replace the existing repository configuration.
+     * 
+     * @param name the name of the new repository that is to be added
+     * @return the interface for choosing the class, which returns the interface used to configure the repository source; never
+     *         null
+     * @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
+     * @see #addRepository(RepositorySource)
+     */
+    public ChooseClass<RepositorySource, RepositoryDetails> addRepository( final String name ) {
+        return new ClassChooser<RepositorySource, RepositoryDetails>() {
+
+            @Override
+            protected RepositoryDetails getComponentBuilder( String className,
+                                                             String... classpath ) {
+                try {
+                    Class<?> clazz = Class.forName(className);
+                    Object newInstance = clazz.newInstance();
+                    assert newInstance instanceof RepositorySource;
+
+                    DnaRepositoryDetails details = new DnaRepositoryDetails((RepositorySource)newInstance);
+                    DnaConfiguration.this.repositories.put(name, details);
+
+                    return details;
+                } catch (Exception ex) {
+                    throw new DnaConfigurationException(ex);
+                }
+            }
+        };
+    }
+
+    /**
+     * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name, and
+     * if the name of an existing repository is used, this will replace the existing repository configuration.
+     * 
+     * @param source the {@link RepositorySource} instance that should be used
+     * @return this configuration object, for method-chaining purposes
+     * @throws IllegalArgumentException if the repository source reference is null
+     * @see #addRepository(String)
+     */
+    public DnaConfiguration addRepository( RepositorySource source ) {
+        return this.with(source.getName(), new DnaRepositoryDetails(source));
+    }
+
+    /**
+     * Add a new {@link Sequencer sequencer} to this configuration. The new sequencer will have the supplied name, and if the name
+     * of an existing sequencer is used, this will replace the existing sequencer configuration.
+     * 
+     * @param name the name of the new sequencer
+     * @return the interface for choosing the class, which returns the interface used to configure the sequencer; never null
+     * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
+     */
+    public ChooseClass<Sequencer, SequencerDetails> addSequencer( final String name ) {
+        return new ClassChooser<Sequencer, SequencerDetails>() {
+
+            @Override
+            protected SequencerDetails getComponentBuilder( String className,
+                                                            String... classpath ) {
+                try {
+                    Class<?> clazz = Class.forName(className);
+                    Object newInstance = clazz.newInstance();
+                    assert newInstance instanceof Sequencer;
+
+                    DnaSequencerDetails details = new DnaSequencerDetails(name, (Sequencer)newInstance, classpath);
+                    DnaConfiguration.this.sequencers.put(name, details);
+
+                    return details;
+                } catch (Exception ex) {
+                    throw new DnaConfigurationException(ex);
+                }
+            }
+        };
+    }
+
+    /**
+     * Add a new {@link StreamSequencer sequencer} to this configuration. The new stream sequencer will have the supplied name,
+     * and if the name of an existing sequencer is used, this will replace the existing sequencer configuration.
+     * 
+     * @param name the name of the new sequencer
+     * @return the interface for choosing the class, which returns the interface used to configure the stream sequencer; never
+     *         null
+     * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
+     */
+    public ChooseClass<StreamSequencer, SequencerDetails> addStreamSequencer( final String name ) {
+        return new ClassChooser<StreamSequencer, SequencerDetails>() {
+
+            @Override
+            protected SequencerDetails getComponentBuilder( String className,
+                                                            String... classpath ) {
+                try {
+                    Class<?> clazz = Class.forName(className);
+                    Object newInstance = clazz.newInstance();
+                    assert newInstance instanceof StreamSequencer;
+
+                    DnaSequencerDetails details = new DnaSequencerDetails(
+                                                                          name,
+                                                                          new StreamSequencerAdapter((StreamSequencer)newInstance),
+                                                                          classpath);
+                    DnaConfiguration.this.sequencers.put(name, details);
+
+                    return details;
+                } catch (Exception ex) {
+                    throw new DnaConfigurationException(ex);
+                }
+            }
+        };
+    }
+
+    /**
+     * Add a new {@link MimeTypeDetector MIME type detector} to this configuration. The new detector will have the supplied name,
+     * and if the name of an existing detector is used, this will replace the existing detector configuration.
+     * 
+     * @param name the name of the new detector
+     * @return the interface for choosing the class, which returns the interface used to configure the detector; never null
+     * @throws IllegalArgumentException if the detector name is null, empty, or otherwise invalid
+     */
+    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails> addMimeTypeDetector( final String name ) {
+        return new ClassChooser<MimeTypeDetector, MimeTypeDetectorDetails>() {
+
+            @Override
+            protected MimeTypeDetectorDetails getComponentBuilder( String className,
+                                                                   String... classpath ) {
+                try {
+                    Class<?> clazz = Class.forName(className);
+                    Object newInstance = clazz.newInstance();
+                    assert newInstance instanceof MimeTypeDetector;
+
+                    DnaMimeTypeDetectorDetails details = new DnaMimeTypeDetectorDetails(name, (MimeTypeDetector)newInstance,
+                                                                                        classpath);
+                    DnaConfiguration.this.mimeTypeDetectors.put(name, details);
+
+                    return details;
+                } catch (Exception ex) {
+                    throw new DnaConfigurationException(ex);
+                }
+            }
+        };
+    }
+
+    /**
+     * Complete this configuration and create the corresponding engine.
+     * 
+     * @return the new engine configured by this instance
+     */
+    public DnaEngine build() {
+        return new DnaEngine(this);
+    }
+
+    /**
+     * Interface used to configure a {@link RepositorySource repository}.
+     */
+    public interface RepositoryDetails
+        extends SetDescription<RepositoryDetails>, SetProperties<RepositoryDetails>, ConfigurationBuilder {
+
+        RepositorySource getRepositorySource();
+    }
+
+    /**
+     * Local implementation of the {@link MimeTypeDetectorDetails} interface that tracks all of the user-provided configuration
+     * details.
+     */
+    protected class DnaMimeTypeDetectorDetails implements MimeTypeDetectorDetails {
+        final MimeTypeDetector mimeTypeDetector;
+        private String name;
+        private String description;
+        private Map<String, Object> properties;
+        private String className;
+        private String[] classpath;
+
+        protected DnaMimeTypeDetectorDetails( String name,
+                                              MimeTypeDetector mimeTypeDetector,
+                                              String[] classpath ) {
+            this.mimeTypeDetector = mimeTypeDetector;
+            this.name = name;
+            this.description = mimeTypeDetector.getClass().getName();
+            this.properties = new HashMap<String, Object>();
+            this.className = mimeTypeDetector.getClass().getName();
+            this.classpath = classpath;
+        }
+
+        public Map<String, Object> getProperties() {
+            return properties;
+        }
+
+        protected String getDescription() {
+            return description;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public MimeTypeDetectorDetails describedAs( String description ) {
+            this.description = description;
+            return this;
+        }
+
+        public PropertySetter<MimeTypeDetectorDetails> with( final String propertyName ) {
+            return new MappingPropertySetter<MimeTypeDetectorDetails>(propertyName, this);
+        }
+
+        public MimeTypeDetector getMimeTypeDetector() {
+            return mimeTypeDetector;
+        }
+
+        MimeTypeDetectorConfig getMimeTypeDetectorConfig() {
+            return new MimeTypeDetectorConfig(name, description, properties, className, classpath);
+        }
+
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+
+    }
+
+    /**
+     * Local implementation of the {@link RepositoryDetails} interface that tracks all of the user-provided configuration details.
+     */
+    protected class DnaRepositoryDetails implements RepositoryDetails {
+        private final RepositorySource source;
+        private final String description;
+        private Map<String, Object> properties;
+
+        protected DnaRepositoryDetails( RepositorySource source ) {
+            this.source = source;
+            this.description = source.getName();
+            this.properties = new HashMap<String, Object>();
+        }
+
+        protected DnaRepositoryDetails( RepositorySource source,
+                                        String description ) {
+            this.source = source;
+            this.description = description;
+        }
+
+        public Map<String, Object> getProperties() {
+            return properties;
+        }
+
+        protected String getDescription() {
+            return description;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public RepositoryDetails describedAs( String description ) {
+            return new DnaRepositoryDetails(this.source, description);
+        }
+
+        public PropertySetter<RepositoryDetails> with( final String propertyName ) {
+            return new BeanPropertySetter<RepositoryDetails>(source, propertyName, this);
+        }
+
+        public RepositorySource getRepositorySource() {
+            return source;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+    }
+
+    /**
+     * Local implementation of the {@link SequencerDetails} interface that tracks all of the user-provided configuration details.
+     */
+    protected class DnaSequencerDetails implements SequencerDetails {
+        private final Sequencer sequencer;
+        private String name;
+        private String description;
+        private Map<String, Object> properties;
+        private String className;
+        private String[] classpath;
+        final List<PathExpression> sourcePathExpressions;
+        final List<PathExpression> targetPathExpressions;
+
+        protected DnaSequencerDetails( String name,
+                                       Sequencer sequencer,
+                                       String[] classpath ) {
+            this.sequencer = sequencer;
+            this.name = name;
+            this.description = sequencer.getClass().getName();
+            this.properties = new HashMap<String, Object>();
+            this.className = sequencer.getClass().getName();
+            this.classpath = classpath;
+            this.sourcePathExpressions = new ArrayList<PathExpression>();
+            this.targetPathExpressions = new ArrayList<PathExpression>();
+        }
+
+        public Map<String, Object> getProperties() {
+            return properties;
+        }
+
+        protected String getDescription() {
+            return description;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public SequencerDetails describedAs( String description ) {
+            this.description = description;
+            return this;
+        }
+
+        public PropertySetter<SequencerDetails> with( final String propertyName ) {
+            return new MappingPropertySetter<SequencerDetails>(propertyName, this);
+        }
+
+        public Sequencer getSequencer() {
+            return sequencer;
+        }
+
+        SequencerConfig getSequencerConfig() {
+            return new SequencerConfig(this.name, this.description, this.properties, this.className, this.classpath);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(java.lang.String)
+         */
+        public PathExpressionOutput sequencingFrom( String inputExpressionPath ) {
+            return this.sequencingFrom(PathExpression.compile(inputExpressionPath));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
+         */
+        public PathExpressionOutput sequencingFrom( final PathExpression inputExpressionPath ) {
+            final DnaSequencerDetails details = this;
+            return new PathExpressionOutput() {
+
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.repository.DnaConfiguration.PathExpressionOutput#andOutputtingTo(java.lang.String)
+                 */
+                public DnaSequencerDetails andOutputtingTo( final String outputExpressionPath ) {
+                    details.sourcePathExpressions.add(inputExpressionPath);
+                    details.targetPathExpressions.add(PathExpression.compile(outputExpressionPath));
+                    return details;
+                }
+            };
+        }
+
+    }
+
+    /**
+     * Interface used to configure a {@link Sequencer sequencer}.
+     */
+    public interface SequencerDetails extends SetDescription<SequencerDetails>, ConfigurationBuilder {
+
+        /**
+         * Specify the input {@link PathExpression path expression} represented as a string, which determines when this sequencer
+         * will be executed.
+         * 
+         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
+         *        sequencer
+         * @return the interface used to specify the output path expression; never null
+         */
+        PathExpressionOutput sequencingFrom( String inputPathExpression );
+
+        /**
+         * Specify the input {@link PathExpression path expression}, which determines when this sequencer will be executed.
+         * 
+         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
+         *        sequencer
+         * @return the interface used to specify the output path expression; never null
+         */
+        PathExpressionOutput sequencingFrom( PathExpression inputPathExpression );
+    }
+
+    /**
+     * Interface used to specify the output path expression for a {@link SequencerDetails#sequencingFrom(PathExpression) sequencer
+     * configuration}.
+     */
+    public interface PathExpressionOutput {
+        /**
+         * Specify the output {@link PathExpression path expression}, which determines where this sequencer's output will be
+         * placed.
+         * 
+         * @param outputExpression the path expression for the location(s) where output generated by the sequencer is to be placed
+         * @return the interface used to continue specifying the configuration of the sequencer
+         */
+        SequencerDetails andOutputtingTo( String outputExpression );
+    }
+
+    /**
+     * Interface used to configure a {@link MimeTypeDetector MIME type detector}.
+     */
+    public interface MimeTypeDetectorDetails
+        extends SetDescription<MimeTypeDetectorDetails>, SetProperties<MimeTypeDetectorDetails>, ConfigurationBuilder {
+    }
+
+    /**
+     * Interface for configuring the JavaBean-style properties of an object.
+     * 
+     * @param <ReturnType> the interface returned after the property has been set.
+     * @author Randall Hauch
+     */
+    public interface SetProperties<ReturnType> {
+        /**
+         * Specify the name of the JavaBean-style property that is to be set. The value may be set using the interface returned by
+         * this method.
+         * 
+         * @param beanPropertyName the name of the JavaBean-style property (e.g., "retryLimit")
+         * @return the interface used to set the value for the property; never null
+         */
+        PropertySetter<ReturnType> with( String beanPropertyName );
+    }
+
+    /**
+     * The interface used to set the value for a JavaBean-style property.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     * @author Randall Hauch
+     * @see SetProperties#with(String)
+     */
+    public interface PropertySetter<ReturnType> {
+        /**
+         * Set the property value to an integer.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( int value );
+
+        /**
+         * Set the property value to a long number.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( long value );
+
+        /**
+         * Set the property value to a short.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( short value );
+
+        /**
+         * Set the property value to a boolean.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( boolean value );
+
+        /**
+         * Set the property value to a float.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( float value );
+
+        /**
+         * Set the property value to a double.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( double value );
+
+        /**
+         * Set the property value to a string.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( String value );
+
+        /**
+         * Set the property value to an object.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( Object value );
+    }
+
+    /**
+     * The interface used to configure the class used for a component.
+     * 
+     * @param <ComponentClassType> the class or interface that the component is to implement
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface ChooseClass<ComponentClassType, ReturnType> {
+
+        /**
+         * Specify the name of the class that should be instantiated for the instance. The classpath information will need to be
+         * defined using the returned interface.
+         * 
+         * @param classname the name of the class that should be instantiated
+         * @return the interface used to define the classpath information; never null
+         * @throws IllegalArgumentException if the class name is null, empty, blank, or not a valid class name
+         */
+        LoadedFrom<ReturnType> usingClass( String classname );
+
+        /**
+         * Specify the class that should be instantiated for the instance. Because the class is already available to this class
+         * loader, there is no need to specify the classloader information.
+         * 
+         * @param clazz the class that should be instantiated
+         * @return the next component to continue configuration; never null
+         * @throws IllegalArgumentException if the class reference is null
+         */
+        ReturnType usingClass( Class<? extends ComponentClassType> clazz );
+    }
+
+    /**
+     * The interface used to set a description on a component.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface SetDescription<ReturnType> {
+        /**
+         * Specify the description of this component.
+         * 
+         * @param description the description; may be null or empty
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType describedAs( String description );
+    }
+
+    /**
+     * Interface for specifying from where the component's class is to be loaded.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface LoadedFrom<ReturnType> {
+        /**
+         * Specify the names of the classloaders that form the classpath for the component, from which the component's class (and
+         * its dependencies) can be loaded. The names correspond to the names supplied to the
+         * {@link ExecutionContext#getClassLoader(String...)} methods.
+         * 
+         * @param classPathNames the names for the classloaders, as passed to the {@link ClassLoaderFactory} implementation (e.g.,
+         *        the {@link ExecutionContext}).
+         * @return the next component to continue configuration; never null
+         * @see #loadedFromClasspath()
+         * @see ExecutionContext#getClassLoader(String...)
+         */
+        ReturnType loadedFrom( String... classPathNames );
+
+        /**
+         * Specify that the component (and its dependencies) will be found on the current (or
+         * {@link Thread#getContextClassLoader() current context}) classloader.
+         * 
+         * @return the next component to continue configuration; never null
+         * @see #loadedFrom(String...)
+         * @see ExecutionContext#getClassLoader(String...)
+         */
+        ReturnType loadedFromClasspath();
+    }
+
+    /**
+     * Interface for classes that can return a reference to their DNA configuration.
+     */
+    public interface ConfigurationBuilder {
+
+        Map<String, Object> getProperties();
+
+        /**
+         * Return a reference to the enclosing configuration so that it can be built or further configured.
+         * 
+         * @return a reference to the enclosing configuration
+         */
+        DnaConfiguration and();
+    }
+
+    /**
+     * Abstract implementation of {@link ChooseClass} that has a single abstract method to obtain the interface that should be
+     * returned when the class name and classpath have been selected.
+     * 
+     * @param <ComponentClass> the type of the component that is being chosen
+     * @param <ReturnType> the interface that should be returned when the class name and classpath have been chosen.
+     * @author Randall Hauch
+     */
+    protected abstract class ClassChooser<ComponentClass, ReturnType> implements ChooseClass<ComponentClass, ReturnType> {
+
+        protected abstract ReturnType getComponentBuilder( String className,
+                                                           String... classpath );
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ChooseClass#usingClass(java.lang.String)
+         */
+        public LoadedFrom<ReturnType> usingClass( final String classname ) {
+            CheckArg.isNotEmpty(classname, "classname");
+            return new LoadedFrom<ReturnType>() {
+                public ReturnType loadedFromClasspath() {
+                    return getComponentBuilder(classname);
+                }
+
+                public ReturnType loadedFrom( String... classpath ) {
+                    return getComponentBuilder(classname, classpath);
+                }
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ChooseClass#usingClass(java.lang.Class)
+         */
+        public ReturnType usingClass( Class<? extends ComponentClass> clazz ) {
+            CheckArg.isNotNull(clazz, "clazz");
+            return getComponentBuilder(clazz.getName());
+        }
+    }
+
+    /**
+     * Utility method to instantiate a class.
+     * 
+     * @param <T> the interface or superclass that the instantiated object is to implement
+     * @param interfaceType the interface or superclass type that the instantiated object is to implement
+     * @param className the name of the class
+     * @param classloaderNames the names of the class loaders
+     * @return the new instance
+     */
+    @SuppressWarnings( "unchecked" )
+    protected <T> T instantiate( Class<T> interfaceType,
+                                 String className,
+                                 String... classloaderNames ) {
+        // Load the class and create the instance ...
+        try {
+            Class<?> clazz = getExecutionContext().getClassLoader(classloaderNames).loadClass(className);
+            return (T)clazz.newInstance();
+        } catch (Throwable err) {
+            I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
+            throw new DnaConfigurationException(msg.text(className, err.getMessage()), err);
+        }
+    }
+
+    /**
+     * Reusable implementation of {@link PropertySetter} that sets the JavaBean-style property using reflection.
+     * 
+     * @param <ReturnType>
+     * @author Randall Hauch
+     */
+    protected class BeanPropertySetter<ReturnType> implements PropertySetter<ReturnType> {
+        private final Object javaBean;
+        private final String beanPropertyName;
+        private final ReturnType returnObject;
+
+        protected BeanPropertySetter( Object javaBean,
+                                      String beanPropertyName,
+                                      ReturnType returnObject ) {
+            assert javaBean != null;
+            assert beanPropertyName != null;
+            assert returnObject != null;
+            this.javaBean = javaBean;
+            this.beanPropertyName = beanPropertyName;
+            this.returnObject = returnObject;
+        }
+
+        public ReturnType setTo( boolean value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( int value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( long value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( short value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( float value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( double value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( String value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( Object value ) {
+            // Set the JavaBean-style property on the RepositorySource instance ...
+            Reflection reflection = new Reflection(javaBean.getClass());
+            try {
+                reflection.invokeSetterMethodOnTarget(beanPropertyName, javaBean, value);
+            } catch (Throwable err) {
+                I18n msg = RepositoryI18n.errorSettingJavaBeanPropertyOnInstanceOfClass;
+                throw new DnaConfigurationException(msg.text(beanPropertyName, javaBean.getClass(), err.getMessage()), err);
+            }
+            return returnObject;
+        }
+    }
+
+    /**
+     * Reusable implementation of {@link PropertySetter} that aggregates the properties to set to be placed into a map
+     * 
+     * @param <ReturnType>
+     * @author Randall Hauch
+     */
+    protected class MappingPropertySetter<ReturnType extends ConfigurationBuilder> implements PropertySetter<ReturnType> {
+        private final String beanPropertyName;
+        private final ReturnType returnObject;
+
+        protected MappingPropertySetter( String beanPropertyName,
+                                         ReturnType returnObject ) {
+            assert beanPropertyName != null;
+            assert returnObject != null;
+            this.beanPropertyName = beanPropertyName;
+            this.returnObject = returnObject;
+        }
+
+        public ReturnType setTo( boolean value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( int value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( long value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( short value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( float value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( double value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( String value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( Object value ) {
+            returnObject.getProperties().put(beanPropertyName, value);
+            return returnObject;
+        }
+    }
+}


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

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfigurationException.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfigurationException.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfigurationException.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.repository;
+
+/**
+ * @author Randall Hauch
+ */
+public class DnaConfigurationException extends RuntimeException {
+
+    /**
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 
+     */
+    public DnaConfigurationException() {
+    }
+
+    /**
+     * @param message
+     */
+    public DnaConfigurationException( String message ) {
+        super(message);
+
+    }
+
+    /**
+     * @param cause
+     */
+    public DnaConfigurationException( Throwable cause ) {
+        super(cause);
+
+    }
+
+    /**
+     * @param message
+     * @param cause
+     */
+    public DnaConfigurationException( String message,
+                                      Throwable cause ) {
+        super(message, cause);
+
+    }
+
+}


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

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -0,0 +1,152 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.repository;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+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.repository.DnaConfiguration.DnaMimeTypeDetectorDetails;
+import org.jboss.dna.repository.DnaConfiguration.DnaRepositoryDetails;
+import org.jboss.dna.repository.DnaConfiguration.DnaSequencerDetails;
+import org.jboss.dna.repository.observation.ObservationService;
+import org.jboss.dna.repository.sequencer.SequencingService;
+import org.jboss.dna.repository.service.AdministeredService;
+
+/**
+ * @author Randall Hauch
+ */
+ at Immutable
+public class DnaEngine {
+
+    public static final String CONFIGURATION_REPOSITORY_NAME = "dna:configuration";
+
+    private final ExecutionContext context;
+    private final List<AdministeredService> services;
+
+    private final RepositoryService repositoryService;
+    private final ObservationService observationService;
+    private final SequencingService sequencingService;
+    private final ExecutorService executorService;
+
+    private final RepositoryConnectionFactory connectionFactory;
+
+    DnaEngine( DnaConfiguration configuration ) {
+        this.context = new ExecutionContext();
+
+        DnaRepositoryDetails configSource = configuration.repositories.get(CONFIGURATION_REPOSITORY_NAME);
+        assert configSource != null : "Must specify a repository source named " + CONFIGURATION_REPOSITORY_NAME;
+
+        // Use a magic number for now
+        executorService = new ScheduledThreadPoolExecutor(10);
+
+        observationService = null; // new ObservationService(null);
+
+        RepositoryLibrary library = new RepositoryLibrary();
+        for (DnaRepositoryDetails details : configuration.repositories.values()) {
+            // Adding configuration source to the library until proven wrong!
+            library.addSource(details.getRepositorySource());
+        }
+
+        for (DnaMimeTypeDetectorDetails details : configuration.mimeTypeDetectors.values()) {
+            library.getMimeTypeDetectors().addDetector(details.getMimeTypeDetectorConfig());
+        }
+        repositoryService = new RepositoryService(library, configSource.getRepositorySource().getName(), "", context);
+
+        sequencingService = new SequencingService();
+        sequencingService.setExecutorService(executorService);
+        for (DnaSequencerDetails details : configuration.sequencers.values()) {
+            sequencingService.addSequencer(details.getSequencerConfig());
+        }
+
+        this.services = Arrays.asList(new AdministeredService[] { /* observationService, */repositoryService, sequencingService,});
+
+        connectionFactory = new RepositoryConnectionFactory() {
+            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+                RepositorySource source = DnaEngine.this.getRepositorySource(sourceName);
+                if (sourceName == null) {
+                    throw new RepositorySourceException(sourceName);
+                }
+
+                return source.getConnection();
+            }
+        };
+    }
+
+    /*
+     * Lookup methods
+     */
+    public final ExecutionContext getExecutionContext() {
+        return context;
+    }
+
+    public final RepositorySource getRepositorySource( String repositoryName ) {
+        return repositoryService.getRepositorySourceManager().getSource(repositoryName);
+    }
+
+    public final RepositoryConnectionFactory getRepositoryConnectionFactory() {
+        return connectionFactory;
+    }
+
+    public final RepositoryService getRepositoryService() {
+        return repositoryService;
+    }
+
+    public final ObservationService getObservationService() {
+        return observationService;
+    }
+
+    public final SequencingService getSequencingService() {
+        return sequencingService;
+    }
+
+    /*
+     * Lifecycle methods
+     */
+
+    public void start() {
+        for (AdministeredService service : services) {
+            service.getAdministrator().start();
+        }
+    }
+
+    public void shutdown() {
+        for (AdministeredService service : services) {
+            service.getAdministrator().shutdown();
+        }
+
+        try {
+            executorService.awaitTermination(10 * 60, TimeUnit.SECONDS); // No TimeUnit.MINUTES in JDK 5
+        } catch (InterruptedException ie) {
+            // Reset the thread's status and continue this method ...
+            Thread.interrupted();
+        }
+        executorService.shutdown();
+    }
+}


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

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2009-04-22 13:38:14 UTC (rev 845)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -1,137 +1,142 @@
-/*
- * 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.repository;
-
-import java.util.Locale;
-import java.util.Set;
-import org.jboss.dna.common.i18n.I18n;
-
-/**
- * @author Randall Hauch
- * @author John Verhaeg
- */
-public final class RepositoryI18n {
-
-    // Services and Repository
-    public static I18n invalidStateString;
-    public static I18n serviceShutdowAndMayNotBeStarted;
-    public static I18n serviceShutdowAndMayNotBePaused;
-    public static I18n serviceNotShutdowAndMayNotBeTerminated;
-    public static I18n unableToFindRepositoryInJndi;
-    public static I18n unableToRegisterRepositoryInJndi;
-    public static I18n unableToUnregisterRepositoryInJndi;
-    public static I18n unableToRemoveRepository;
-    public static I18n unableToFindRepositoryWithName;
-    public static I18n errorProcessingEvents;
-    public static I18n errorFindingPropertyNameInPropertyAddedEvent;
-    public static I18n errorFindingPropertyNameInPropertyChangedEvent;
-    public static I18n errorFindingPropertyNameInPropertyRemovedEvent;
-
-    // Rules
-    public static I18n unableToObtainJsr94RuleAdministrator;
-    public static I18n errorUsingJsr94RuleAdministrator;
-    public static I18n unableToObtainJsr94ServiceProvider;
-    public static I18n errorAddingOrUpdatingRuleSet;
-    public static I18n errorRollingBackRuleSetAfterUpdateFailed;
-    public static I18n errorReadingRulesAndProperties;
-    public static I18n errorDeregisteringRuleSetBeforeUpdatingIt;
-    public static I18n errorRecreatingRuleSet;
-    public static I18n errorRemovingRuleSet;
-    public static I18n errorRemovingRuleSetUponShutdown;
-    public static I18n unableToFindRuleSet;
-    public static I18n errorExecutingRuleSetWithGlobalsAndFacts;
-    public static I18n unableToBuildRuleSetRegularExpressionPattern;
-
-    public static I18n errorObtainingSessionToRepositoryWorkspace;
-    public static I18n errorWritingProblemsOnRuleSet;
-
-    public static I18n federationServiceName;
-    public static I18n observationServiceName;
-    public static I18n ruleServiceName;
-
-    // Sequencing
-    public static I18n sequencingServiceName;
-    public static I18n unableToChangeExecutionContextWhileRunning;
-    public static I18n unableToStartSequencingServiceWithoutExecutionContext;
-    public static I18n errorWhileSequencingNode;
-    public static I18n errorInRepositoryWhileSequencingNode;
-    public static I18n errorFindingSequencersToRunAgainstNode;
-    public static I18n errorInRepositoryWhileFindingSequencersToRunAgainstNode;
-    public static I18n executionContextHasBeenClosed;
-    public static I18n unableToFindPropertyForSequencing;
-
-    // Properties
-    public static I18n errorReadingPropertiesFromContainerNode;
-    public static I18n requiredPropertyOnNodeWasExpectedToBeStringValue;
-    public static I18n optionalPropertyOnNodeWasExpectedToBeStringValue;
-    public static I18n requiredPropertyOnNodeWasExpectedToBeStringArrayValue;
-    public static I18n optionalPropertyOnNodeWasExpectedToBeStringArrayValue;
-    public static I18n requiredPropertyOnNodeCouldNotBeRead;
-    public static I18n optionalPropertyOnNodeCouldNotBeRead;
-    public static I18n requiredPropertyIsMissingFromNode;
-    public static I18n errorGettingRequiredPropertyFromNode;
-    public static I18n errorGettingOptionalPropertyFromNode;
-    public static I18n errorClosingBinaryStreamForPropertyFromNode;
-    public static I18n requiredNodeDoesNotExistRelativeToNode;
-    public static I18n errorGettingNodeRelativeToNode;
-    public static I18n unknownPropertyValueType;
-
-    // Path expressions
-    public static I18n pathExpressionIsInvalid;
-    public static I18n pathExpressionMayNotBeBlank;
-    public static I18n pathExpressionHasInvalidSelect;
-    public static I18n pathExpressionHasInvalidMatch;
-
-    // Observation
-    public static I18n errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService;
-
-    // General
-    public static I18n invalidRepositoryNodePath;
-    public static I18n unableToLoadClassUsingClasspath;
-    public static I18n unableToInstantiateClassUsingClasspath;
-    public static I18n unableToAccessClassUsingClasspath;
-
-    // Repository
-    public static I18n errorStartingRepositoryService;
-
-    static {
-        try {
-            I18n.initialize(RepositoryI18n.class);
-        } catch (final Exception err) {
-            System.err.println(err);
-        }
-    }
-
-    public static Set<Locale> getLocalizationProblemLocales() {
-        return I18n.getLocalizationProblemLocales(RepositoryI18n.class);
-    }
-
-    public static Set<String> getLocalizationProblems() {
-        return I18n.getLocalizationProblems(RepositoryI18n.class);
-    }
-
-    public static Set<String> getLocalizationProblems( Locale locale ) {
-        return I18n.getLocalizationProblems(RepositoryI18n.class, locale);
-    }
-}
+/*
+ * 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.repository;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ * @author John Verhaeg
+ */
+public final class RepositoryI18n {
+
+    // Configuration
+    public static I18n errorCreatingInstanceOfClass;
+    public static I18n errorCreatingInstanceOfClassUsingClassLoaders;
+    public static I18n errorSettingJavaBeanPropertyOnInstanceOfClass;
+
+    // Services and Repository
+    public static I18n invalidStateString;
+    public static I18n serviceShutdowAndMayNotBeStarted;
+    public static I18n serviceShutdowAndMayNotBePaused;
+    public static I18n serviceNotShutdowAndMayNotBeTerminated;
+    public static I18n unableToFindRepositoryInJndi;
+    public static I18n unableToRegisterRepositoryInJndi;
+    public static I18n unableToUnregisterRepositoryInJndi;
+    public static I18n unableToRemoveRepository;
+    public static I18n unableToFindRepositoryWithName;
+    public static I18n errorProcessingEvents;
+    public static I18n errorFindingPropertyNameInPropertyAddedEvent;
+    public static I18n errorFindingPropertyNameInPropertyChangedEvent;
+    public static I18n errorFindingPropertyNameInPropertyRemovedEvent;
+
+    // Rules
+    public static I18n unableToObtainJsr94RuleAdministrator;
+    public static I18n errorUsingJsr94RuleAdministrator;
+    public static I18n unableToObtainJsr94ServiceProvider;
+    public static I18n errorAddingOrUpdatingRuleSet;
+    public static I18n errorRollingBackRuleSetAfterUpdateFailed;
+    public static I18n errorReadingRulesAndProperties;
+    public static I18n errorDeregisteringRuleSetBeforeUpdatingIt;
+    public static I18n errorRecreatingRuleSet;
+    public static I18n errorRemovingRuleSet;
+    public static I18n errorRemovingRuleSetUponShutdown;
+    public static I18n unableToFindRuleSet;
+    public static I18n errorExecutingRuleSetWithGlobalsAndFacts;
+    public static I18n unableToBuildRuleSetRegularExpressionPattern;
+
+    public static I18n errorObtainingSessionToRepositoryWorkspace;
+    public static I18n errorWritingProblemsOnRuleSet;
+
+    public static I18n federationServiceName;
+    public static I18n observationServiceName;
+    public static I18n ruleServiceName;
+
+    // Sequencing
+    public static I18n sequencingServiceName;
+    public static I18n unableToChangeExecutionContextWhileRunning;
+    public static I18n unableToStartSequencingServiceWithoutExecutionContext;
+    public static I18n errorWhileSequencingNode;
+    public static I18n errorInRepositoryWhileSequencingNode;
+    public static I18n errorFindingSequencersToRunAgainstNode;
+    public static I18n errorInRepositoryWhileFindingSequencersToRunAgainstNode;
+    public static I18n executionContextHasBeenClosed;
+    public static I18n unableToFindPropertyForSequencing;
+
+    // Properties
+    public static I18n errorReadingPropertiesFromContainerNode;
+    public static I18n requiredPropertyOnNodeWasExpectedToBeStringValue;
+    public static I18n optionalPropertyOnNodeWasExpectedToBeStringValue;
+    public static I18n requiredPropertyOnNodeWasExpectedToBeStringArrayValue;
+    public static I18n optionalPropertyOnNodeWasExpectedToBeStringArrayValue;
+    public static I18n requiredPropertyOnNodeCouldNotBeRead;
+    public static I18n optionalPropertyOnNodeCouldNotBeRead;
+    public static I18n requiredPropertyIsMissingFromNode;
+    public static I18n errorGettingRequiredPropertyFromNode;
+    public static I18n errorGettingOptionalPropertyFromNode;
+    public static I18n errorClosingBinaryStreamForPropertyFromNode;
+    public static I18n requiredNodeDoesNotExistRelativeToNode;
+    public static I18n errorGettingNodeRelativeToNode;
+    public static I18n unknownPropertyValueType;
+
+    // Path expressions
+    public static I18n pathExpressionIsInvalid;
+    public static I18n pathExpressionMayNotBeBlank;
+    public static I18n pathExpressionHasInvalidSelect;
+    public static I18n pathExpressionHasInvalidMatch;
+
+    // Observation
+    public static I18n errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService;
+
+    // General
+    public static I18n invalidRepositoryNodePath;
+    public static I18n unableToLoadClassUsingClasspath;
+    public static I18n unableToInstantiateClassUsingClasspath;
+    public static I18n unableToAccessClassUsingClasspath;
+
+    // Repository
+    public static I18n errorStartingRepositoryService;
+
+    static {
+        try {
+            I18n.initialize(RepositoryI18n.class);
+        } catch (final Exception err) {
+            System.err.println(err);
+        }
+    }
+
+    public static Set<Locale> getLocalizationProblemLocales() {
+        return I18n.getLocalizationProblemLocales(RepositoryI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems() {
+        return I18n.getLocalizationProblems(RepositoryI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems( Locale locale ) {
+        return I18n.getLocalizationProblems(RepositoryI18n.class, locale);
+    }
+}

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/mimetype/MimeTypeDetectorConfig.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/mimetype/MimeTypeDetectorConfig.java	2009-04-22 13:38:14 UTC (rev 845)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/mimetype/MimeTypeDetectorConfig.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.repository.mimetype;
 
+import java.util.Map;
 import org.jboss.dna.common.component.ComponentConfig;
 
 /**
@@ -32,8 +33,9 @@
 
     public MimeTypeDetectorConfig( String name,
                            String description,
+                           Map<String, Object> properties,
                            String classname,
                            String[] classpath ) {
-        super(name, description, classname, classpath);
+        super(name, description, properties, classname, classpath);
     }
 }

Modified: trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
===================================================================
--- trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2009-04-22 13:38:14 UTC (rev 845)
+++ trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2009-04-22 14:05:41 UTC (rev 846)
@@ -21,6 +21,10 @@
 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 #
+errorCreatingInstanceOfClass = Attempt to create instance of {0} resulted in error: {1}
+errorCreatingInstanceOfClassUsingClassLoaders = Attempt to create instance of {0} using classloader path {1} resulted in error: {2}
+errorSettingJavaBeanPropertyOnInstanceOfClass = Attempt to set {0} property on {1} instance resulted in error: {2}
+
 invalidStateString = Invalid state parameter "{0}"
 serviceShutdowAndMayNotBeStarted = The {0} has been shutdown and may not be (re)started
 serviceShutdowAndMayNotBePaused = The {0} has been shutdown and my not be paused

Added: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	                        (rev 0)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -0,0 +1,215 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.repository;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.repository.DnaConfiguration.DnaSequencerDetails;
+import org.jboss.dna.repository.sequencer.MockSequencerA;
+import org.jboss.dna.repository.sequencer.MockSequencerB;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class DnaConfigurationTest {
+
+    private ExecutionContext context;
+    private DnaConfiguration configuration;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        configuration = new DnaConfiguration();
+    }
+
+    @Test
+    public void shouldAllowCreatingWithNoArguments() {
+        configuration = new DnaConfiguration();
+    }
+
+    @Test
+    public void shouldAllowCreatingWithSpecifiedExecutionContext() {
+        configuration = new DnaConfiguration(context);
+    }
+
+    @Test
+    public void shouldAllowSpecifyingConfigurationRepository() {
+        DnaConfiguration config = configuration.withConfigurationRepository()
+                                               .usingClass("org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource")
+                                               .loadedFromClasspath()
+                                               .describedAs("description")
+                                               .with("retryLimit")
+                                               .setTo(5)
+                                               .with("name")
+                                               .setTo("repository name")
+                                               .and();
+        assertThat(config, is(notNullValue()));
+        assertThat(config.repositories.get(null), is(nullValue()));
+    }
+
+    @Test
+    public void shouldAllowAddingRepositorySourceInstance() {
+        RepositorySource newSource = mock(RepositorySource.class);
+        configuration.addRepository(newSource);
+    }
+
+    @Test
+    public void shouldAllowAddingRepositorySourceByClassNameAndSettingProperties() {
+        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
+                                               .usingClass("org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource")
+                                               .loadedFromClasspath()
+                                               .describedAs("description")
+                                               .with("retryLimit")
+                                               .setTo(5)
+                                               .with("name")
+                                               .setTo("repository name")
+                                               .and();
+        assertThat(config, is(notNullValue()));
+
+        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
+        assertThat(source, is(notNullValue()));
+        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
+
+        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
+        assertThat(isource.getRetryLimit(), is(5));
+        assertThat(isource.getName(), is("repository name"));
+    }
+
+    @Test
+    public void shouldAllowAddingRepositorySourceByClassReferenceAndSettingProperties() {
+        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
+                                               .usingClass(InMemoryRepositorySource.class)
+                                               .describedAs("description")
+                                               .with("retryLimit")
+                                               .setTo(5)
+                                               .with("name")
+                                               .setTo("repository name")
+                                               .and();
+
+        assertThat(config, is(notNullValue()));
+        assertThat(config.repositories.get(null), is(nullValue()));
+
+        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
+        assertThat(source, is(notNullValue()));
+        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
+
+        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
+        assertThat(isource.getRetryLimit(), is(5));
+        assertThat(isource.getName(), is("repository name"));
+    }
+
+    @Test
+    public void shouldAllowOverwritingRepositorySourceByRepositoryName() {
+        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
+                                               .usingClass(InMemoryRepositorySource.class)
+                                               .describedAs("description")
+                                               .with("retryLimit")
+                                               .setTo(3)
+                                               .and()
+                                               .addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
+                                               .usingClass(InMemoryRepositorySource.class)
+                                               .describedAs("description")
+                                               .with("name")
+                                               .setTo("repository name")
+                                               .and();
+
+        assertThat(config, is(notNullValue()));
+        assertThat(config.repositories.get(null), is(nullValue()));
+
+        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
+        assertThat(source, is(notNullValue()));
+        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
+
+        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
+        // This relies on the default retry limit for an InMemoryRepositorySource being 0
+        // If the original source was not overwritten, this would be 3
+        assertThat(isource.getRetryLimit(), is(0));
+        assertThat(isource.getName(), is("repository name"));
+    }
+
+    @Test
+    public void shouldAllowAddingMimeTypeDetector() {
+        RepositorySource newSource = mock(RepositorySource.class);
+        MimeTypeDetector newDetector = mock(MimeTypeDetector.class);
+        DnaConfiguration config = configuration.addRepository(newSource)
+                                               .addMimeTypeDetector("default")
+                                               .usingClass(newDetector.getClass())
+                                               .describedAs("default mime type detector")
+                                               .and();
+
+        assertThat(config, is(notNullValue()));
+        assertThat(config.mimeTypeDetectors.get("default").getMimeTypeDetector(), instanceOf(MimeTypeDetector.class));
+        assertThat(config.mimeTypeDetectors.get("invalid name"), is(nullValue()));
+        assertThat(config.mimeTypeDetectors.get("default").getDescription(), is("default mime type detector"));
+    }
+
+    @Test
+    public void shouldAllowAddingSequencer() {
+        RepositorySource newSource = mock(RepositorySource.class);
+
+        DnaConfiguration config = configuration.addRepository(newSource)
+                                               .addSequencer("sequencerA")
+                                               .usingClass(MockSequencerA.class)
+                                               .describedAs("Mock Sequencer A")
+                                               .sequencingFrom("/foo/source")
+                                               .andOutputtingTo("/foo/target")
+                                               .sequencingFrom("/bar/source")
+                                               .andOutputtingTo("/bar/target")
+                                               .and()
+                                               .addSequencer("sequencerB")
+                                               .usingClass(MockSequencerB.class)
+                                               .sequencingFrom("/baz/source")
+                                               .andOutputtingTo("/baz/target")
+                                               .describedAs("Mock Sequencer B")
+                                               .and();
+
+        assertThat(config, is(notNullValue()));
+        assertThat(config.sequencers.get("default"), is(nullValue()));
+        assertThat(config.sequencers.get("sequencerA").getSequencer(), instanceOf(MockSequencerA.class));
+        assertThat(config.sequencers.get("sequencerB").getSequencer(), instanceOf(MockSequencerB.class));
+
+        DnaSequencerDetails detailsA = config.sequencers.get("sequencerA");
+        assertThat(detailsA.getDescription(), is("Mock Sequencer A"));
+        assertThat(detailsA.sourcePathExpressions.size(), is(2));
+        assertThat(detailsA.sourcePathExpressions.get(0).toString(), is("/foo/source"));
+        assertThat(detailsA.targetPathExpressions.get(0).toString(), is("/foo/target"));
+        assertThat(detailsA.sourcePathExpressions.get(1).toString(), is("/bar/source"));
+        assertThat(detailsA.targetPathExpressions.get(1).toString(), is("/bar/target"));
+
+        DnaSequencerDetails detailsB = config.sequencers.get("sequencerB");
+        assertThat(detailsB.getDescription(), is("Mock Sequencer B"));
+        assertThat(detailsB.sourcePathExpressions.size(), is(1));
+        assertThat(detailsB.sourcePathExpressions.get(0).toString(), is("/baz/source"));
+        assertThat(detailsB.targetPathExpressions.get(0).toString(), is("/baz/target"));
+    }
+
+}


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

Added: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java	                        (rev 0)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -0,0 +1,191 @@
+/*
+ * 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.repository;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import javax.jcr.observation.Event;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.repository.mimetype.MimeTypeDetectors;
+import org.jboss.dna.repository.observation.NodeChanges;
+import org.jboss.dna.repository.sequencer.MockSequencerA;
+import org.jboss.dna.repository.sequencer.SequencingService;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+
+public class DnaEngineTest {
+
+    private DnaEngine engine;
+
+    @Before
+    public void beforeEach() {
+    }
+
+    @Test
+    public void shouldAllowCreatingWithEmptyConfig() {
+        engine = new DnaConfiguration().build();
+    }
+
+    @Test
+    public void shouldAllowCreatingWithConfigRepository() throws InterruptedException {
+        engine = new DnaConfiguration()
+            .withConfigurationRepository()
+            .usingClass(InMemoryRepositorySource.class)
+            .describedAs("Configuration Repository")
+            .with("name").setTo("config repo")
+            .and().build();
+
+        assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
+        assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
+
+        RepositoryLibrary library = engine.getRepositoryService().getRepositorySourceManager();
+        assertThat(library.getConnectionPool("config repo").getInUseCount(), is(0));
+
+        RepositoryConnection connection = library.getConnectionPool("config repo").getConnection();
+        assertThat(connection.ping(500, TimeUnit.MILLISECONDS), is(true));
+        connection.close();
+
+    }
+
+    @Test
+    public void shouldAllowCreatingMultipleRepositories() throws Exception {
+        engine = new DnaConfiguration()
+            .withConfigurationRepository()
+            .usingClass(InMemoryRepositorySource.class)
+            .describedAs("Configuration Repository")
+            .with("name").setTo("config repo")
+            .and().addRepository("JCR")
+            .usingClass(InMemoryRepositorySource.class)
+            .describedAs("Backing Repository for JCR Implementation")
+            .with("name").setTo("JCR")
+            .and().build();
+
+        assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
+        assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
+
+        assertThat(engine.getRepositorySource("JCR"), is(notNullValue()));
+        assertThat(engine.getRepositorySource("JCR"), is(instanceOf(InMemoryRepositorySource.class)));
+        assertThat(engine.getRepositorySource("JCR").getName(), is("JCR"));
+
+        RepositoryLibrary library = engine.getRepositoryService().getRepositorySourceManager();
+        RepositoryConnection connection = library.getConnectionPool("JCR").getConnection();
+        assertThat(connection.ping(500, TimeUnit.MILLISECONDS), is(true));
+        connection.close();
+
+    }
+
+    @Test
+    public void shouldAllowAddingMimeTypeDetectors() throws Exception {
+        engine = new DnaConfiguration()
+            .withConfigurationRepository()
+            .usingClass(InMemoryRepositorySource.class)
+            .describedAs("Configuration Repository")
+            .with("name").setTo("config repo")
+            .and().addMimeTypeDetector("default")
+            .usingClass(MockMimeTypeDetector.class)
+            .describedAs("Default MimeTypeDetector")
+            .with("mimeType").setTo("mock")
+            .and().build();
+
+        assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
+        assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
+
+        RepositoryLibrary library = engine.getRepositoryService().getRepositorySourceManager();
+        MimeTypeDetectors detectors = library.getMimeTypeDetectors();
+        
+        assertThat(detectors.mimeTypeOf("test", new ByteArrayInputStream("This is useless data".getBytes())), is("mock"));
+    }
+    
+    @Test
+    public void shouldAllowAddingSequencers() throws Exception {
+        engine = new DnaConfiguration()
+            .withConfigurationRepository()
+            .usingClass(InMemoryRepositorySource.class)
+            .describedAs("Configuration Repository")
+            .with("name").setTo("config repo")
+            .and().addSequencer("Mock Sequencer A")
+            .usingClass(MockSequencerA.class)
+            .describedAs("A Mock Sequencer")
+            .sequencingFrom("/**").andOutputtingTo("/")
+            .and().build();
+
+        assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
+        assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
+
+        SequencingService sequencer = engine.getSequencingService();
+        assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(0L));
+        
+        NodeChanges changes = NodeChanges.create("", Arrays.asList(new Event[] { }));
+        sequencer.onNodeChanges(changes);
+
+        assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(0L));
+
+        Event e1 = mock(Event.class);
+        stub(e1.getType()).toReturn(Event.NODE_ADDED);
+        stub(e1.getPath()).toReturn("/test");
+        stub(e1.getUserID()).toReturn("Test");
+        
+//        changes = NodeChanges.create("", Arrays.asList(new Event[] { e1, }));
+//        sequencer.onNodeChanges(changes);
+//        
+//        // Shutdown the engine to force all pending tasks to complete
+//        engine.shutdown();
+//        
+//        assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(1L));
+
+    }    
+
+    public static class MockMimeTypeDetector implements MimeTypeDetector {
+        private String mimeType = "";
+        
+        public MockMimeTypeDetector() {
+            
+        }
+
+        public void setMimeType(String mimeType) {
+            this.mimeType = mimeType;
+        }
+        
+        public String mimeTypeOf( String name,
+                                  InputStream is ) {
+            return mimeType;
+        }
+    }
+
+
+}


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

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/mimetype/AbstractMimeTypeTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/mimetype/AbstractMimeTypeTest.java	2009-04-22 13:38:14 UTC (rev 845)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/mimetype/AbstractMimeTypeTest.java	2009-04-22 14:05:41 UTC (rev 846)
@@ -28,6 +28,7 @@
 import static org.junit.Assert.assertThat;
 import java.io.File;
 import java.io.InputStream;
+import java.util.Collections;
 import org.jboss.dna.graph.mimetype.MimeTypeDetector;
 import org.junit.After;
 import org.junit.Before;
@@ -45,7 +46,7 @@
 
     protected AbstractMimeTypeTest( Class<? extends MimeTypeDetector> detector ) {
         assertThat(detector, notNullValue());
-        this.config = new MimeTypeDetectorConfig("MIME-Type Detector", "MIME-Type Detector", detector.getName(), null);
+        this.config = new MimeTypeDetectorConfig("MIME-Type Detector", "MIME-Type Detector", Collections.<String, Object>emptyMap(), detector.getName(), null);
     }
 
     @Before



From dna-commits at lists.jboss.org  Thu Apr 23 12:25:31 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 12:25:31 -0400
Subject: [dna-commits] DNA SVN: r847 -
	trunk/dna-common/src/main/java/org/jboss/dna/common/text.
Message-ID: <E1Lx1jv-0002kM-5l@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 12:25:30 -0400 (Thu, 23 Apr 2009)
New Revision: 847

Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/text/Inflector.java
Log:
DNA-380 The inflector is not lowercasing the first character in the 'camelCase(...)' method

Corrected the camelCase(...)  method to lowercase the first character per the JavaDoc.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/Inflector.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/Inflector.java	2009-04-22 14:05:41 UTC (rev 846)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/Inflector.java	2009-04-23 16:25:30 UTC (rev 847)
@@ -297,14 +297,13 @@
             return replaceAllWithUppercase(result, "(^|_)(.)", 2);
         }
         if (lowerCaseAndUnderscoredWord.length() < 2) return lowerCaseAndUnderscoredWord;
-        return "" + lowerCaseAndUnderscoredWord.charAt(0)
+        return "" + Character.toLowerCase(lowerCaseAndUnderscoredWord.charAt(0))
                + camelCase(lowerCaseAndUnderscoredWord, true, delimiterChars).substring(1);
     }
 
     /**
-     * Makes an underscored form from the expression in the string (the reverse of the
-     * {@link #camelCase(String, boolean, char[]) camelCase} method. Also changes any characters that match the supplied
-     * delimiters into underscore.
+     * Makes an underscored form from the expression in the string (the reverse of the {@link #camelCase(String, boolean, char[])
+     * camelCase} method. Also changes any characters that match the supplied delimiters into underscore.
      * <p>
      * Examples:
      * 
@@ -491,9 +490,9 @@
      * other backreferences.
      * <p>
      * The Java {@link Pattern regular expression processing} does not use the preprocessing directives <code>\l</code>,
-     * <code>&#92;u</code>, <code>\L</code>, and <code>\U</code>. If so, such directives could be used in the replacement
-     * string to uppercase or lowercase the backreferences. For example, <code>\L1</code> would lowercase the first
-     * backreference, and <code>&#92;u3</code> would uppercase the 3rd backreference.
+     * <code>&#92;u</code>, <code>\L</code>, and <code>\U</code>. If so, such directives could be used in the replacement string
+     * to uppercase or lowercase the backreferences. For example, <code>\L1</code> would lowercase the first backreference, and
+     * <code>&#92;u3</code> would uppercase the 3rd backreference.
      * </p>
      * 
      * @param input



From dna-commits at lists.jboss.org  Thu Apr 23 12:29:29 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 12:29:29 -0400
Subject: [dna-commits] DNA SVN: r848 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph.
Message-ID: <E1Lx1nl-0002wZ-2E@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 12:29:28 -0400 (Thu, 23 Apr 2009)
New Revision: 848

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
Log:
DNA-381 The Graph API should allow creating a node if it does not already exist

Added "createIfMissing(...)" methods that mirror the "create(...)" methods, but which use a different NodeConflictBehavior setting.

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-04-23 16:25:30 UTC (rev 847)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-04-23 16:29:28 UTC (rev 848)
@@ -945,7 +945,8 @@
     }
 
     /**
-     * Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
      * <p>
      * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
      * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
@@ -966,6 +967,131 @@
     }
 
     /**
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
+     * <p>
+     * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+     * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+     * parent or new node.
+     * </p>
+     * 
+     * @param atPath the path to the node that is to be created.
+     * @return an object that may be used to start another request
+     */
+    public GetNodeConjunction<Graph> createIfMissing( String atPath ) {
+        Path at = createPath(atPath);
+        Path parent = at.getParent();
+        Name child = at.getLastSegment().getName();
+        Location location = requests.createNode(Location.create(parent),
+                                                getCurrentWorkspaceName(),
+                                                child,
+                                                EMPTY_PROPERTIES,
+                                                NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+        return new GetNodeOrReturnGraph(location);
+    }
+
+    /**
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
+     * <p>
+     * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+     * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+     * parent or new node.
+     * </p>
+     * 
+     * @param at the path to the node that is to be created.
+     * @return an object that may be used to start another request
+     */
+    public GetNodeConjunction<Graph> createIfMissing( final Path at ) {
+        Path parent = at.getParent();
+        Name child = at.getLastSegment().getName();
+        Location location = requests.createNode(Location.create(parent),
+                                                getCurrentWorkspaceName(),
+                                                child,
+                                                EMPTY_PROPERTIES,
+                                                NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+        return new GetNodeOrReturnGraph(location);
+    }
+
+    /**
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
+     * <p>
+     * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+     * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+     * parent or new node.
+     * </p>
+     * 
+     * @param atPath the path to the node that is to be created.
+     * @param properties the properties for the new node
+     * @return an object that may be used to start another request
+     */
+    public GetNodeConjunction<Graph> createIfMissing( String atPath,
+                                                      Property... properties ) {
+        Path at = createPath(atPath);
+        Path parent = at.getParent();
+        Name child = at.getLastSegment().getName();
+        Location location = requests.createNode(Location.create(parent),
+                                                getCurrentWorkspaceName(),
+                                                child,
+                                                properties,
+                                                NodeConflictBehavior.UPDATE).getActualLocationOfNode();
+        return new GetNodeOrReturnGraph(location);
+    }
+
+    /**
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
+     * <p>
+     * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+     * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+     * parent or new node.
+     * </p>
+     * 
+     * @param at the path to the node that is to be created.
+     * @param properties the properties for the new node
+     * @return an object that may be used to start another request
+     */
+    public GetNodeConjunction<Graph> createIfMissing( Path at,
+                                                      Property... properties ) {
+        CheckArg.isNotNull(at, "at");
+        Path parent = at.getParent();
+        Name child = at.getLastSegment().getName();
+        Location location = requests.createNode(Location.create(parent),
+                                                getCurrentWorkspaceName(),
+                                                child,
+                                                properties,
+                                                NodeConflictBehavior.UPDATE).getActualLocationOfNode();
+        return new GetNodeOrReturnGraph(location);
+    }
+
+    /**
+     * Begin the request to create a node located at the supplied path, if the node does not exist. This request is submitted to
+     * the repository immediately.
+     * <p>
+     * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+     * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+     * parent or new node.
+     * </p>
+     * 
+     * @param at the path to the node that is to be created.
+     * @param properties the properties for the new node
+     * @return an object that may be used to start another request
+     */
+    public GetNodeConjunction<Graph> createIfMissing( Path at,
+                                                      Iterable<Property> properties ) {
+        CheckArg.isNotNull(at, "at");
+        Path parent = at.getParent();
+        Name child = at.getLastSegment().getName();
+        Location location = requests.createNode(Location.create(parent),
+                                                getCurrentWorkspaceName(),
+                                                child,
+                                                properties.iterator(),
+                                                NodeConflictBehavior.UPDATE).getActualLocationOfNode();
+        return new GetNodeOrReturnGraph(location);
+    }
+
+    /**
      * Begin the request to create a node under the existing parent node at the supplied location. Use this method if you are
      * creating a node when you have the {@link Location} of a parent from a previous request.
      * <p>
@@ -2901,11 +3027,12 @@
 
                 public On<BatchConjunction> to( Object firstValue,
                                                 Object... otherValues ) {
-                    firstValue = convertReferenceValue(firstValue);
+                    Object[] values = new Object[otherValues.length + 1];
+                    values[0] = convertReferenceValue(firstValue);
                     for (int i = 0, len = otherValues.length; i != len; ++i) {
-                        otherValues[i] = convertReferenceValue(otherValues[i]);
+                        values[i + 1] = convertReferenceValue(otherValues[i]);
                     }
-                    return set(getContext().getPropertyFactory().create(propertyName, firstValue, otherValues));
+                    return set(getContext().getPropertyFactory().create(propertyName, values));
                 }
 
                 public On<BatchConjunction> to( Iterable<?> values ) {
@@ -4699,6 +4826,37 @@
     public interface BatchConjunction extends Conjunction<Batch>, Executable<Node> {
     }
 
+    public interface GetNodeConjunction<Next> extends Conjunction<Next> {
+        Node andReturn();
+    }
+
+    protected class GetNodeOrReturnGraph implements GetNodeConjunction<Graph> {
+        private final Location location;
+
+        GetNodeOrReturnGraph( Location location ) {
+            assert location != null;
+            this.location = location;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.Graph.Conjunction#and()
+         */
+        public Graph and() {
+            return Graph.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.Graph.GetNodeConjunction#andReturn()
+         */
+        public Node andReturn() {
+            return and().getNodeAt(location);
+        }
+    }
+
     // ----------------------------------------------------------------------------------------------------------------
     // Node Implementation
     // ----------------------------------------------------------------------------------------------------------------



From dna-commits at lists.jboss.org  Thu Apr 23 12:43:27 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 12:43:27 -0400
Subject: [dna-commits] DNA SVN: r849 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 4 other directories.
Message-ID: <E1Lx21H-0007bQ-Ur@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 12:43:27 -0400 (Thu, 23 Apr 2009)
New Revision: 849

Added:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java
Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/Reflection.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java
   trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java
Log:
DNA-58 Create repository configuration and component

Refactored the DnaConfiguration to make use of the graph repository for storing the configuration.  When used, the DnaConfiguration instance persists all the information to this graph, where it can be read and used.  A "save()" method was added to allow the code using the DnaConfiguration instance to control when the content is updated.  (This also simplified the unit testing, since it's very easy to check the graph content after the DnaConfiguration methods are used.)  The DnaEngine was then updated to read the configuration repository content and to instantiate and set up the services.

This "configuration repository" defaults to an in-memory repository, but can be set up to use a particular RepositorySource instance (that has already been set up) or to reflectively create an instance given the class name, optional class loader, and various (implementation-specific) Java bean properties.  

This means that in the simplest case, one can simply create a DnaConfiguration and configure the sequencers, sources, detectors, etc.  It also makes it very simple to "read" an XML file with the configuration, since that just uses the XML graph importer.  But using a configuration repository shines in the more advanced cases, such as when a process participates in a cluster.  Here, the process can create a DnaConfiguration, very easily set it up to use a shared (and remote) configuration repository (that already has the content), and without any other work immediately build the DnaEngine.

Using a graph as the configuration repository also means that any changes to the configuration could be monitored via graph events, allowing the DnaEngine to monitor any changes and dynamically alter its services.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/Reflection.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/Reflection.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/Reflection.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -234,7 +234,7 @@
     }
 
     /**
-     * Find the method on the target class that matches the supplied method name.
+     * Find the methods on the target class that matches the supplied method name.
      * 
      * @param methodNamePattern the regular expression pattern for the name of the method that is to be found.
      * @return the Method objects that have a matching name, or an empty array if there are no methods that have a matching name.
@@ -252,6 +252,50 @@
     }
 
     /**
+     * Find the getter methods on the target class that begin with "get" or "is", that have no parameters, and that return
+     * something other than void. This method skips the {@link Object#getClass()} method.
+     * 
+     * @return the Method objects for the getters; never null but possibly empty
+     */
+    public Method[] findGetterMethods() {
+        final Method[] allMethods = this.targetClass.getMethods();
+        final List<Method> result = new ArrayList<Method>();
+        for (int i = 0; i < allMethods.length; i++) {
+            final Method m = allMethods[i];
+            int numParams = m.getParameterTypes().length;
+            if (numParams != 0) continue;
+            String name = m.getName();
+            if (name.equals("getClass")) continue;
+            if (m.getReturnType() == Void.TYPE) continue;
+            if (name.startsWith("get") || name.startsWith("is")) {
+                result.add(m);
+            }
+        }
+        return result.toArray(new Method[result.size()]);
+    }
+
+    /**
+     * Find the property names with getter methods on the target class. This method returns the property names for the methods
+     * returned by {@link #findGetterMethods()}.
+     * 
+     * @return the Java Bean property names for the getters; never null but possibly empty
+     */
+    public String[] findGetterPropertyNames() {
+        final Method[] getters = findGetterMethods();
+        final List<String> result = new ArrayList<String>();
+        for (int i = 0; i < getters.length; i++) {
+            final Method m = getters[i];
+            String name = m.getName();
+            if (name.startsWith("get") && name.length() > 3) {
+                result.add(name.substring(3));
+            } else if (name.startsWith("is") && name.length() > 2) {
+                result.add(name.substring(2));
+            }
+        }
+        return result.toArray(new String[result.size()]);
+    }
+
+    /**
      * Find the method on the target class that matches the supplied method name.
      * 
      * @param methodName the name of the method that is to be found.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -29,7 +29,7 @@
 /**
  * @author Randall Hauch
  */
-public class DnaLexicon extends org.jboss.dna.graph.DnaLexicon {
+public class DnaLexicon extends org.jboss.dna.repository.DnaLexicon {
 
     public static final Name DEFINED = new BasicName(Namespace.URI, "defined");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -21,45 +21,61 @@
  */
 package org.jboss.dna.repository;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.component.ClassLoaderFactory;
 import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.Inflector;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.util.Reflection;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathExpression;
-import org.jboss.dna.graph.sequencer.StreamSequencer;
-import org.jboss.dna.repository.mimetype.MimeTypeDetectorConfig;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.repository.sequencer.Sequencer;
-import org.jboss.dna.repository.sequencer.SequencerConfig;
-import org.jboss.dna.repository.sequencer.StreamSequencerAdapter;
 
 /**
  */
 @Immutable
 public class DnaConfiguration {
 
-    protected RepositorySource configurationSource;
-    protected String configurationSourceDescription;
-    protected Graph configuration;
+    protected static final Map<String, Name> NAMES_TO_MAP;
+    static {
+        Map<String, Name> names = new HashMap<String, Name>();
+        names.put(DnaLexicon.READABLE_NAME.getLocalName(), DnaLexicon.READABLE_NAME);
+        names.put(DnaLexicon.DESCRIPTION.getLocalName(), DnaLexicon.DESCRIPTION);
+        names.put(DnaLexicon.DEFAULT_CACHE_POLICY.getLocalName(), DnaLexicon.DEFAULT_CACHE_POLICY);
+        names.put(DnaLexicon.RETRY_LIMIT.getLocalName(), DnaLexicon.RETRY_LIMIT);
+        names.put(DnaLexicon.PATH_EXPRESSIONS.getLocalName(), DnaLexicon.PATH_EXPRESSIONS);
+        names.put(DnaLexicon.CLASSNAME.getLocalName(), DnaLexicon.CLASSNAME);
+        names.put(DnaLexicon.CLASSPATH.getLocalName(), DnaLexicon.CLASSPATH);
+        NAMES_TO_MAP = Collections.unmodifiableMap(names);
+    }
+
+    protected class Source {
+        protected RepositorySource source;
+        protected String description;
+        protected Path path;
+    }
+
     private final ExecutionContext context;
+    protected Source configurationSource;
+    private Path sourcesPath;
+    private Path sequencersPath;
+    private Path detectorsPath;
+    private Graph graph;
+    private Graph.Batch batch;
 
     /**
-     * Mapping of repository names to configured repositories
-     */
-    protected final Map<String, DnaRepositoryDetails> repositories;
-    protected final Map<String, DnaMimeTypeDetectorDetails> mimeTypeDetectors;
-    protected final Map<String, DnaSequencerDetails> sequencers;
-
-    /**
      * Create a new configuration for DNA.
      */
     public DnaConfiguration() {
@@ -70,61 +86,79 @@
      * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
      * 
      * @param context the new context, or null if a default-constructed execution context should be used
+     * @throws IllegalArgumentException if the supplied context reference is null
      */
     public DnaConfiguration( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
         this.context = context;
-        this.repositories = new HashMap<String, DnaRepositoryDetails>();
-        this.mimeTypeDetectors = new HashMap<String, DnaMimeTypeDetectorDetails>();
-        this.sequencers = new HashMap<String, DnaSequencerDetails>();
 
         // Set up the default configuration repository ...
         this.configurationSource = createDefaultConfigurationSource();
+    }
 
+    /**
+     * Method that is used to set up the default configuration repository source. By default, this method sets up the
+     * {@link InMemoryRepositorySource} loaded from the classpath.
+     * 
+     * @return the default repository source
+     */
+    protected Source createDefaultConfigurationSource() {
+        InMemoryRepositorySource defaultSource = new InMemoryRepositorySource();
+        defaultSource.setName("Configuration");
+        Source result = new Source();
+        result.source = defaultSource;
+        result.path = this.context.getValueFactories().getPathFactory().createRootPath();
+        result.description = "Configuration Repository";
+        return result;
     }
 
-    private DnaConfiguration( DnaConfiguration source ) {
-        this.configuration = source.configuration;
-        this.configurationSource = source.configurationSource;
-        this.context = source.context;
-        this.configurationSourceDescription = source.configurationSourceDescription;
-        this.repositories = new HashMap<String, DnaRepositoryDetails>(source.repositories);
-        this.mimeTypeDetectors = new HashMap<String, DnaMimeTypeDetectorDetails>(source.mimeTypeDetectors);
-        this.sequencers = new HashMap<String, DnaSequencerDetails>(source.sequencers);
+    protected final ExecutionContext context() {
+        return this.context;
     }
 
-    private DnaConfiguration with( String repositoryName,
-                                   DnaRepositoryDetails details ) {
-        DnaConfiguration newConfig = new DnaConfiguration(this);
-        newConfig.repositories.put(repositoryName, details);
-
-        return newConfig;
+    protected final PathFactory pathFactory() {
+        return context().getValueFactories().getPathFactory();
     }
 
-    protected ExecutionContext getExecutionContext() {
-        return this.context;
+    /**
+     * Get the graph containing the configuration information. This should be called only after the
+     * {@link #withConfigurationRepository() configuration repository} is set up.
+     * 
+     * @return the configuration repository graph; never null
+     * @see #graph()
+     */
+    protected final Graph graph() {
+        if (this.graph == null) {
+            this.graph = Graph.create(configurationSource.source, context);
+        }
+        return this.graph;
     }
 
-    protected Graph getConfiguration() {
-        if (this.configuration == null) {
-            this.configuration = Graph.create(configurationSource, context);
+    /**
+     * Get the graph batch that can be used to change the configuration, where the changes are enqueued until {@link #save()
+     * saved}. This should be called only after the {@link #withConfigurationRepository() configuration repository} is set up.
+     * 
+     * @return the latest batch for changes to the configuration repository; never null
+     * @see #graph()
+     */
+    protected final Graph.Batch configuration() {
+        if (this.batch == null) {
+            this.batch = graph().batch();
         }
-        return this.configuration;
+        return this.batch;
     }
 
     /**
-     * Method that is used to set up the default configuration repository source. By default, this method sets up the
-     * {@link InMemoryRepositorySource} loaded from the classpath.
+     * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
      * 
-     * @return the default repository source
+     * @return this configuration object for method chaining purposes; never null
      */
-    protected RepositorySource createDefaultConfigurationSource() {
-        this.withConfigurationRepository()
-            .usingClass(InMemoryRepositorySource.class.getName())
-            .loadedFromClasspath()
-            .describedAs("Configuration Repository")
-            .with("name")
-            .setTo("Configuration");
-        return configurationSource;
+    public DnaConfiguration save() {
+        if (this.batch != null) {
+            this.batch.execute();
+            this.batch = this.graph.batch();
+        }
+        return this;
     }
 
     /**
@@ -135,40 +169,69 @@
      * @return the interface for choosing the class, which returns the interface used to configure the repository source that will
      *         be used for the configuration repository; never null
      */
-    public ChooseClass<RepositorySource, RepositoryDetails> withConfigurationRepository() {
-        return addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME);
+    public ChooseClass<RepositorySource, ConfigRepositoryDetails> withConfigurationRepository() {
+        final Source source = this.configurationSource;
+        // The config repository is different, since it has to load immediately ...
+        return new ChooseClass<RepositorySource, ConfigRepositoryDetails>() {
+            public LoadedFrom<ConfigRepositoryDetails> usingClass( final String className ) {
+                return new LoadedFrom<ConfigRepositoryDetails>() {
+                    @SuppressWarnings( "unchecked" )
+                    public ConfigRepositoryDetails loadedFrom( String... classpath ) {
+                        ClassLoader classLoader = context().getClassLoader(classpath);
+                        Class<? extends RepositorySource> clazz = null;
+                        try {
+                            clazz = (Class<? extends RepositorySource>)classLoader.loadClass(className);
+                        } catch (ClassNotFoundException err) {
+                            throw new DnaConfigurationException(RepositoryI18n.unableToLoadClassUsingClasspath.text(className,
+                                                                                                                    classpath));
+                        }
+                        return usingClass(clazz);
+                    }
+
+                    @SuppressWarnings( "unchecked" )
+                    public ConfigRepositoryDetails loadedFromClasspath() {
+                        Class<? extends RepositorySource> clazz = null;
+                        try {
+                            clazz = (Class<? extends RepositorySource>)Class.forName(className);
+                        } catch (ClassNotFoundException err) {
+                            throw new DnaConfigurationException(RepositoryI18n.unableToLoadClass.text(className));
+                        }
+                        return usingClass(clazz);
+                    }
+                };
+            }
+
+            public ConfigRepositoryDetails usingClass( Class<? extends RepositorySource> repositorySource ) {
+                try {
+                    source.source = repositorySource.newInstance();
+                } catch (InstantiationException err) {
+                    I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
+                    throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
+                } catch (IllegalAccessException err) {
+                    I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
+                    throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
+                }
+                return new ConfigurationSourceDetails();
+            }
+        };
     }
 
     /**
      * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name, and
      * if the name of an existing repository is used, this will replace the existing repository configuration.
      * 
-     * @param name the name of the new repository that is to be added
+     * @param id the id of the new repository that is to be added
      * @return the interface for choosing the class, which returns the interface used to configure the repository source; never
      *         null
      * @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
      * @see #addRepository(RepositorySource)
      */
-    public ChooseClass<RepositorySource, RepositoryDetails> addRepository( final String name ) {
-        return new ClassChooser<RepositorySource, RepositoryDetails>() {
-
-            @Override
-            protected RepositoryDetails getComponentBuilder( String className,
-                                                             String... classpath ) {
-                try {
-                    Class<?> clazz = Class.forName(className);
-                    Object newInstance = clazz.newInstance();
-                    assert newInstance instanceof RepositorySource;
-
-                    DnaRepositoryDetails details = new DnaRepositoryDetails((RepositorySource)newInstance);
-                    DnaConfiguration.this.repositories.put(name, details);
-
-                    return details;
-                } catch (Exception ex) {
-                    throw new DnaConfigurationException(ex);
-                }
-            }
-        };
+    public ChooseClass<RepositorySource, RepositoryDetails> addRepository( final String id ) {
+        CheckArg.isNotEmpty(id, "id");
+        // Now create the "dna:source" node with the supplied id ...
+        Path sourcePath = pathFactory().create(sourcesPath(), id);
+        configuration().create(sourcePath).with(DnaLexicon.READABLE_NAME, id).and();
+        return new ClassChooser<RepositorySource, RepositoryDetails>(sourcePath, new GraphRepositoryDetails(sourcePath));
     }
 
     /**
@@ -181,338 +244,130 @@
      * @see #addRepository(String)
      */
     public DnaConfiguration addRepository( RepositorySource source ) {
-        return this.with(source.getName(), new DnaRepositoryDetails(source));
+        CheckArg.isNotNull(source, "source");
+        CheckArg.isNotEmpty(source.getName(), "source.getName()");
+        String name = source.getName();
+        RepositoryDetails details = addRepository(source.getName()).usingClass(source.getClass().getName()).loadedFromClasspath();
+        // Record all of the bean properties ...
+        Path sourcePath = pathFactory().create(sourcesPath(), name);
+        Reflection reflector = new Reflection(source.getClass());
+        for (String propertyName : reflector.findGetterPropertyNames()) {
+            Object value;
+            try {
+                value = reflector.invokeGetterMethodOnTarget(propertyName, source);
+                if (value == null) continue;
+                propertyName = Inflector.getInstance().lowerCamelCase(propertyName);
+                if (NAMES_TO_MAP.containsKey(propertyName)) {
+                    configuration().set(NAMES_TO_MAP.get(propertyName)).to(value).on(sourcePath);
+                } else {
+                    configuration().set(propertyName).to(value).on(sourcePath);
+                }
+            } catch (ValueFormatException err) {
+                throw err;
+            } catch (Throwable err) {
+                // Unable to call getter and set property
+            }
+        }
+        return details.and();
     }
 
     /**
      * Add a new {@link Sequencer sequencer} to this configuration. The new sequencer will have the supplied name, and if the name
      * of an existing sequencer is used, this will replace the existing sequencer configuration.
      * 
-     * @param name the name of the new sequencer
+     * @param id the identifier of the new sequencer
      * @return the interface for choosing the class, which returns the interface used to configure the sequencer; never null
      * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
      */
-    public ChooseClass<Sequencer, SequencerDetails> addSequencer( final String name ) {
-        return new ClassChooser<Sequencer, SequencerDetails>() {
-
-            @Override
-            protected SequencerDetails getComponentBuilder( String className,
-                                                            String... classpath ) {
-                try {
-                    Class<?> clazz = Class.forName(className);
-                    Object newInstance = clazz.newInstance();
-                    assert newInstance instanceof Sequencer;
-
-                    DnaSequencerDetails details = new DnaSequencerDetails(name, (Sequencer)newInstance, classpath);
-                    DnaConfiguration.this.sequencers.put(name, details);
-
-                    return details;
-                } catch (Exception ex) {
-                    throw new DnaConfigurationException(ex);
-                }
-            }
-        };
+    public ChooseClass<Sequencer, SequencerDetails> addSequencer( final String id ) {
+        CheckArg.isNotEmpty(id, "id");
+        // Now create the "dna:sequencer" node with the supplied id ...
+        Path sequencerPath = pathFactory().create(sequencersPath(), id);
+        configuration().create(sequencerPath).with(DnaLexicon.READABLE_NAME, id).and();
+        return new ClassChooser<Sequencer, SequencerDetails>(sequencerPath, new GraphSequencerDetails(sequencerPath));
     }
 
     /**
-     * Add a new {@link StreamSequencer sequencer} to this configuration. The new stream sequencer will have the supplied name,
-     * and if the name of an existing sequencer is used, this will replace the existing sequencer configuration.
-     * 
-     * @param name the name of the new sequencer
-     * @return the interface for choosing the class, which returns the interface used to configure the stream sequencer; never
-     *         null
-     * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
-     */
-    public ChooseClass<StreamSequencer, SequencerDetails> addStreamSequencer( final String name ) {
-        return new ClassChooser<StreamSequencer, SequencerDetails>() {
-
-            @Override
-            protected SequencerDetails getComponentBuilder( String className,
-                                                            String... classpath ) {
-                try {
-                    Class<?> clazz = Class.forName(className);
-                    Object newInstance = clazz.newInstance();
-                    assert newInstance instanceof StreamSequencer;
-
-                    DnaSequencerDetails details = new DnaSequencerDetails(
-                                                                          name,
-                                                                          new StreamSequencerAdapter((StreamSequencer)newInstance),
-                                                                          classpath);
-                    DnaConfiguration.this.sequencers.put(name, details);
-
-                    return details;
-                } catch (Exception ex) {
-                    throw new DnaConfigurationException(ex);
-                }
-            }
-        };
-    }
-
-    /**
      * Add a new {@link MimeTypeDetector MIME type detector} to this configuration. The new detector will have the supplied name,
      * and if the name of an existing detector is used, this will replace the existing detector configuration.
      * 
-     * @param name the name of the new detector
+     * @param id the id of the new detector
      * @return the interface for choosing the class, which returns the interface used to configure the detector; never null
      * @throws IllegalArgumentException if the detector name is null, empty, or otherwise invalid
      */
-    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails> addMimeTypeDetector( final String name ) {
-        return new ClassChooser<MimeTypeDetector, MimeTypeDetectorDetails>() {
-
-            @Override
-            protected MimeTypeDetectorDetails getComponentBuilder( String className,
-                                                                   String... classpath ) {
-                try {
-                    Class<?> clazz = Class.forName(className);
-                    Object newInstance = clazz.newInstance();
-                    assert newInstance instanceof MimeTypeDetector;
-
-                    DnaMimeTypeDetectorDetails details = new DnaMimeTypeDetectorDetails(name, (MimeTypeDetector)newInstance,
-                                                                                        classpath);
-                    DnaConfiguration.this.mimeTypeDetectors.put(name, details);
-
-                    return details;
-                } catch (Exception ex) {
-                    throw new DnaConfigurationException(ex);
-                }
-            }
-        };
+    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails> addMimeTypeDetector( final String id ) {
+        CheckArg.isNotEmpty(id, "id");
+        // Now create the "dna:sequencer" node with the supplied id ...
+        Path detectorPath = pathFactory().create(detectorsPath(), id);
+        configuration().create(detectorPath).with(DnaLexicon.READABLE_NAME, id).and();
+        return new ClassChooser<MimeTypeDetector, MimeTypeDetectorDetails>(detectorPath,
+                                                                           new GraphMimeTypeDetectorDetails(detectorPath));
     }
 
     /**
      * Complete this configuration and create the corresponding engine.
      * 
      * @return the new engine configured by this instance
+     * @throws DnaConfigurationException if the engine cannot be created from this configuration.
      */
-    public DnaEngine build() {
+    public DnaEngine build() throws DnaConfigurationException {
+        save();
         return new DnaEngine(this);
     }
 
-    /**
-     * Interface used to configure a {@link RepositorySource repository}.
-     */
-    public interface RepositoryDetails
-        extends SetDescription<RepositoryDetails>, SetProperties<RepositoryDetails>, ConfigurationBuilder {
-
-        RepositorySource getRepositorySource();
+    protected Path sourcesPath() {
+        // Make sure the "dna:sources" node is there
+        if (sourcesPath == null) {
+            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.SOURCES);
+            Node node = graph().createIfMissing(path).andReturn();
+            this.sourcesPath = node.getLocation().getPath();
+        }
+        return this.sourcesPath;
     }
 
-    /**
-     * Local implementation of the {@link MimeTypeDetectorDetails} interface that tracks all of the user-provided configuration
-     * details.
-     */
-    protected class DnaMimeTypeDetectorDetails implements MimeTypeDetectorDetails {
-        final MimeTypeDetector mimeTypeDetector;
-        private String name;
-        private String description;
-        private Map<String, Object> properties;
-        private String className;
-        private String[] classpath;
-
-        protected DnaMimeTypeDetectorDetails( String name,
-                                              MimeTypeDetector mimeTypeDetector,
-                                              String[] classpath ) {
-            this.mimeTypeDetector = mimeTypeDetector;
-            this.name = name;
-            this.description = mimeTypeDetector.getClass().getName();
-            this.properties = new HashMap<String, Object>();
-            this.className = mimeTypeDetector.getClass().getName();
-            this.classpath = classpath;
+    protected Path sequencersPath() {
+        // Make sure the "dna:sequencers" node is there
+        if (sequencersPath == null) {
+            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.SEQUENCERS);
+            Node node = graph().createIfMissing(path).andReturn();
+            this.sequencersPath = node.getLocation().getPath();
         }
+        return this.sequencersPath;
+    }
 
-        public Map<String, Object> getProperties() {
-            return properties;
+    protected Path detectorsPath() {
+        // Make sure the "dna:mimeTypeDetectors" node is there
+        if (detectorsPath == null) {
+            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.MIME_TYPE_DETECTORS);
+            Node node = graph().createIfMissing(path).andReturn();
+            this.detectorsPath = node.getLocation().getPath();
         }
-
-        protected String getDescription() {
-            return description;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public MimeTypeDetectorDetails describedAs( String description ) {
-            this.description = description;
-            return this;
-        }
-
-        public PropertySetter<MimeTypeDetectorDetails> with( final String propertyName ) {
-            return new MappingPropertySetter<MimeTypeDetectorDetails>(propertyName, this);
-        }
-
-        public MimeTypeDetector getMimeTypeDetector() {
-            return mimeTypeDetector;
-        }
-
-        MimeTypeDetectorConfig getMimeTypeDetectorConfig() {
-            return new MimeTypeDetectorConfig(name, description, properties, className, classpath);
-        }
-
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-
+        return this.detectorsPath;
     }
 
     /**
-     * Local implementation of the {@link RepositoryDetails} interface that tracks all of the user-provided configuration details.
+     * Interface used to configure a {@link RepositorySource repository}.
      */
-    protected class DnaRepositoryDetails implements RepositoryDetails {
-        private final RepositorySource source;
-        private final String description;
-        private Map<String, Object> properties;
-
-        protected DnaRepositoryDetails( RepositorySource source ) {
-            this.source = source;
-            this.description = source.getName();
-            this.properties = new HashMap<String, Object>();
-        }
-
-        protected DnaRepositoryDetails( RepositorySource source,
-                                        String description ) {
-            this.source = source;
-            this.description = description;
-        }
-
-        public Map<String, Object> getProperties() {
-            return properties;
-        }
-
-        protected String getDescription() {
-            return description;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public RepositoryDetails describedAs( String description ) {
-            return new DnaRepositoryDetails(this.source, description);
-        }
-
-        public PropertySetter<RepositoryDetails> with( final String propertyName ) {
-            return new BeanPropertySetter<RepositoryDetails>(source, propertyName, this);
-        }
-
-        public RepositorySource getRepositorySource() {
-            return source;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
+    public interface RepositoryDetails
+        extends SetName<RepositoryDetails>, SetDescription<RepositoryDetails>, SetProperties<RepositoryDetails>,
+        ConfigurationBuilder {
     }
 
-    /**
-     * Local implementation of the {@link SequencerDetails} interface that tracks all of the user-provided configuration details.
-     */
-    protected class DnaSequencerDetails implements SequencerDetails {
-        private final Sequencer sequencer;
-        private String name;
-        private String description;
-        private Map<String, Object> properties;
-        private String className;
-        private String[] classpath;
-        final List<PathExpression> sourcePathExpressions;
-        final List<PathExpression> targetPathExpressions;
-
-        protected DnaSequencerDetails( String name,
-                                       Sequencer sequencer,
-                                       String[] classpath ) {
-            this.sequencer = sequencer;
-            this.name = name;
-            this.description = sequencer.getClass().getName();
-            this.properties = new HashMap<String, Object>();
-            this.className = sequencer.getClass().getName();
-            this.classpath = classpath;
-            this.sourcePathExpressions = new ArrayList<PathExpression>();
-            this.targetPathExpressions = new ArrayList<PathExpression>();
-        }
-
-        public Map<String, Object> getProperties() {
-            return properties;
-        }
-
-        protected String getDescription() {
-            return description;
-        }
-
+    public interface ConfigRepositoryDetails
+        extends SetDescription<ConfigRepositoryDetails>, SetProperties<ConfigRepositoryDetails>, ConfigurationBuilder {
         /**
-         * {@inheritDoc}
+         * Specify the path under which the configuration content is to be found. This path is assumed to be "/" by default.
          * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         * @param path the path to the configuration content in the configuration source; may not be null
+         * @return this instance for method chaining purposes; never null
          */
-        public SequencerDetails describedAs( String description ) {
-            this.description = description;
-            return this;
-        }
-
-        public PropertySetter<SequencerDetails> with( final String propertyName ) {
-            return new MappingPropertySetter<SequencerDetails>(propertyName, this);
-        }
-
-        public Sequencer getSequencer() {
-            return sequencer;
-        }
-
-        SequencerConfig getSequencerConfig() {
-            return new SequencerConfig(this.name, this.description, this.properties, this.className, this.classpath);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(java.lang.String)
-         */
-        public PathExpressionOutput sequencingFrom( String inputExpressionPath ) {
-            return this.sequencingFrom(PathExpression.compile(inputExpressionPath));
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
-         */
-        public PathExpressionOutput sequencingFrom( final PathExpression inputExpressionPath ) {
-            final DnaSequencerDetails details = this;
-            return new PathExpressionOutput() {
-
-                /**
-                 * {@inheritDoc}
-                 * 
-                 * @see org.jboss.dna.repository.DnaConfiguration.PathExpressionOutput#andOutputtingTo(java.lang.String)
-                 */
-                public DnaSequencerDetails andOutputtingTo( final String outputExpressionPath ) {
-                    details.sourcePathExpressions.add(inputExpressionPath);
-                    details.targetPathExpressions.add(PathExpression.compile(outputExpressionPath));
-                    return details;
-                }
-            };
-        }
-
+        public ConfigRepositoryDetails under( String path );
     }
 
     /**
      * Interface used to configure a {@link Sequencer sequencer}.
      */
-    public interface SequencerDetails extends SetDescription<SequencerDetails>, ConfigurationBuilder {
+    public interface SequencerDetails extends SetName<SequencerDetails>, SetDescription<SequencerDetails>, ConfigurationBuilder {
 
         /**
          * Specify the input {@link PathExpression path expression} represented as a string, which determines when this sequencer
@@ -529,9 +384,9 @@
          * 
          * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
          *        sequencer
-         * @return the interface used to specify the output path expression; never null
+         * @return the interface used to continue specifying the configuration of the sequencer
          */
-        PathExpressionOutput sequencingFrom( PathExpression inputPathExpression );
+        SequencerDetails sequencingFrom( PathExpression inputPathExpression );
     }
 
     /**
@@ -553,7 +408,8 @@
      * Interface used to configure a {@link MimeTypeDetector MIME type detector}.
      */
     public interface MimeTypeDetectorDetails
-        extends SetDescription<MimeTypeDetectorDetails>, SetProperties<MimeTypeDetectorDetails>, ConfigurationBuilder {
+        extends SetName<MimeTypeDetectorDetails>, SetDescription<MimeTypeDetectorDetails>,
+        SetProperties<MimeTypeDetectorDetails>, ConfigurationBuilder {
     }
 
     /**
@@ -670,6 +526,7 @@
          * 
          * @param clazz the class that should be instantiated
          * @return the next component to continue configuration; never null
+         * @throws DnaConfigurationException if the class could not be accessed and instantiated (if needed)
          * @throws IllegalArgumentException if the class reference is null
          */
         ReturnType usingClass( Class<? extends ComponentClassType> clazz );
@@ -691,6 +548,21 @@
     }
 
     /**
+     * The interface used to set a human readable name on a component.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface SetName<ReturnType> {
+        /**
+         * Specify the human-readable name for this component.
+         * 
+         * @param description the description; may be null or empty
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType named( String description );
+    }
+
+    /**
      * Interface for specifying from where the component's class is to be loaded.
      * 
      * @param <ReturnType> the interface returned from these methods
@@ -725,8 +597,6 @@
      */
     public interface ConfigurationBuilder {
 
-        Map<String, Object> getProperties();
-
         /**
          * Return a reference to the enclosing configuration so that it can be built or further configured.
          * 
@@ -735,6 +605,211 @@
         DnaConfiguration and();
     }
 
+    protected class ConfigurationSourceDetails implements ConfigRepositoryDetails {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public ConfigRepositoryDetails describedAs( String description ) {
+            DnaConfiguration.this.configurationSource.description = description;
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<ConfigRepositoryDetails> with( String propertyName ) {
+            return new BeanPropertySetter<ConfigRepositoryDetails>(DnaConfiguration.this.configurationSource.source,
+                                                                   propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigRepositoryDetails#under(java.lang.String)
+         */
+        public ConfigRepositoryDetails under( String path ) {
+            CheckArg.isNotNull(path, "path");
+            DnaConfiguration.this.configurationSource.path = context().getValueFactories().getPathFactory().create(path);
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+    }
+
+    protected class GraphRepositoryDetails implements RepositoryDetails {
+        private final Path path;
+
+        protected GraphRepositoryDetails( Path path ) {
+            assert path != null;
+            this.path = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
+         */
+        public RepositoryDetails named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public RepositoryDetails describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<RepositoryDetails> with( String propertyName ) {
+            return new GraphPropertySetter<RepositoryDetails>(path, propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+    }
+
+    protected class GraphSequencerDetails implements SequencerDetails {
+        private final Path path;
+
+        protected GraphSequencerDetails( Path path ) {
+            assert path != null;
+            this.path = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(java.lang.String)
+         */
+        public PathExpressionOutput sequencingFrom( final String from ) {
+            CheckArg.isNotEmpty(from, "from");
+            return new PathExpressionOutput() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.repository.DnaConfiguration.PathExpressionOutput#andOutputtingTo(java.lang.String)
+                 */
+                public SequencerDetails andOutputtingTo( String into ) {
+                    CheckArg.isNotEmpty(into, "into");
+                    return sequencingFrom(PathExpression.compile(from + " => " + into));
+                }
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
+         */
+        public SequencerDetails named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
+         */
+        public SequencerDetails sequencingFrom( PathExpression expression ) {
+            CheckArg.isNotNull(expression, "expression");
+            configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(expression.getExpression());
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public SequencerDetails describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+    }
+
+    protected class GraphMimeTypeDetectorDetails implements MimeTypeDetectorDetails {
+        private final Path path;
+
+        protected GraphMimeTypeDetectorDetails( Path path ) {
+            assert path != null;
+            this.path = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
+         */
+        public MimeTypeDetectorDetails named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<MimeTypeDetectorDetails> with( String propertyName ) {
+            return new GraphPropertySetter<MimeTypeDetectorDetails>(path, propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
+         */
+        public MimeTypeDetectorDetails describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
+         */
+        public DnaConfiguration and() {
+            return DnaConfiguration.this;
+        }
+    }
+
     /**
      * Abstract implementation of {@link ChooseClass} that has a single abstract method to obtain the interface that should be
      * returned when the class name and classpath have been selected.
@@ -743,10 +818,17 @@
      * @param <ReturnType> the interface that should be returned when the class name and classpath have been chosen.
      * @author Randall Hauch
      */
-    protected abstract class ClassChooser<ComponentClass, ReturnType> implements ChooseClass<ComponentClass, ReturnType> {
+    protected class ClassChooser<ComponentClass, ReturnType> implements ChooseClass<ComponentClass, ReturnType> {
+        protected final Path pathOfComponentNode;
+        protected final ReturnType returnObject;
 
-        protected abstract ReturnType getComponentBuilder( String className,
-                                                           String... classpath );
+        protected ClassChooser( Path pathOfComponentNode,
+                                ReturnType returnObject ) {
+            assert pathOfComponentNode != null;
+            assert returnObject != null;
+            this.pathOfComponentNode = pathOfComponentNode;
+            this.returnObject = returnObject;
+        }
 
         /**
          * {@inheritDoc}
@@ -755,13 +837,22 @@
          */
         public LoadedFrom<ReturnType> usingClass( final String classname ) {
             CheckArg.isNotEmpty(classname, "classname");
+            configuration().set(DnaLexicon.CLASSNAME).to(classname).on(pathOfComponentNode);
             return new LoadedFrom<ReturnType>() {
                 public ReturnType loadedFromClasspath() {
-                    return getComponentBuilder(classname);
+                    return returnObject;
                 }
 
                 public ReturnType loadedFrom( String... classpath ) {
-                    return getComponentBuilder(classname, classpath);
+                    CheckArg.isNotEmpty(classpath, "classpath");
+                    if (classpath.length == 1 && classpath[0] != null) {
+                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0]).on(pathOfComponentNode);
+                    } else {
+                        Object[] remaining = new String[classpath.length - 1];
+                        System.arraycopy(classpath, 1, remaining, 0, remaining.length);
+                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0], remaining).on(pathOfComponentNode);
+                    }
+                    return returnObject;
                 }
             };
         }
@@ -773,34 +864,11 @@
          */
         public ReturnType usingClass( Class<? extends ComponentClass> clazz ) {
             CheckArg.isNotNull(clazz, "clazz");
-            return getComponentBuilder(clazz.getName());
+            return usingClass(clazz.getName()).loadedFromClasspath();
         }
     }
 
     /**
-     * Utility method to instantiate a class.
-     * 
-     * @param <T> the interface or superclass that the instantiated object is to implement
-     * @param interfaceType the interface or superclass type that the instantiated object is to implement
-     * @param className the name of the class
-     * @param classloaderNames the names of the class loaders
-     * @return the new instance
-     */
-    @SuppressWarnings( "unchecked" )
-    protected <T> T instantiate( Class<T> interfaceType,
-                                 String className,
-                                 String... classloaderNames ) {
-        // Load the class and create the instance ...
-        try {
-            Class<?> clazz = getExecutionContext().getClassLoader(classloaderNames).loadClass(className);
-            return (T)clazz.newInstance();
-        } catch (Throwable err) {
-            I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
-            throw new DnaConfigurationException(msg.text(className, err.getMessage()), err);
-        }
-    }
-
-    /**
      * Reusable implementation of {@link PropertySetter} that sets the JavaBean-style property using reflection.
      * 
      * @param <ReturnType>
@@ -864,53 +932,96 @@
     }
 
     /**
-     * Reusable implementation of {@link PropertySetter} that aggregates the properties to set to be placed into a map
+     * Reusable implementation of {@link PropertySetter} that sets the property on the specified node in the configuration graph.
      * 
      * @param <ReturnType>
      * @author Randall Hauch
      */
-    protected class MappingPropertySetter<ReturnType extends ConfigurationBuilder> implements PropertySetter<ReturnType> {
+    protected class GraphPropertySetter<ReturnType> implements PropertySetter<ReturnType> {
+        private final Path path;
         private final String beanPropertyName;
         private final ReturnType returnObject;
 
-        protected MappingPropertySetter( String beanPropertyName,
-                                         ReturnType returnObject ) {
+        protected GraphPropertySetter( Path path,
+                                       String beanPropertyName,
+                                       ReturnType returnObject ) {
+            assert path != null;
             assert beanPropertyName != null;
             assert returnObject != null;
-            this.beanPropertyName = beanPropertyName;
+            this.path = path;
+            this.beanPropertyName = Inflector.getInstance().lowerCamelCase(beanPropertyName);
             this.returnObject = returnObject;
         }
 
         public ReturnType setTo( boolean value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( int value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( long value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( short value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( float value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( double value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( String value ) {
-            return setTo((Object)value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
+            return returnObject;
         }
 
         public ReturnType setTo( Object value ) {
-            returnObject.getProperties().put(beanPropertyName, value);
+            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
+                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
+            } else {
+                configuration().set(beanPropertyName).to(value).on(path);
+            }
             return returnObject;
         }
     }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -21,23 +21,44 @@
  */
 package org.jboss.dna.repository;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.SimpleProblems;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.JcrMixLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
 import org.jboss.dna.graph.connector.RepositoryConnection;
 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.repository.DnaConfiguration.DnaMimeTypeDetectorDetails;
-import org.jboss.dna.repository.DnaConfiguration.DnaRepositoryDetails;
-import org.jboss.dna.repository.DnaConfiguration.DnaSequencerDetails;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathExpression;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.repository.mimetype.MimeTypeDetectorConfig;
 import org.jboss.dna.repository.observation.ObservationService;
+import org.jboss.dna.repository.sequencer.SequencerConfig;
 import org.jboss.dna.repository.sequencer.SequencingService;
 import org.jboss.dna.repository.service.AdministeredService;
+import org.jboss.dna.repository.util.JcrExecutionContext;
+import org.jboss.dna.repository.util.SessionFactory;
+import org.jboss.dna.repository.util.SimpleSessionFactory;
 
 /**
  * @author Randall Hauch
@@ -47,6 +68,7 @@
 
     public static final String CONFIGURATION_REPOSITORY_NAME = "dna:configuration";
 
+    private final Problems problems;
     private final ExecutionContext context;
     private final List<AdministeredService> services;
 
@@ -58,33 +80,39 @@
     private final RepositoryConnectionFactory connectionFactory;
 
     DnaEngine( DnaConfiguration configuration ) {
-        this.context = new ExecutionContext();
+        this.problems = new SimpleProblems();
 
-        DnaRepositoryDetails configSource = configuration.repositories.get(CONFIGURATION_REPOSITORY_NAME);
-        assert configSource != null : "Must specify a repository source named " + CONFIGURATION_REPOSITORY_NAME;
+        // Use the configuration's context ...
+        this.context = configuration.context();
 
-        // Use a magic number for now
-        executorService = new ScheduledThreadPoolExecutor(10);
+        // Add the configuration source to the repository library ...
+        final DnaConfiguration.Source configSourceInfo = configuration.configurationSource;
+        final RepositorySource configSource = configSourceInfo.source;
+        RepositoryLibrary library = new RepositoryLibrary();
+        library.addSource(configSource);
 
-        observationService = null; // new ObservationService(null);
+        // Create the RepositoryService, pointing it to the configuration repository ...
+        Path pathToConfigurationRoot = configSourceInfo.path;
+        repositoryService = new RepositoryService(library, configSource.getName(), "", pathToConfigurationRoot, context);
 
-        RepositoryLibrary library = new RepositoryLibrary();
-        for (DnaRepositoryDetails details : configuration.repositories.values()) {
-            // Adding configuration source to the library until proven wrong!
-            library.addSource(details.getRepositorySource());
+        for (MimeTypeDetectorConfig config : loadMimeTypeDetectors(problems, context, configSourceInfo)) {
+            library.getMimeTypeDetectors().addDetector(config);
         }
 
-        for (DnaMimeTypeDetectorDetails details : configuration.mimeTypeDetectors.values()) {
-            library.getMimeTypeDetectors().addDetector(details.getMimeTypeDetectorConfig());
-        }
-        repositoryService = new RepositoryService(library, configSource.getRepositorySource().getName(), "", context);
-
+        // Create the sequencing service ...
+        executorService = new ScheduledThreadPoolExecutor(10); // Use a magic number for now
         sequencingService = new SequencingService();
+        SessionFactory sessionFactory = new SimpleSessionFactory();
+        JcrExecutionContext jcrContext = new JcrExecutionContext(context, sessionFactory, "");
+        sequencingService.setExecutionContext(jcrContext);
         sequencingService.setExecutorService(executorService);
-        for (DnaSequencerDetails details : configuration.sequencers.values()) {
-            sequencingService.addSequencer(details.getSequencerConfig());
+        for (SequencerConfig sequencerConfig : loadSequencingConfigurations(problems, context, configSourceInfo)) {
+            sequencingService.addSequencer(sequencerConfig);
         }
 
+        // Create the observation service ...
+        observationService = null; // new ObservationService(null);
+
         this.services = Arrays.asList(new AdministeredService[] { /* observationService, */repositoryService, sequencingService,});
 
         connectionFactory = new RepositoryConnectionFactory() {
@@ -99,6 +127,145 @@
         };
     }
 
+    protected static List<MimeTypeDetectorConfig> loadMimeTypeDetectors( Problems problems,
+                                                                         ExecutionContext context,
+                                                                         DnaConfiguration.Source source ) {
+        List<MimeTypeDetectorConfig> detectors = new ArrayList<MimeTypeDetectorConfig>();
+        Graph graph = Graph.create(source.source, context);
+        Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(source.path,
+                                                                                        DnaLexicon.MIME_TYPE_DETECTORS);
+        try {
+            Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
+
+            Set<Name> skipProperties = new HashSet<Name>();
+            skipProperties.add(DnaLexicon.READABLE_NAME);
+            skipProperties.add(DnaLexicon.DESCRIPTION);
+            skipProperties.add(DnaLexicon.CLASSNAME);
+            skipProperties.add(DnaLexicon.CLASSPATH);
+            skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
+            Set<String> skipNamespaces = new HashSet<String>();
+            skipNamespaces.add(JcrLexicon.Namespace.URI);
+            skipNamespaces.add(JcrNtLexicon.Namespace.URI);
+            skipNamespaces.add(JcrMixLexicon.Namespace.URI);
+
+            for (Location sequencerLocation : subgraph.getRoot().getChildren()) {
+                Node sequencerNode = subgraph.getNode(sequencerLocation);
+                String name = stringValueOf(context, sequencerNode, DnaLexicon.READABLE_NAME);
+                String desc = stringValueOf(context, sequencerNode, DnaLexicon.DESCRIPTION);
+                String classname = stringValueOf(context, sequencerNode, DnaLexicon.CLASSNAME);
+                String[] classpath = stringValuesOf(context, sequencerNode, DnaLexicon.CLASSPATH);
+                Map<String, Object> properties = new HashMap<String, Object>();
+                for (Property property : sequencerNode.getProperties()) {
+                    Name propertyName = property.getName();
+                    if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
+                    if (skipProperties.contains(propertyName)) continue;
+                    if (property.isSingle()) {
+                        properties.put(propertyName.getLocalName(), property.getFirstValue());
+                    } else {
+                        properties.put(propertyName.getLocalName(), property.getValuesAsArray());
+                    }
+                }
+                MimeTypeDetectorConfig config = new MimeTypeDetectorConfig(name, desc, properties, classname, classpath);
+                detectors.add(config);
+            }
+        } catch (PathNotFoundException e) {
+            // no detectors registered ...
+        }
+        return detectors;
+    }
+
+    protected static List<SequencerConfig> loadSequencingConfigurations( Problems problems,
+                                                                         ExecutionContext context,
+                                                                         DnaConfiguration.Source source ) {
+        List<SequencerConfig> configs = new ArrayList<SequencerConfig>();
+        Graph graph = Graph.create(source.source, context);
+        Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(source.path, DnaLexicon.SEQUENCERS);
+        try {
+            Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
+
+            Set<Name> skipProperties = new HashSet<Name>();
+            skipProperties.add(DnaLexicon.READABLE_NAME);
+            skipProperties.add(DnaLexicon.DESCRIPTION);
+            skipProperties.add(DnaLexicon.CLASSNAME);
+            skipProperties.add(DnaLexicon.CLASSPATH);
+            skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
+            Set<String> skipNamespaces = new HashSet<String>();
+            skipNamespaces.add(JcrLexicon.Namespace.URI);
+            skipNamespaces.add(JcrNtLexicon.Namespace.URI);
+            skipNamespaces.add(JcrMixLexicon.Namespace.URI);
+
+            for (Location sequencerLocation : subgraph.getRoot().getChildren()) {
+                Node sequencerNode = subgraph.getNode(sequencerLocation);
+                String name = stringValueOf(context, sequencerNode, DnaLexicon.READABLE_NAME);
+                String desc = stringValueOf(context, sequencerNode, DnaLexicon.DESCRIPTION);
+                String classname = stringValueOf(context, sequencerNode, DnaLexicon.CLASSNAME);
+                String[] classpath = stringValuesOf(context, sequencerNode, DnaLexicon.CLASSPATH);
+                String[] expressionStrings = stringValuesOf(context, sequencerNode, DnaLexicon.PATH_EXPRESSIONS);
+                List<PathExpression> pathExpressions = new ArrayList<PathExpression>();
+                if (expressionStrings != null) {
+                    for (String expressionString : expressionStrings) {
+                        try {
+                            pathExpressions.add(PathExpression.compile(expressionString));
+                        } catch (Throwable t) {
+                            problems.addError(t,
+                                              RepositoryI18n.pathExpressionIsInvalidOnSequencer,
+                                              expressionString,
+                                              name,
+                                              t.getLocalizedMessage());
+                        }
+                    }
+                }
+                String[] goodExpressionStrings = new String[pathExpressions.size()];
+                for (int i = 0; i != pathExpressions.size(); ++i) {
+                    PathExpression expression = pathExpressions.get(i);
+                    goodExpressionStrings[i] = expression.getExpression();
+                }
+                Map<String, Object> properties = new HashMap<String, Object>();
+                for (Property property : sequencerNode.getProperties()) {
+                    Name propertyName = property.getName();
+                    if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
+                    if (skipProperties.contains(propertyName)) continue;
+                    if (property.isSingle()) {
+                        properties.put(propertyName.getLocalName(), property.getFirstValue());
+                    } else {
+                        properties.put(propertyName.getLocalName(), property.getValuesAsArray());
+                    }
+                }
+                SequencerConfig config = new SequencerConfig(name, desc, properties, classname, classpath, goodExpressionStrings);
+                configs.add(config);
+            }
+        } catch (PathNotFoundException e) {
+            // no detectors registered ...
+        }
+        return configs;
+    }
+
+    private static String stringValueOf( ExecutionContext context,
+                                         Node node,
+                                         Name propertyName ) {
+        Property property = node.getProperty(propertyName);
+        if (property == null) return null;
+        if (property.isEmpty()) return null;
+        return context.getValueFactories().getStringFactory().create(property.getFirstValue());
+    }
+
+    private static String[] stringValuesOf( ExecutionContext context,
+                                            Node node,
+                                            Name propertyName ) {
+        Property property = node.getProperty(propertyName);
+        if (property == null) return null;
+        return context.getValueFactories().getStringFactory().create(property.getValuesAsArray());
+    }
+
+    /**
+     * Get the problems that were encountered when setting up this engine from the configuration.
+     * 
+     * @return the problems, which may be empty but will never be null
+     */
+    public Problems getProblems() {
+        return problems;
+    }
+
     /*
      * Lookup methods
      */

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -0,0 +1,45 @@
+/*
+ * 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.repository;
+
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * @author Randall Hauch
+ */
+public class DnaLexicon extends org.jboss.dna.graph.DnaLexicon {
+
+    public static final Name SOURCES = new BasicName(Namespace.URI, "sources");
+    public static final Name SOURCE = new BasicName(Namespace.URI, "source");
+    public static final Name READABLE_NAME = new BasicName(Namespace.URI, "readableName");
+    public static final Name DESCRIPTION = new BasicName(Namespace.URI, "description");
+    public static final Name SEQUENCERS = new BasicName(Namespace.URI, "sequencers");
+    public static final Name SEQUENCER = new BasicName(Namespace.URI, "sequencer");
+    public static final Name PATH_EXPRESSIONS = new BasicName(Namespace.URI, "pathExpressions");
+    public static final Name MIME_TYPE_DETECTORS = new BasicName(Namespace.URI, "mimeTypeDetectors");
+    public static final Name MIME_TYPE_DETECTOR = new BasicName(Namespace.URI, "mimeTypeDetector");
+    public static final Name RETRY_LIMIT = new BasicName(Namespace.URI, "retryLimit");
+    public static final Name DEFAULT_CACHE_POLICY = new BasicName(Namespace.URI, "defaultCachePolicy");
+}


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

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -37,6 +37,7 @@
     public static I18n errorCreatingInstanceOfClass;
     public static I18n errorCreatingInstanceOfClassUsingClassLoaders;
     public static I18n errorSettingJavaBeanPropertyOnInstanceOfClass;
+    public static I18n pathExpressionIsInvalidOnSequencer;
 
     // Services and Repository
     public static I18n invalidStateString;
@@ -113,6 +114,7 @@
 
     // General
     public static I18n invalidRepositoryNodePath;
+    public static I18n unableToLoadClass;
     public static I18n unableToLoadClassUsingClasspath;
     public static I18n unableToInstantiateClassUsingClasspath;
     public static I18n unableToAccessClassUsingClasspath;

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -35,9 +35,9 @@
 import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.common.util.Reflection;
 import org.jboss.dna.connector.federation.FederationException;
-import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.Subgraph;
@@ -205,7 +205,8 @@
 
             // Read the configuration and repository source nodes (children under "/dna:sources") ...
             Graph graph = Graph.create(getConfigurationSourceName(), sources, context);
-            Path pathToSourcesNode = context.getValueFactories().getPathFactory().create(pathToConfigurationRoot, "dna:sources");
+            Path pathToSourcesNode = context.getValueFactories().getPathFactory().create(pathToConfigurationRoot,
+                                                                                         DnaLexicon.SOURCES);
             try {
                 String workspaceName = getConfigurationWorkspaceName();
                 if (workspaceName != null) graph.useWorkspace(workspaceName);
@@ -266,14 +267,10 @@
             problems.addError(err, RepositoryI18n.unableToInstantiateClassUsingClasspath, classname, classpath);
         }
 
-        // We need to set the name using the local name of the node, so hack this by putting another name property
-        // into the map of properties. Since only the local name is used, we don't care what the namespace of the
-        // fake property name is, so create something bogus. However, it really won't hurt if this happens to override an existing
-        // property.
-        String fakeUri = DnaLexicon.Namespace.URI + "/300939b9dg93kd9gb";
-        Name nameName = context.getValueFactories().getNameFactory().create(fakeUri, "name");
-        Property nameProperty = context.getPropertyFactory().create(nameName, path.getLastSegment().getName().getLocalName());
-        properties.put(nameName, nameProperty);
+        // We need to set the name using the local name of the node...
+        Property nameProperty = context.getPropertyFactory().create(JcrLexicon.NAME,
+                                                                    path.getLastSegment().getName().getLocalName());
+        properties.put(JcrLexicon.NAME, nameProperty);
 
         // Now set all the properties that we can, ignoring any property that doesn't fit the pattern ...
         Reflection reflection = new Reflection(source.getClass());

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -34,21 +34,9 @@
 import org.jboss.dna.repository.RepositoryI18n;
 
 /**
- * A SessionFactory implementation that creates {@link Session} instances using {@link Repository} instances registered in JNDI.
+ * A SessionFactory implementation that creates {@link Session} instances from a map of named {@link Repository} references
+ * managed by this factory.
  * <p>
- * This factory using a naming convention where the name supplied to the {@link #createSession(String)} contains both the name of
- * the repository and the name of the workspace. Typically, this is <i><code>repositoryName/workspaceName</code></i>, where
- * <code>repositoryName</code> is the JNDI name under which the Repository instance was bound, and <code>workspaceName</code>
- * is the name of the workspace. Note that this method looks for the last delimiter in the whole name to distinguish between the
- * repository and workspace names.
- * </p>
- * <p>
- * For example, if "<code>java:comp/env/repository/dataRepository/myWorkspace</code>" is passed to the
- * {@link #createSession(String)} method, this factory will look for a {@link Repository} instance registered in JDNI with the
- * name "<code>java:comp/env/repository/dataRepository</code>" and use it to {@link Repository#login(String) create a session}
- * to the workspace named "<code>myWorkspace</code>".
- * </p>
- * <p>
  * By default, this factory creates an anonymous JCR session. To use sessions with specific {@link Credentials}, simply
  * {@link #registerCredentials(String, Credentials) register} credentials for the appropriate repository/workspace name. For
  * security reasons, it is not possible to retrieve the Credentials once registered with this factory.

Modified: trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
===================================================================
--- trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2009-04-23 16:43:27 UTC (rev 849)
@@ -24,6 +24,7 @@
 errorCreatingInstanceOfClass = Attempt to create instance of {0} resulted in error: {1}
 errorCreatingInstanceOfClassUsingClassLoaders = Attempt to create instance of {0} using classloader path {1} resulted in error: {2}
 errorSettingJavaBeanPropertyOnInstanceOfClass = Attempt to set {0} property on {1} instance resulted in error: {2}
+pathExpressionIsInvalidOnSequencer = The path expression "{0}" is invalid on the "{1}" sequencer: {2}
 
 invalidStateString = Invalid state parameter "{0}"
 serviceShutdowAndMayNotBeStarted = The {0} has been shutdown and may not be (re)started
@@ -93,6 +94,7 @@
 errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService = Error unregistering workspace listener while shutting down observation service
 
 invalidRepositoryNodePath = The repository node path "{0}" is not valid: {1}
+unableToLoadClass = Unable to load class "{0}"
 unableToLoadClassUsingClasspath = Unable to load class "{0}" using classpath "{1}"
 unableToInstantiateClassUsingClasspath = Unable to instantiate class "{0}" using classpath "{1}"
 unableToAccessClassUsingClasspath = Unable to access class "{0}" using classpath "{1}"

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -24,16 +24,18 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.IsNull.notNullValue;
-import static org.hamcrest.core.IsNull.nullValue;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
+import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
+import java.util.UUID;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.cache.ImmutableCachePolicy;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.mimetype.MimeTypeDetector;
-import org.jboss.dna.repository.DnaConfiguration.DnaSequencerDetails;
+import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.repository.sequencer.MockSequencerA;
-import org.jboss.dna.repository.sequencer.MockSequencerB;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -51,6 +53,10 @@
         configuration = new DnaConfiguration();
     }
 
+    protected Path.Segment segment( String segment ) {
+        return context.getValueFactories().getPathFactory().createSegment(segment);
+    }
+
     @Test
     public void shouldAllowCreatingWithNoArguments() {
         configuration = new DnaConfiguration();
@@ -62,6 +68,11 @@
     }
 
     @Test
+    public void shouldHaveDefaultConfigurationSourceIfNotSpecified() {
+        assertThat(configuration.graph(), is(notNullValue()));
+    }
+
+    @Test
     public void shouldAllowSpecifyingConfigurationRepository() {
         DnaConfiguration config = configuration.withConfigurationRepository()
                                                .usingClass("org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource")
@@ -71,145 +82,225 @@
                                                .setTo(5)
                                                .with("name")
                                                .setTo("repository name")
-                                               .and();
+                                               .and()
+                                               .save();
         assertThat(config, is(notNullValue()));
-        assertThat(config.repositories.get(null), is(nullValue()));
+        assertThat(config.configurationSource.source, is(instanceOf(InMemoryRepositorySource.class)));
+        InMemoryRepositorySource source = (InMemoryRepositorySource)config.configurationSource.source;
+        assertThat(source.getName(), is("repository name"));
+        assertThat(source.getRetryLimit(), is(5));
     }
 
     @Test
     public void shouldAllowAddingRepositorySourceInstance() {
-        RepositorySource newSource = mock(RepositorySource.class);
-        configuration.addRepository(newSource);
+        UUID rootUuid = UUID.randomUUID();
+        CachePolicy cachePolicy = new ImmutableCachePolicy(100);
+        InMemoryRepositorySource newSource = new InMemoryRepositorySource();
+        newSource.setName("name");
+        newSource.setDefaultCachePolicy(cachePolicy);
+        newSource.setDefaultWorkspaceName("default workspace name");
+        newSource.setRetryLimit(100);
+        newSource.setRootNodeUuid(rootUuid);
+
+        // Update the configuration and save it ...
+        configuration.addRepository(newSource).save();
+
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/name"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.READABLE_NAME, "name"));
+        assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.RETRY_LIMIT, 100));
+        assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.DEFAULT_CACHE_POLICY, cachePolicy));
+        assertThat(subgraph.getNode("/dna:sources/name"), hasProperty("defaultWorkspaceName", "default workspace name"));
+        assertThat(subgraph.getNode("/dna:sources/name"), hasProperty("rootNodeUuid", rootUuid));
     }
 
     @Test
     public void shouldAllowAddingRepositorySourceByClassNameAndSettingProperties() {
-        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
-                                               .usingClass("org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource")
-                                               .loadedFromClasspath()
-                                               .describedAs("description")
-                                               .with("retryLimit")
-                                               .setTo(5)
-                                               .with("name")
-                                               .setTo("repository name")
-                                               .and();
-        assertThat(config, is(notNullValue()));
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class.getName())
+                     .loadedFromClasspath()
+                     .describedAs("description")
+                     .with("retryLimit")
+                     .setTo(5)
+                     .and()
+                     .save();
 
-        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
-        assertThat(source, is(notNullValue()));
-        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
+    }
 
-        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
-        assertThat(isource.getRetryLimit(), is(5));
-        assertThat(isource.getName(), is("repository name"));
+    @Test
+    public void shouldAllowAddingRepositorySourceByClassNameAndClasspathAndSettingProperties() {
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class.getName())
+                     .loadedFrom("cp1", "cp2")
+                     .describedAs("description")
+                     .with("retryLimit")
+                     .setTo(5)
+                     .and()
+                     .save();
+
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSPATH, "cp1", "cp2"));
     }
 
     @Test
     public void shouldAllowAddingRepositorySourceByClassReferenceAndSettingProperties() {
-        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
-                                               .usingClass(InMemoryRepositorySource.class)
-                                               .describedAs("description")
-                                               .with("retryLimit")
-                                               .setTo(5)
-                                               .with("name")
-                                               .setTo("repository name")
-                                               .and();
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class)
+                     .describedAs("description")
+                     .with("retryLimit")
+                     .setTo(5)
+                     .and()
+                     .save();
 
-        assertThat(config, is(notNullValue()));
-        assertThat(config.repositories.get(null), is(nullValue()));
-
-        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
-        assertThat(source, is(notNullValue()));
-        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
-
-        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
-        assertThat(isource.getRetryLimit(), is(5));
-        assertThat(isource.getName(), is("repository name"));
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
     }
 
     @Test
     public void shouldAllowOverwritingRepositorySourceByRepositoryName() {
-        DnaConfiguration config = configuration.addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
-                                               .usingClass(InMemoryRepositorySource.class)
-                                               .describedAs("description")
-                                               .with("retryLimit")
-                                               .setTo(3)
-                                               .and()
-                                               .addRepository(DnaEngine.CONFIGURATION_REPOSITORY_NAME)
-                                               .usingClass(InMemoryRepositorySource.class)
-                                               .describedAs("description")
-                                               .with("name")
-                                               .setTo("repository name")
-                                               .and();
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class)
+                     .describedAs("description")
+                     .with("retryLimit")
+                     .setTo(3)
+                     .and()
+                     .addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class)
+                     .describedAs("new description")
+                     .with("retryLimit")
+                     .setTo(6)
+                     .and()
+                     .save();
 
-        assertThat(config, is(notNullValue()));
-        assertThat(config.repositories.get(null), is(nullValue()));
-
-        RepositorySource source = config.repositories.get(DnaEngine.CONFIGURATION_REPOSITORY_NAME).getRepositorySource();
-        assertThat(source, is(notNullValue()));
-        assertThat(source, is(instanceOf(InMemoryRepositorySource.class)));
-
-        InMemoryRepositorySource isource = (InMemoryRepositorySource)source;
-        // This relies on the default retry limit for an InMemoryRepositorySource being 0
-        // If the original source was not overwritten, this would be 3
-        assertThat(isource.getRetryLimit(), is(0));
-        assertThat(isource.getName(), is("repository name"));
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 6));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "new description"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
     }
 
     @Test
     public void shouldAllowAddingMimeTypeDetector() {
-        RepositorySource newSource = mock(RepositorySource.class);
-        MimeTypeDetector newDetector = mock(MimeTypeDetector.class);
-        DnaConfiguration config = configuration.addRepository(newSource)
-                                               .addMimeTypeDetector("default")
-                                               .usingClass(newDetector.getClass())
-                                               .describedAs("default mime type detector")
-                                               .and();
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class)
+                     .describedAs("description")
+                     .and()
+                     .addMimeTypeDetector("detector")
+                     .usingClass(ExtensionBasedMimeTypeDetector.class)
+                     .describedAs("default detector")
+                     .and()
+                     .save();
 
-        assertThat(config, is(notNullValue()));
-        assertThat(config.mimeTypeDetectors.get("default").getMimeTypeDetector(), instanceOf(MimeTypeDetector.class));
-        assertThat(config.mimeTypeDetectors.get("invalid name"), is(nullValue()));
-        assertThat(config.mimeTypeDetectors.get("default").getDescription(), is("default mime type detector"));
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors").getChildren(), hasChild(segment("detector")));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.READABLE_NAME, "detector"));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.DESCRIPTION, "default detector"));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"),
+                   hasProperty(DnaLexicon.CLASSNAME, ExtensionBasedMimeTypeDetector.class.getName()));
     }
 
     @Test
     public void shouldAllowAddingSequencer() {
-        RepositorySource newSource = mock(RepositorySource.class);
+        // Update the configuration and save it ...
+        configuration.addRepository("Source1")
+                     .usingClass(InMemoryRepositorySource.class)
+                     .describedAs("description")
+                     .named("A Source")
+                     .and()
+                     .addSequencer("sequencerA")
+                     .usingClass(MockSequencerA.class)
+                     .named("The (Main) Sequencer")
+                     .describedAs("Mock Sequencer A")
+                     // .sequencingFrom("/foo/source")
+                     // .andOutputtingTo("/foo/target")
+                     .sequencingFrom("/bar/source")
+                     .andOutputtingTo("/bar/target")
+                     .and()
+                     .save();
 
-        DnaConfiguration config = configuration.addRepository(newSource)
-                                               .addSequencer("sequencerA")
-                                               .usingClass(MockSequencerA.class)
-                                               .describedAs("Mock Sequencer A")
-                                               .sequencingFrom("/foo/source")
-                                               .andOutputtingTo("/foo/target")
-                                               .sequencingFrom("/bar/source")
-                                               .andOutputtingTo("/bar/target")
-                                               .and()
-                                               .addSequencer("sequencerB")
-                                               .usingClass(MockSequencerB.class)
-                                               .sequencingFrom("/baz/source")
-                                               .andOutputtingTo("/baz/target")
-                                               .describedAs("Mock Sequencer B")
-                                               .and();
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "A Source"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
+        assertThat(subgraph.getNode("/dna:sequencers").getChildren(), hasChild(segment("sequencerA")));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.READABLE_NAME, "The (Main) Sequencer"));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.DESCRIPTION, "Mock Sequencer A"));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                               MockSequencerA.class.getName()));
+        System.out.println(subgraph.getNode("/dna:sequencers/sequencerA").getProperty(DnaLexicon.PATH_EXPRESSIONS));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.PATH_EXPRESSIONS,
+        // "/foo/source => /foo/target",
+                                                                               "/bar/source => /bar/target"));
+    }
 
-        assertThat(config, is(notNullValue()));
-        assertThat(config.sequencers.get("default"), is(nullValue()));
-        assertThat(config.sequencers.get("sequencerA").getSequencer(), instanceOf(MockSequencerA.class));
-        assertThat(config.sequencers.get("sequencerB").getSequencer(), instanceOf(MockSequencerB.class));
+    @Test
+    public void shouldAllowConfigurationInMultipleSteps() {
+        configuration.addRepository("Source1").usingClass(InMemoryRepositorySource.class).describedAs("description");
+        configuration.addMimeTypeDetector("detector")
+                     .usingClass(ExtensionBasedMimeTypeDetector.class)
+                     .describedAs("default detector");
+        configuration.save();
 
-        DnaSequencerDetails detailsA = config.sequencers.get("sequencerA");
-        assertThat(detailsA.getDescription(), is("Mock Sequencer A"));
-        assertThat(detailsA.sourcePathExpressions.size(), is(2));
-        assertThat(detailsA.sourcePathExpressions.get(0).toString(), is("/foo/source"));
-        assertThat(detailsA.targetPathExpressions.get(0).toString(), is("/foo/target"));
-        assertThat(detailsA.sourcePathExpressions.get(1).toString(), is("/bar/source"));
-        assertThat(detailsA.targetPathExpressions.get(1).toString(), is("/bar/target"));
-
-        DnaSequencerDetails detailsB = config.sequencers.get("sequencerB");
-        assertThat(detailsB.getDescription(), is("Mock Sequencer B"));
-        assertThat(detailsB.sourcePathExpressions.size(), is(1));
-        assertThat(detailsB.sourcePathExpressions.get(0).toString(), is("/baz/source"));
-        assertThat(detailsB.targetPathExpressions.get(0).toString(), is("/baz/target"));
+        // Verify that the graph has been updated correctly ...
+        Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+        assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+        assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                         InMemoryRepositorySource.class.getName()));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors").getChildren(), hasChild(segment("detector")));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.READABLE_NAME, "detector"));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.DESCRIPTION, "default detector"));
+        assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"),
+                   hasProperty(DnaLexicon.CLASSNAME, ExtensionBasedMimeTypeDetector.class.getName()));
     }
-
 }

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java	2009-04-23 16:29:28 UTC (rev 848)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaEngineTest.java	2009-04-23 16:43:27 UTC (rev 849)
@@ -63,12 +63,13 @@
 
     @Test
     public void shouldAllowCreatingWithConfigRepository() throws InterruptedException {
-        engine = new DnaConfiguration()
-            .withConfigurationRepository()
-            .usingClass(InMemoryRepositorySource.class)
-            .describedAs("Configuration Repository")
-            .with("name").setTo("config repo")
-            .and().build();
+        engine = new DnaConfiguration().withConfigurationRepository()
+                                       .usingClass(InMemoryRepositorySource.class)
+                                       .describedAs("Configuration Repository")
+                                       .with("name")
+                                       .setTo("config repo")
+                                       .and()
+                                       .build();
 
         assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
         assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
@@ -84,17 +85,23 @@
 
     @Test
     public void shouldAllowCreatingMultipleRepositories() throws Exception {
-        engine = new DnaConfiguration()
-            .withConfigurationRepository()
-            .usingClass(InMemoryRepositorySource.class)
-            .describedAs("Configuration Repository")
-            .with("name").setTo("config repo")
-            .and().addRepository("JCR")
-            .usingClass(InMemoryRepositorySource.class)
-            .describedAs("Backing Repository for JCR Implementation")
-            .with("name").setTo("JCR")
-            .and().build();
+        engine = new DnaConfiguration().withConfigurationRepository()
+                                       .usingClass(InMemoryRepositorySource.class)
+                                       .describedAs("Configuration Repository")
+                                       .with("name")
+                                       .setTo("config repo")
+                                       .and()
+                                       .addRepository("JCR")
+                                       .usingClass(InMemoryRepositorySource.class)
+                                       .describedAs("Backing Repository for JCR Implementation")
+                                       .with("name")
+                                       .setTo("JCR")
+                                       .and()
+                                       .build();
+        // Start the engine ...
+        engine.start();
 
+        // Verify the components are here ...
         assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
         assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
 
@@ -111,46 +118,52 @@
 
     @Test
     public void shouldAllowAddingMimeTypeDetectors() throws Exception {
-        engine = new DnaConfiguration()
-            .withConfigurationRepository()
-            .usingClass(InMemoryRepositorySource.class)
-            .describedAs("Configuration Repository")
-            .with("name").setTo("config repo")
-            .and().addMimeTypeDetector("default")
-            .usingClass(MockMimeTypeDetector.class)
-            .describedAs("Default MimeTypeDetector")
-            .with("mimeType").setTo("mock")
-            .and().build();
+        engine = new DnaConfiguration().withConfigurationRepository()
+                                       .usingClass(InMemoryRepositorySource.class)
+                                       .describedAs("Configuration Repository")
+                                       .with("name")
+                                       .setTo("config repo")
+                                       .and()
+                                       .addMimeTypeDetector("default")
+                                       .usingClass(MockMimeTypeDetector.class)
+                                       .describedAs("Default MimeTypeDetector")
+                                       .with("mimeType")
+                                       .setTo("mock")
+                                       .and()
+                                       .build();
 
         assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
         assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
 
         RepositoryLibrary library = engine.getRepositoryService().getRepositorySourceManager();
         MimeTypeDetectors detectors = library.getMimeTypeDetectors();
-        
+
         assertThat(detectors.mimeTypeOf("test", new ByteArrayInputStream("This is useless data".getBytes())), is("mock"));
     }
-    
+
     @Test
     public void shouldAllowAddingSequencers() throws Exception {
-        engine = new DnaConfiguration()
-            .withConfigurationRepository()
-            .usingClass(InMemoryRepositorySource.class)
-            .describedAs("Configuration Repository")
-            .with("name").setTo("config repo")
-            .and().addSequencer("Mock Sequencer A")
-            .usingClass(MockSequencerA.class)
-            .describedAs("A Mock Sequencer")
-            .sequencingFrom("/**").andOutputtingTo("/")
-            .and().build();
+        engine = new DnaConfiguration().withConfigurationRepository()
+                                       .usingClass(InMemoryRepositorySource.class)
+                                       .describedAs("Configuration Repository")
+                                       .with("name")
+                                       .setTo("config repo")
+                                       .and()
+                                       .addSequencer("Mock Sequencer A")
+                                       .usingClass(MockSequencerA.class)
+                                       .describedAs("A Mock Sequencer")
+                                       .sequencingFrom("/**")
+                                       .andOutputtingTo("/")
+                                       .and()
+                                       .build();
 
         assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
         assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
 
         SequencingService sequencer = engine.getSequencingService();
         assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(0L));
-        
-        NodeChanges changes = NodeChanges.create("", Arrays.asList(new Event[] { }));
+
+        NodeChanges changes = NodeChanges.create("", Arrays.asList(new Event[] {}));
         sequencer.onNodeChanges(changes);
 
         assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(0L));
@@ -159,33 +172,32 @@
         stub(e1.getType()).toReturn(Event.NODE_ADDED);
         stub(e1.getPath()).toReturn("/test");
         stub(e1.getUserID()).toReturn("Test");
-        
-//        changes = NodeChanges.create("", Arrays.asList(new Event[] { e1, }));
-//        sequencer.onNodeChanges(changes);
-//        
-//        // Shutdown the engine to force all pending tasks to complete
-//        engine.shutdown();
-//        
-//        assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(1L));
 
-    }    
+        // changes = NodeChanges.create("", Arrays.asList(new Event[] { e1, }));
+        // sequencer.onNodeChanges(changes);
+        //        
+        // // Shutdown the engine to force all pending tasks to complete
+        // engine.shutdown();
+        //        
+        // assertThat(sequencer.getStatistics().getNumberOfNodesSequenced(), is(1L));
 
+    }
+
     public static class MockMimeTypeDetector implements MimeTypeDetector {
         private String mimeType = "";
-        
+
         public MockMimeTypeDetector() {
-            
+
         }
 
-        public void setMimeType(String mimeType) {
+        public void setMimeType( String mimeType ) {
             this.mimeType = mimeType;
         }
-        
+
         public String mimeTypeOf( String name,
                                   InputStream is ) {
             return mimeType;
         }
     }
 
-
 }



From dna-commits at lists.jboss.org  Thu Apr 23 14:09:02 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 14:09:02 -0400
Subject: [dna-commits] DNA SVN: r850 - in trunk/dna-repository/src:
	test/java/org/jboss/dna/repository and 1 other directory.
Message-ID: <E1Lx3M6-0001hc-1V@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 14:09:01 -0400 (Thu, 23 Apr 2009)
New Revision: 850

Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
Log:
DNA-58 Create repository configuration and component

Changed DnaConfiguration to make it more tolernant of changes to the source pointing to the configuration repository.  In particular, this allows the DnaEngine to obtain an immutable definition of the configuration source, which allows the engine to properly access the source even if the DnaConfiguration instance is changed.

The DnaEngine was also refactored to pull into a nested scanner class the code that reads the configuration content.

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 16:43:27 UTC (rev 849)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 18:09:01 UTC (rev 850)
@@ -41,6 +41,7 @@
 import org.jboss.dna.graph.property.PathExpression;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.RootPath;
 import org.jboss.dna.repository.sequencer.Sequencer;
 
 /**
@@ -61,14 +62,56 @@
         NAMES_TO_MAP = Collections.unmodifiableMap(names);
     }
 
-    protected class Source {
-        protected RepositorySource source;
-        protected String description;
-        protected Path path;
+    @Immutable
+    protected class ConfigurationRepository {
+        private final RepositorySource source;
+        private final String description;
+        private final Path path;
+
+        protected ConfigurationRepository( RepositorySource source ) {
+            this(source, null, null);
+        }
+
+        protected ConfigurationRepository( RepositorySource source,
+                                           String description,
+                                           Path path ) {
+            this.source = source;
+            this.description = description != null ? description : "";
+            this.path = path != null ? path : RootPath.INSTANCE;
+        }
+
+        /**
+         * @return source
+         */
+        public RepositorySource getRepositorySource() {
+            return source;
+        }
+
+        /**
+         * @return description
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * @return path
+         */
+        public Path getPath() {
+            return path;
+        }
+
+        public ConfigurationRepository with( String description ) {
+            return new ConfigurationRepository(source, description, path);
+        }
+
+        public ConfigurationRepository with( Path path ) {
+            return new ConfigurationRepository(source, description, path);
+        }
     }
 
     private final ExecutionContext context;
-    protected Source configurationSource;
+    protected ConfigurationRepository configurationSource;
     private Path sourcesPath;
     private Path sequencersPath;
     private Path detectorsPath;
@@ -102,13 +145,10 @@
      * 
      * @return the default repository source
      */
-    protected Source createDefaultConfigurationSource() {
+    protected ConfigurationRepository createDefaultConfigurationSource() {
         InMemoryRepositorySource defaultSource = new InMemoryRepositorySource();
         defaultSource.setName("Configuration");
-        Source result = new Source();
-        result.source = defaultSource;
-        result.path = this.context.getValueFactories().getPathFactory().createRootPath();
-        result.description = "Configuration Repository";
+        ConfigurationRepository result = new ConfigurationRepository(defaultSource, "Configuration Repository", null);
         return result;
     }
 
@@ -129,7 +169,7 @@
      */
     protected final Graph graph() {
         if (this.graph == null) {
-            this.graph = Graph.create(configurationSource.source, context);
+            this.graph = Graph.create(configurationSource.getRepositorySource(), context);
         }
         return this.graph;
     }
@@ -170,7 +210,6 @@
      *         be used for the configuration repository; never null
      */
     public ChooseClass<RepositorySource, ConfigRepositoryDetails> withConfigurationRepository() {
-        final Source source = this.configurationSource;
         // The config repository is different, since it has to load immediately ...
         return new ChooseClass<RepositorySource, ConfigRepositoryDetails>() {
             public LoadedFrom<ConfigRepositoryDetails> usingClass( final String className ) {
@@ -203,7 +242,7 @@
 
             public ConfigRepositoryDetails usingClass( Class<? extends RepositorySource> repositorySource ) {
                 try {
-                    source.source = repositorySource.newInstance();
+                    DnaConfiguration.this.configurationSource = new ConfigurationRepository(repositorySource.newInstance());
                 } catch (InstantiationException err) {
                     I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
                     throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
@@ -318,7 +357,7 @@
     protected Path sourcesPath() {
         // Make sure the "dna:sources" node is there
         if (sourcesPath == null) {
-            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.SOURCES);
+            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SOURCES);
             Node node = graph().createIfMissing(path).andReturn();
             this.sourcesPath = node.getLocation().getPath();
         }
@@ -328,7 +367,7 @@
     protected Path sequencersPath() {
         // Make sure the "dna:sequencers" node is there
         if (sequencersPath == null) {
-            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.SEQUENCERS);
+            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SEQUENCERS);
             Node node = graph().createIfMissing(path).andReturn();
             this.sequencersPath = node.getLocation().getPath();
         }
@@ -338,7 +377,7 @@
     protected Path detectorsPath() {
         // Make sure the "dna:mimeTypeDetectors" node is there
         if (detectorsPath == null) {
-            Path path = pathFactory().create(this.configurationSource.path, DnaLexicon.MIME_TYPE_DETECTORS);
+            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.MIME_TYPE_DETECTORS);
             Node node = graph().createIfMissing(path).andReturn();
             this.detectorsPath = node.getLocation().getPath();
         }
@@ -612,7 +651,7 @@
          * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
          */
         public ConfigRepositoryDetails describedAs( String description ) {
-            DnaConfiguration.this.configurationSource.description = description;
+            DnaConfiguration.this.configurationSource = DnaConfiguration.this.configurationSource.with(description);
             return this;
         }
 
@@ -622,7 +661,8 @@
          * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
          */
         public PropertySetter<ConfigRepositoryDetails> with( String propertyName ) {
-            return new BeanPropertySetter<ConfigRepositoryDetails>(DnaConfiguration.this.configurationSource.source,
+            return new BeanPropertySetter<ConfigRepositoryDetails>(
+                                                                   DnaConfiguration.this.configurationSource.getRepositorySource(),
                                                                    propertyName, this);
         }
 
@@ -633,7 +673,8 @@
          */
         public ConfigRepositoryDetails under( String path ) {
             CheckArg.isNotNull(path, "path");
-            DnaConfiguration.this.configurationSource.path = context().getValueFactories().getPathFactory().create(path);
+            Path newPath = context().getValueFactories().getPathFactory().create(path);
+            DnaConfiguration.this.configurationSource = DnaConfiguration.this.configurationSource.with(newPath);
             return null;
         }
 

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 16:43:27 UTC (rev 849)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 18:09:01 UTC (rev 850)
@@ -61,13 +61,17 @@
 import org.jboss.dna.repository.util.SimpleSessionFactory;
 
 /**
- * @author Randall Hauch
+ * A single instance of the DNA services, which is obtained after setting up the {@link DnaConfiguration#build() configuration}.
+ * 
+ * @see DnaConfiguration
  */
 @Immutable
 public class DnaEngine {
 
     public static final String CONFIGURATION_REPOSITORY_NAME = "dna:configuration";
 
+    private final DnaConfiguration.ConfigurationRepository configuration;
+    private final ConfigurationScanner scanner;
     private final Problems problems;
     private final ExecutionContext context;
     private final List<AdministeredService> services;
@@ -85,17 +89,20 @@
         // Use the configuration's context ...
         this.context = configuration.context();
 
+        // And set up the scanner ...
+        this.configuration = configuration.configurationSource;
+        this.scanner = new ConfigurationScanner(this.problems, this.context, this.configuration);
+
         // Add the configuration source to the repository library ...
-        final DnaConfiguration.Source configSourceInfo = configuration.configurationSource;
-        final RepositorySource configSource = configSourceInfo.source;
+        final RepositorySource configSource = this.configuration.getRepositorySource();
         RepositoryLibrary library = new RepositoryLibrary();
         library.addSource(configSource);
 
         // Create the RepositoryService, pointing it to the configuration repository ...
-        Path pathToConfigurationRoot = configSourceInfo.path;
+        Path pathToConfigurationRoot = this.configuration.getPath();
         repositoryService = new RepositoryService(library, configSource.getName(), "", pathToConfigurationRoot, context);
 
-        for (MimeTypeDetectorConfig config : loadMimeTypeDetectors(problems, context, configSourceInfo)) {
+        for (MimeTypeDetectorConfig config : scanner.getMimeTypeDetectors()) {
             library.getMimeTypeDetectors().addDetector(config);
         }
 
@@ -106,7 +113,7 @@
         JcrExecutionContext jcrContext = new JcrExecutionContext(context, sessionFactory, "");
         sequencingService.setExecutionContext(jcrContext);
         sequencingService.setExecutorService(executorService);
-        for (SequencerConfig sequencerConfig : loadSequencingConfigurations(problems, context, configSourceInfo)) {
+        for (SequencerConfig sequencerConfig : scanner.getSequencingConfigurations()) {
             sequencingService.addSequencer(sequencerConfig);
         }
 
@@ -127,136 +134,6 @@
         };
     }
 
-    protected static List<MimeTypeDetectorConfig> loadMimeTypeDetectors( Problems problems,
-                                                                         ExecutionContext context,
-                                                                         DnaConfiguration.Source source ) {
-        List<MimeTypeDetectorConfig> detectors = new ArrayList<MimeTypeDetectorConfig>();
-        Graph graph = Graph.create(source.source, context);
-        Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(source.path,
-                                                                                        DnaLexicon.MIME_TYPE_DETECTORS);
-        try {
-            Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
-
-            Set<Name> skipProperties = new HashSet<Name>();
-            skipProperties.add(DnaLexicon.READABLE_NAME);
-            skipProperties.add(DnaLexicon.DESCRIPTION);
-            skipProperties.add(DnaLexicon.CLASSNAME);
-            skipProperties.add(DnaLexicon.CLASSPATH);
-            skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
-            Set<String> skipNamespaces = new HashSet<String>();
-            skipNamespaces.add(JcrLexicon.Namespace.URI);
-            skipNamespaces.add(JcrNtLexicon.Namespace.URI);
-            skipNamespaces.add(JcrMixLexicon.Namespace.URI);
-
-            for (Location sequencerLocation : subgraph.getRoot().getChildren()) {
-                Node sequencerNode = subgraph.getNode(sequencerLocation);
-                String name = stringValueOf(context, sequencerNode, DnaLexicon.READABLE_NAME);
-                String desc = stringValueOf(context, sequencerNode, DnaLexicon.DESCRIPTION);
-                String classname = stringValueOf(context, sequencerNode, DnaLexicon.CLASSNAME);
-                String[] classpath = stringValuesOf(context, sequencerNode, DnaLexicon.CLASSPATH);
-                Map<String, Object> properties = new HashMap<String, Object>();
-                for (Property property : sequencerNode.getProperties()) {
-                    Name propertyName = property.getName();
-                    if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
-                    if (skipProperties.contains(propertyName)) continue;
-                    if (property.isSingle()) {
-                        properties.put(propertyName.getLocalName(), property.getFirstValue());
-                    } else {
-                        properties.put(propertyName.getLocalName(), property.getValuesAsArray());
-                    }
-                }
-                MimeTypeDetectorConfig config = new MimeTypeDetectorConfig(name, desc, properties, classname, classpath);
-                detectors.add(config);
-            }
-        } catch (PathNotFoundException e) {
-            // no detectors registered ...
-        }
-        return detectors;
-    }
-
-    protected static List<SequencerConfig> loadSequencingConfigurations( Problems problems,
-                                                                         ExecutionContext context,
-                                                                         DnaConfiguration.Source source ) {
-        List<SequencerConfig> configs = new ArrayList<SequencerConfig>();
-        Graph graph = Graph.create(source.source, context);
-        Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(source.path, DnaLexicon.SEQUENCERS);
-        try {
-            Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
-
-            Set<Name> skipProperties = new HashSet<Name>();
-            skipProperties.add(DnaLexicon.READABLE_NAME);
-            skipProperties.add(DnaLexicon.DESCRIPTION);
-            skipProperties.add(DnaLexicon.CLASSNAME);
-            skipProperties.add(DnaLexicon.CLASSPATH);
-            skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
-            Set<String> skipNamespaces = new HashSet<String>();
-            skipNamespaces.add(JcrLexicon.Namespace.URI);
-            skipNamespaces.add(JcrNtLexicon.Namespace.URI);
-            skipNamespaces.add(JcrMixLexicon.Namespace.URI);
-
-            for (Location sequencerLocation : subgraph.getRoot().getChildren()) {
-                Node sequencerNode = subgraph.getNode(sequencerLocation);
-                String name = stringValueOf(context, sequencerNode, DnaLexicon.READABLE_NAME);
-                String desc = stringValueOf(context, sequencerNode, DnaLexicon.DESCRIPTION);
-                String classname = stringValueOf(context, sequencerNode, DnaLexicon.CLASSNAME);
-                String[] classpath = stringValuesOf(context, sequencerNode, DnaLexicon.CLASSPATH);
-                String[] expressionStrings = stringValuesOf(context, sequencerNode, DnaLexicon.PATH_EXPRESSIONS);
-                List<PathExpression> pathExpressions = new ArrayList<PathExpression>();
-                if (expressionStrings != null) {
-                    for (String expressionString : expressionStrings) {
-                        try {
-                            pathExpressions.add(PathExpression.compile(expressionString));
-                        } catch (Throwable t) {
-                            problems.addError(t,
-                                              RepositoryI18n.pathExpressionIsInvalidOnSequencer,
-                                              expressionString,
-                                              name,
-                                              t.getLocalizedMessage());
-                        }
-                    }
-                }
-                String[] goodExpressionStrings = new String[pathExpressions.size()];
-                for (int i = 0; i != pathExpressions.size(); ++i) {
-                    PathExpression expression = pathExpressions.get(i);
-                    goodExpressionStrings[i] = expression.getExpression();
-                }
-                Map<String, Object> properties = new HashMap<String, Object>();
-                for (Property property : sequencerNode.getProperties()) {
-                    Name propertyName = property.getName();
-                    if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
-                    if (skipProperties.contains(propertyName)) continue;
-                    if (property.isSingle()) {
-                        properties.put(propertyName.getLocalName(), property.getFirstValue());
-                    } else {
-                        properties.put(propertyName.getLocalName(), property.getValuesAsArray());
-                    }
-                }
-                SequencerConfig config = new SequencerConfig(name, desc, properties, classname, classpath, goodExpressionStrings);
-                configs.add(config);
-            }
-        } catch (PathNotFoundException e) {
-            // no detectors registered ...
-        }
-        return configs;
-    }
-
-    private static String stringValueOf( ExecutionContext context,
-                                         Node node,
-                                         Name propertyName ) {
-        Property property = node.getProperty(propertyName);
-        if (property == null) return null;
-        if (property.isEmpty()) return null;
-        return context.getValueFactories().getStringFactory().create(property.getFirstValue());
-    }
-
-    private static String[] stringValuesOf( ExecutionContext context,
-                                            Node node,
-                                            Name propertyName ) {
-        Property property = node.getProperty(propertyName);
-        if (property == null) return null;
-        return context.getValueFactories().getStringFactory().create(property.getValuesAsArray());
-    }
-
     /**
      * Get the problems that were encountered when setting up this engine from the configuration.
      * 
@@ -316,4 +193,149 @@
         }
         executorService.shutdown();
     }
+
+    /**
+     * The component responsible for reading the configuration repository and (eventually) for propagating changes in the
+     * configuration repository into the services.
+     */
+    protected class ConfigurationScanner {
+        private final Problems problems;
+        private final ExecutionContext context;
+        private final DnaConfiguration.ConfigurationRepository configurationRepository;
+
+        protected ConfigurationScanner( Problems problems,
+                                        ExecutionContext context,
+                                        DnaConfiguration.ConfigurationRepository configurationRepository ) {
+            this.problems = problems;
+            this.context = context;
+            this.configurationRepository = configurationRepository;
+        }
+
+        public List<MimeTypeDetectorConfig> getMimeTypeDetectors() {
+            List<MimeTypeDetectorConfig> detectors = new ArrayList<MimeTypeDetectorConfig>();
+            Graph graph = Graph.create(configurationRepository.getRepositorySource(), context);
+            Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(configurationRepository.getPath(),
+                                                                                            DnaLexicon.MIME_TYPE_DETECTORS);
+            try {
+                Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
+
+                Set<Name> skipProperties = new HashSet<Name>();
+                skipProperties.add(DnaLexicon.READABLE_NAME);
+                skipProperties.add(DnaLexicon.DESCRIPTION);
+                skipProperties.add(DnaLexicon.CLASSNAME);
+                skipProperties.add(DnaLexicon.CLASSPATH);
+                skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
+                Set<String> skipNamespaces = new HashSet<String>();
+                skipNamespaces.add(JcrLexicon.Namespace.URI);
+                skipNamespaces.add(JcrNtLexicon.Namespace.URI);
+                skipNamespaces.add(JcrMixLexicon.Namespace.URI);
+
+                for (Location detectorLocation : subgraph.getRoot().getChildren()) {
+                    Node node = subgraph.getNode(detectorLocation);
+                    String name = stringValueOf(node, DnaLexicon.READABLE_NAME);
+                    String desc = stringValueOf(node, DnaLexicon.DESCRIPTION);
+                    String classname = stringValueOf(node, DnaLexicon.CLASSNAME);
+                    String[] classpath = stringValuesOf(node, DnaLexicon.CLASSPATH);
+                    Map<String, Object> properties = new HashMap<String, Object>();
+                    for (Property property : node.getProperties()) {
+                        Name propertyName = property.getName();
+                        if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
+                        if (skipProperties.contains(propertyName)) continue;
+                        if (property.isSingle()) {
+                            properties.put(propertyName.getLocalName(), property.getFirstValue());
+                        } else {
+                            properties.put(propertyName.getLocalName(), property.getValuesAsArray());
+                        }
+                    }
+                    MimeTypeDetectorConfig config = new MimeTypeDetectorConfig(name, desc, properties, classname, classpath);
+                    detectors.add(config);
+                }
+            } catch (PathNotFoundException e) {
+                // no detectors registered ...
+            }
+            return detectors;
+        }
+
+        public List<SequencerConfig> getSequencingConfigurations() {
+            List<SequencerConfig> configs = new ArrayList<SequencerConfig>();
+            Graph graph = Graph.create(configurationRepository.getRepositorySource(), context);
+            Path pathToSequencersNode = context.getValueFactories().getPathFactory().create(configurationRepository.getPath(),
+                                                                                            DnaLexicon.SEQUENCERS);
+            try {
+                Subgraph subgraph = graph.getSubgraphOfDepth(2).at(pathToSequencersNode);
+
+                Set<Name> skipProperties = new HashSet<Name>();
+                skipProperties.add(DnaLexicon.READABLE_NAME);
+                skipProperties.add(DnaLexicon.DESCRIPTION);
+                skipProperties.add(DnaLexicon.CLASSNAME);
+                skipProperties.add(DnaLexicon.CLASSPATH);
+                skipProperties.add(DnaLexicon.PATH_EXPRESSIONS);
+                Set<String> skipNamespaces = new HashSet<String>();
+                skipNamespaces.add(JcrLexicon.Namespace.URI);
+                skipNamespaces.add(JcrNtLexicon.Namespace.URI);
+                skipNamespaces.add(JcrMixLexicon.Namespace.URI);
+
+                for (Location sequencerLocation : subgraph.getRoot().getChildren()) {
+                    Node sequencerNode = subgraph.getNode(sequencerLocation);
+                    String name = stringValueOf(sequencerNode, DnaLexicon.READABLE_NAME);
+                    String desc = stringValueOf(sequencerNode, DnaLexicon.DESCRIPTION);
+                    String classname = stringValueOf(sequencerNode, DnaLexicon.CLASSNAME);
+                    String[] classpath = stringValuesOf(sequencerNode, DnaLexicon.CLASSPATH);
+                    String[] expressionStrings = stringValuesOf(sequencerNode, DnaLexicon.PATH_EXPRESSIONS);
+                    List<PathExpression> pathExpressions = new ArrayList<PathExpression>();
+                    if (expressionStrings != null) {
+                        for (String expressionString : expressionStrings) {
+                            try {
+                                pathExpressions.add(PathExpression.compile(expressionString));
+                            } catch (Throwable t) {
+                                problems.addError(t,
+                                                  RepositoryI18n.pathExpressionIsInvalidOnSequencer,
+                                                  expressionString,
+                                                  name,
+                                                  t.getLocalizedMessage());
+                            }
+                        }
+                    }
+                    String[] goodExpressionStrings = new String[pathExpressions.size()];
+                    for (int i = 0; i != pathExpressions.size(); ++i) {
+                        PathExpression expression = pathExpressions.get(i);
+                        goodExpressionStrings[i] = expression.getExpression();
+                    }
+                    Map<String, Object> properties = new HashMap<String, Object>();
+                    for (Property property : sequencerNode.getProperties()) {
+                        Name propertyName = property.getName();
+                        if (skipNamespaces.contains(propertyName.getNamespaceUri())) continue;
+                        if (skipProperties.contains(propertyName)) continue;
+                        if (property.isSingle()) {
+                            properties.put(propertyName.getLocalName(), property.getFirstValue());
+                        } else {
+                            properties.put(propertyName.getLocalName(), property.getValuesAsArray());
+                        }
+                    }
+                    SequencerConfig config = new SequencerConfig(name, desc, properties, classname, classpath,
+                                                                 goodExpressionStrings);
+                    configs.add(config);
+                }
+            } catch (PathNotFoundException e) {
+                // no detectors registered ...
+            }
+            return configs;
+        }
+
+        private String stringValueOf( Node node,
+                                      Name propertyName ) {
+            Property property = node.getProperty(propertyName);
+            if (property == null) return null;
+            if (property.isEmpty()) return null;
+            return context.getValueFactories().getStringFactory().create(property.getFirstValue());
+        }
+
+        private String[] stringValuesOf( Node node,
+                                         Name propertyName ) {
+            Property property = node.getProperty(propertyName);
+            if (property == null) return null;
+            return context.getValueFactories().getStringFactory().create(property.getValuesAsArray());
+        }
+
+    }
 }

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 16:43:27 UTC (rev 849)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 18:09:01 UTC (rev 850)
@@ -85,8 +85,8 @@
                                                .and()
                                                .save();
         assertThat(config, is(notNullValue()));
-        assertThat(config.configurationSource.source, is(instanceOf(InMemoryRepositorySource.class)));
-        InMemoryRepositorySource source = (InMemoryRepositorySource)config.configurationSource.source;
+        assertThat(config.configurationSource.getRepositorySource(), is(instanceOf(InMemoryRepositorySource.class)));
+        InMemoryRepositorySource source = (InMemoryRepositorySource)config.configurationSource.getRepositorySource();
         assertThat(source.getName(), is("repository name"));
         assertThat(source.getRetryLimit(), is(5));
     }



From dna-commits at lists.jboss.org  Thu Apr 23 14:13:36 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 14:13:36 -0400
Subject: [dna-commits] DNA SVN: r851 -
	trunk/dna-repository/src/main/java/org/jboss/dna/repository.
Message-ID: <E1Lx3QW-0003Ig-8m@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 14:13:36 -0400 (Thu, 23 Apr 2009)
New Revision: 851

Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
Log:
DNA-58 Create repository configuration and component

Minor change to allow a subclass to define the SessionFactory used by the SequencingService.  The configuration for the JCR engine may need to provide its own SessionFactory implementation (or instance).

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 18:09:01 UTC (rev 850)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-23 18:13:36 UTC (rev 851)
@@ -76,6 +76,7 @@
     private final ExecutionContext context;
     private final List<AdministeredService> services;
 
+    private final SessionFactory jcrSessionFactory;
     private final RepositoryService repositoryService;
     private final ObservationService observationService;
     private final SequencingService sequencingService;
@@ -109,8 +110,8 @@
         // Create the sequencing service ...
         executorService = new ScheduledThreadPoolExecutor(10); // Use a magic number for now
         sequencingService = new SequencingService();
-        SessionFactory sessionFactory = new SimpleSessionFactory();
-        JcrExecutionContext jcrContext = new JcrExecutionContext(context, sessionFactory, "");
+        jcrSessionFactory = createSessionFactory();
+        JcrExecutionContext jcrContext = new JcrExecutionContext(context, jcrSessionFactory, "");
         sequencingService.setExecutionContext(jcrContext);
         sequencingService.setExecutorService(executorService);
         for (SequencerConfig sequencerConfig : scanner.getSequencingConfigurations()) {
@@ -135,6 +136,15 @@
     }
 
     /**
+     * Method that can be overridden in subclasses to create (and populate) the SessionFactory used by the sequencing service.
+     * 
+     * @return a session factory, which may not be null
+     */
+    protected SessionFactory createSessionFactory() {
+        return new SimpleSessionFactory();
+    }
+
+    /**
      * Get the problems that were encountered when setting up this engine from the configuration.
      * 
      * @return the problems, which may be empty but will never be null



From dna-commits at lists.jboss.org  Thu Apr 23 14:21:01 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 14:21:01 -0400
Subject: [dna-commits] DNA SVN:  r852 - trunk.
Message-ID: <E1Lx3Xh-0006Qn-Bi@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 14:21:01 -0400 (Thu, 23 Apr 2009)
New Revision: 852

Modified:
   trunk/pom.xml
Log:
Corrected POM.xml file to add back in the projects that were temporarily commented out

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-04-23 18:13:36 UTC (rev 851)
+++ trunk/pom.xml	2009-04-23 18:21:01 UTC (rev 852)
@@ -115,13 +115,13 @@
 		<module>dna-graph</module>
 		<module>dna-repository</module>
 		<module>dna-cnd</module>
-		<!--module>dna-jcr</module-->
+		<module>dna-jcr</module>
 		<module>extensions/dna-classloader-maven</module>
 		<module>extensions/dna-sequencer-images</module>
 		<module>extensions/dna-sequencer-mp3</module>
 		<module>extensions/dna-sequencer-jbpm-jpdl</module>
 		<!--module>extensions/dna-sequencer-esbMessage</module-->
-		<!--module>extensions/dna-sequencer-cnd</module-->
+		<module>extensions/dna-sequencer-cnd</module>
 		<module>extensions/dna-sequencer-java</module>
 		<module>extensions/dna-sequencer-msoffice</module>
 		<module>extensions/dna-sequencer-xml</module>



From dna-commits at lists.jboss.org  Thu Apr 23 14:45:01 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 14:45:01 -0400
Subject: [dna-commits] DNA SVN: r853 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 2 other directories.
Message-ID: <E1Lx3uv-0004hN-GB@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 14:45:00 -0400 (Thu, 23 Apr 2009)
New Revision: 853

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.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/SessionCache.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-365 Workspace.importXml and getContentHandler Are Not Implemented
DNA-369 Nodes Can Be Saved Without Mandatory Properties or Child Nodes Present

Applied the patches from these two issues.  Unfortunately, they couldn't easily be applied separately.

Note that several TCK unit tests had to be commented out (relative to the patch), since they must be testing behavior that is not yet committed to trunk.  (There were a few regressions, but it looks like they're actually related to DNA-374.)

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-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -63,6 +63,7 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
@@ -73,7 +74,6 @@
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.NodeInfo;
-import org.jboss.dna.jcr.cache.PropertyInfo;
 
 /**
  * An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
@@ -126,6 +126,18 @@
             throw new RepositoryException(msg);
         }
     }
+    
+    final NodeEditor editorFor(Graph.Batch operations) throws RepositoryException {
+        try {
+            return cache.getEditorFor(nodeUuid, operations);
+        } catch (ItemNotFoundException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        } catch (InvalidItemStateException err) {
+            String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+            throw new RepositoryException(msg);
+        }
+    }    
 
     final JcrValue valueFrom( int propertyType,
                               Object value ) {
@@ -237,6 +249,10 @@
         return session().nodeTypeManager().getNodeType(primaryTypeName);
     }
 
+    Name getPrimaryTypeName() throws RepositoryException {
+        return nodeInfo().getPrimaryTypeName();
+    }
+    
     /**
      * {@inheritDoc}
      * 
@@ -256,6 +272,10 @@
         return mixinNodeTypes.toArray(new NodeType[mixinNodeTypes.size()]);
     }
 
+    List<Name> getMixinTypeNames() throws RepositoryException {
+        return nodeInfo().getMixinTypeNames();
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -729,50 +749,7 @@
             throw new ConstraintViolationException();
         }
 
-        Property existingMixinProperty = getProperty(JcrLexicon.MIXIN_TYPES);
-        Value[] existingMixinValues;
-        if (existingMixinProperty != null) {
-            existingMixinValues = existingMixinProperty.getValues();
-        } else {
-            existingMixinValues = new Value[0];
-        }
-
-        Value[] newMixinValues = new Value[existingMixinValues.length + 1];
-        System.arraycopy(existingMixinValues, 0, newMixinValues, 0, existingMixinValues.length);
-        newMixinValues[newMixinValues.length - 1] = valueFrom(PropertyType.NAME, mixinCandidateType.getInternalName());
-
-        cache.findJcrProperty(editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, false));
-
-        // ------------------------------------------------------------------------------
-        // Create any auto-created properties/nodes from new type
-        // ------------------------------------------------------------------------------
-
-        for (JcrPropertyDefinition propertyDefinition : mixinCandidateType.propertyDefinitions()) {
-            if (propertyDefinition.isAutoCreated() && !propertyDefinition.isProtected()) {
-                if (null == cache.findJcrProperty(new PropertyId(nodeUuid, propertyDefinition.getInternalName()))) {
-                    assert propertyDefinition.getDefaultValues() != null;
-                    if (propertyDefinition.isMultiple()) {
-                        setProperty(propertyDefinition.getName(), propertyDefinition.getDefaultValues());
-                    } else {
-                        assert propertyDefinition.getDefaultValues().length == 1;
-                        setProperty(propertyDefinition.getName(), propertyDefinition.getDefaultValues()[0]);
-                    }
-                }
-            }
-        }
-
-        for (JcrNodeDefinition nodeDefinition : mixinCandidateType.childNodeDefinitions()) {
-            if (nodeDefinition.isAutoCreated() && !nodeDefinition.isProtected()) {
-                Name nodeName = nodeDefinition.getInternalName();
-                if (!nodeInfo().getChildren().getChildren(nodeName).hasNext()) {
-                    assert nodeDefinition.getDefaultPrimaryType() != null;
-                    editor().createChild(nodeName,
-                                         (UUID)null,
-                                         ((JcrNodeType)nodeDefinition.getDefaultPrimaryType()).getInternalName());
-                }
-            }
-        }
-
+        this.editor().addMixin(mixinCandidateType);
     }
 
     /**
@@ -891,7 +868,7 @@
             }
         }
 
-        cache.findJcrProperty(editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, false));
+        cache.findJcrProperty(editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, PropertyType.NAME, false));
 
     }
 
@@ -1022,31 +999,16 @@
         throw new UnsupportedOperationException();
     }
 
-    /**
-     * Checks whether there is an existing property with this name that does not match the given cardinality. If such a property
-     * exists, a {@code javax.jcr.ValueFormatException} is thrown, as per section 7.1.5 of the JCR 1.0.1 specification.
-     * 
-     * @param propertyName the name of the property
-     * @param isMultiple whether the property must have multiple values
-     * @throws javax.jcr.ValueFormatException if the property exists but has the opposite cardinality
-     * @throws RepositoryException if any other error occurs
-     */
-    private void checkCardinalityOfExistingProperty( Name propertyName,
-                                                     boolean isMultiple )
-        throws javax.jcr.ValueFormatException, RepositoryException {
-        // Check for existing single-valued property - can't set multiple values on single-valued property
-        PropertyInfo propInfo = this.nodeInfo().getProperty(propertyName);
-        if (propInfo != null && propInfo.isMultiValued() != isMultiple) {
-            if (isMultiple) {
-                I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
-                throw new ValueFormatException(msg.text(getPath(),
-                                                        propertyName.getString(cache.namespaces),
-                                                        cache.workspaceName()));
-            }
-            I18n msg = JcrI18n.unableToSetMultiValuedPropertyUsingSingleValue;
-            throw new ValueFormatException(msg.text(getPath(), propertyName, cache.workspaceName()));
+    protected final Property removeExistingValuedProperty( String name )
+        throws ConstraintViolationException, RepositoryException {
+        PropertyId id = new PropertyId(nodeUuid, nameFrom(name));
+        AbstractJcrProperty property = cache.findJcrProperty(id);
+        if (property != null) {
+            property.remove();
+            return property;
         }
-
+        // else the property doesn't exist ...
+        throw new RepositoryException(JcrI18n.propertyNotFoundOnNode.text(name, getPath(), cache.workspaceName()));
     }
 
     /**
@@ -1057,10 +1019,7 @@
     public final Property setProperty( String name,
                                        boolean value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.BOOLEAN, value)));
-
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.BOOLEAN, value)));
     }
 
     /**
@@ -1071,15 +1030,12 @@
     public final Property setProperty( String name,
                                        Calendar value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
 
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
-
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
     /**
@@ -1090,10 +1046,7 @@
     public final Property setProperty( String name,
                                        double value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.DOUBLE, value)));
-
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.DOUBLE, value)));
     }
 
     /**
@@ -1105,12 +1058,10 @@
                                        InputStream value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
     /**
@@ -1121,9 +1072,7 @@
     public final Property setProperty( String name,
                                        long value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.LONG, value)));
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.LONG, value)));
     }
 
     /**
@@ -1135,13 +1084,10 @@
                                        Node value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
 
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(value)));
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(value)));
     }
 
     /**
@@ -1153,12 +1099,10 @@
                                        String value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(PropertyType.STRING, value)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value)));
     }
 
     /**
@@ -1171,12 +1115,10 @@
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valueFrom(type, value)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valueFrom(type, value)));
     }
 
     /**
@@ -1188,12 +1130,10 @@
                                        String[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (values == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, true);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valuesFrom(PropertyType.STRING, values)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values), PropertyType.UNDEFINED));
     }
 
     /**
@@ -1206,12 +1146,10 @@
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (values == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, true);
-        return cache.findJcrProperty(editor().setProperty(propertyName, valuesFrom(type, values)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(type, values), PropertyType.UNDEFINED));
     }
 
     /**
@@ -1223,24 +1161,10 @@
                                        Value value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, (JcrValue)value));
-    }
 
-    protected final Property removeExistingValuedProperty( String name )
-        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-        PropertyId id = new PropertyId(nodeUuid, nameFrom(name));
-        AbstractJcrProperty property = cache.findJcrProperty(id);
-        if (property != null) {
-            property.remove();
-            return property;
-        }
-        // else the property doesn't exist ...
-        throw new RepositoryException(JcrI18n.propertyNotFoundOnNode.text(name, getPath(), cache.workspaceName()));
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), (JcrValue)value));
     }
 
     /**
@@ -1253,12 +1177,10 @@
                                        int type )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         if (value == null) {
-            // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, false);
-        return cache.findJcrProperty(editor().setProperty(propertyName, ((JcrValue)value).asType(type)));
+
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type)));
     }
 
     /**
@@ -1273,44 +1195,8 @@
             // If there is an existing property, then remove it ...
             return removeExistingValuedProperty(name);
         }
-        int len = values.length;
-        Value[] newValues = null;
-        if (len == 0) {
-            newValues = JcrMultiValueProperty.EMPTY_VALUES;
-        } else {
-            List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
-            int expectedType = -1;
-            for (int i = 0; i != len; ++i) {
-                Value value = values[i];
-                if (value == null) continue;
-                if (expectedType == -1) {
-                    expectedType = value.getType();
-                } else if (value.getType() != expectedType) {
-                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
-                    StringBuilder sb = new StringBuilder();
-                    sb.append('[');
-                    for (int j = 0; j != values.length; ++j) {
-                        if (j != 0) sb.append(",");
-                        sb.append(values[j].toString());
-                    }
-                    sb.append(']');
-                    String propType = PropertyType.nameFromValue(expectedType);
-                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
-                    throw new ValueFormatException(msg.text(name, values, propType, getPath(), cache.workspaceName()));
-                }
-                valuesWithDesiredType.add(value);
-            }
-            if (valuesWithDesiredType.isEmpty()) {
-                newValues = JcrMultiValueProperty.EMPTY_VALUES;
-            } else {
-                newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
-            }
-        }
 
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, true);
-        // Set the value, perhaps to an empty array ...
-        return cache.findJcrProperty(editor().setProperty(propertyName, newValues));
+        return setProperty(name, values, PropertyType.UNDEFINED);
     }
 
     /**
@@ -1327,48 +1213,8 @@
             return removeExistingValuedProperty(name);
         }
 
-        int len = values.length;
-        Value[] newValues = null;
-        if (len == 0) {
-            newValues = JcrMultiValueProperty.EMPTY_VALUES;
-        } else {
-            List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
-            int expectedType = -1;
-            for (int i = 0; i != len; ++i) {
-                Value value = values[i];
-
-                if (value == null) continue;
-                if (expectedType == -1) {
-                    expectedType = value.getType();
-                } else if (value.getType() != expectedType) {
-                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
-                    StringBuilder sb = new StringBuilder();
-                    sb.append('[');
-                    for (int j = 0; j != values.length; ++j) {
-                        if (j != 0) sb.append(",");
-                        sb.append(values[j].toString());
-                    }
-                    sb.append(']');
-                    String propType = PropertyType.nameFromValue(expectedType);
-                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
-                    throw new ValueFormatException(msg.text(name, values, propType, getPath(), cache.workspaceName()));
-                }
-                if (value.getType() != type) {
-                    value = ((JcrValue)value).asType(type);
-                }
-                valuesWithDesiredType.add(value);
-            }
-            if (valuesWithDesiredType.isEmpty()) {
-                newValues = JcrMultiValueProperty.EMPTY_VALUES;
-            } else {
-                newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
-            }
-        }
-
-        Name propertyName = nameFrom(name);
-        checkCardinalityOfExistingProperty(propertyName, true);
         // Set the value, perhaps to an empty array ...
-        return cache.findJcrProperty(editor().setProperty(propertyName, newValues));
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name), values, type));
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrContentHandler.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -32,16 +32,19 @@
 import java.util.UUID;
 import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.ItemExistsException;
-import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.text.TextDecoder;
 import org.jboss.dna.common.text.XmlNameEncoder;
 import org.jboss.dna.common.util.Base64;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.xml.sax.Attributes;
@@ -71,9 +74,10 @@
 
     protected static final TextDecoder DOCUMENT_VIEW_NAME_DECODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
 
+    private final NameFactory nameFactory;
+
     protected final JcrSession session;
     protected final int uuidBehavior;
-    protected final SaveMode saveMode;
 
     protected final String primaryTypeName;
     protected final String mixinTypesName;
@@ -82,6 +86,8 @@
     private AbstractJcrNode currentNode;
     private ContentHandler delegate;
 
+    private Graph.Batch pendingOperations;
+
     enum SaveMode {
         WORKSPACE,
         SESSION
@@ -99,15 +105,37 @@
                || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW;
 
         this.session = session;
+        this.nameFactory = session.getExecutionContext().getValueFactories().getNameFactory();
         this.currentNode = session.getNode(parentPath);
         this.uuidBehavior = uuidBehavior;
-        this.saveMode = saveMode;
 
+        if (saveMode == SaveMode.WORKSPACE) {
+            this.pendingOperations = session.createBatch();
+        }
+
         this.primaryTypeName = JcrLexicon.PRIMARY_TYPE.getString(this.session.namespaces());
         this.mixinTypesName = JcrLexicon.MIXIN_TYPES.getString(this.session.namespaces());
         this.uuidName = JcrLexicon.UUID.getString(this.session.namespaces());
     }
 
+    protected final Name nameFor( String name ) {
+        return nameFactory.create(name);
+    }
+
+    protected final Value valueFor( String value,
+                                    int type ) throws ValueFormatException {
+        return session.getValueFactory().createValue(value, type);
+        // return new JcrValue(session.getExecutionContext().getValueFactories(), cache(), type, value);
+    }
+
+    protected final SessionCache cache() {
+        return session.cache();
+    }
+
+    protected final Graph.Batch operations() {
+        return pendingOperations;
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -124,6 +152,20 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.xml.sax.helpers.DefaultHandler#endDocument()
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        if (pendingOperations != null) {
+            pendingOperations.execute();
+        }
+
+        super.endDocument();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
      */
     @Override
@@ -273,9 +315,10 @@
                         uuid = UUID.fromString(rawUuid.get(0).getString());
                     }
 
-                    AbstractJcrNode newNode = parentNode.addNode(currentNodeName, currentProps.get(primaryTypeName)
-                                                                                              .get(0)
-                                                                                              .getString(), uuid);
+                    String typeName = currentProps.get(primaryTypeName).get(0).getString();
+                    AbstractJcrNode newNode = cache().findJcrNode(parentNode.editorFor(operations()).createChild(nameFor(currentNodeName),
+                                                                                                                 uuid,
+                                                                                                                 nameFor(typeName)).getUuid());
 
                     for (Map.Entry<String, List<Value>> entry : currentProps.entrySet()) {
                         if (entry.getKey().equals(primaryTypeName)) {
@@ -284,7 +327,8 @@
 
                         if (entry.getKey().equals(mixinTypesName)) {
                             for (Value value : entry.getValue()) {
-                                newNode.addMixin(value.getString());
+                                JcrNodeType mixinType = session.workspace().nodeTypeManager().getNodeType(nameFor(value.getString()));
+                                newNode.editorFor(operations()).addMixin(mixinType);
                             }
                             continue;
                         }
@@ -296,9 +340,11 @@
                         List<Value> values = entry.getValue();
 
                         if (values.size() == 1) {
-                            newNode.setProperty(entry.getKey(), values.get(0));
+                            newNode.editorFor(operations()).setProperty(nameFor(entry.getKey()), (JcrValue)values.get(0));
                         } else {
-                            newNode.setProperty(entry.getKey(), values.toArray(new Value[values.size()]));
+                            newNode.editorFor(operations()).setProperty(nameFor(entry.getKey()),
+                                                                        values.toArray(new Value[values.size()]),
+                                                                        PropertyType.UNDEFINED);
                         }
                     }
 
@@ -385,7 +431,7 @@
                         switch (uuidBehavior) {
                             case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
                                 parentNode = (AbstractJcrNode)existingNodeWithUuid.getParent();
-                                existingNodeWithUuid.remove();
+                                parentNode.editorFor(operations()).destroyChild(uuid);
                                 break;
                             case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
                                 uuid = UUID.randomUUID();
@@ -394,7 +440,8 @@
                                 if (existingNodeWithUuid.path().isAtOrAbove(parentStack.firstElement().path())) {
                                     throw new ConstraintViolationException();
                                 }
-                                existingNodeWithUuid.remove();
+                                AbstractJcrNode temp = (AbstractJcrNode)existingNodeWithUuid.getParent();
+                                temp.editorFor(operations()).destroyChild(uuid);
                                 break;
                             case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
                                 throw new ItemExistsException();
@@ -403,7 +450,9 @@
                 }
 
                 name = DOCUMENT_VIEW_NAME_DECODER.decode(name);
-                AbstractJcrNode currentNode = parentNode.addNode(name, primaryTypeName, uuid);
+                AbstractJcrNode currentNode = cache().findJcrNode(parentNode.editorFor(operations()).createChild(nameFor(name),
+                                                                                                                 uuid,
+                                                                                                                 nameFor(primaryTypeName)).getUuid());
 
                 for (int i = 0; i < atts.getLength(); i++) {
                     if (JcrContentHandler.this.primaryTypeName.equals(atts.getQName(i))) {
@@ -411,7 +460,8 @@
                     }
 
                     if (mixinTypesName.equals(atts.getQName(i))) {
-                        currentNode.addMixin(atts.getValue(i));
+                        JcrNodeType mixinType = session.workspace().nodeTypeManager().getNodeType(nameFor(atts.getValue(i)));
+                        currentNode.editorFor(operations()).addMixin(mixinType);
                         continue;
                     }
 
@@ -424,7 +474,8 @@
                     // String value = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getValue(i));
                     String value = atts.getValue(i);
                     String propertyName = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getQName(i));
-                    currentNode.setProperty(propertyName, value);
+                    currentNode.editorFor(operations()).setProperty(nameFor(propertyName),
+                                                                    (JcrValue)valueFor(value, PropertyType.STRING));
                 }
 
                 parentStack.push(currentNode);
@@ -451,12 +502,14 @@
                                 int start,
                                 int length ) throws SAXException {
             try {
-                NamespaceRegistry namespaces = session.getExecutionContext().getNamespaceRegistry();
-                Node parentNode = parentStack.peek();
-                Node currentNode = parentNode.addNode(JcrLexicon.XMLTEXT.getString(namespaces),
-                                                      JcrNtLexicon.UNSTRUCTURED.getString(namespaces));
+                AbstractJcrNode parentNode = parentStack.peek();
+                AbstractJcrNode currentNode = cache().findJcrNode(parentNode.editorFor(operations()).createChild(JcrLexicon.XMLTEXT,
+                                                                                                                 null,
+                                                                                                                 JcrNtLexicon.UNSTRUCTURED).getUuid());
+
                 String s = new String(ch, start, length);
-                currentNode.setProperty(JcrLexicon.XMLCHARACTERS.getString(namespaces), s);
+                currentNode.editorFor(operations()).setProperty(JcrLexicon.XMLCHARACTERS,
+                                                                (JcrValue)valueFor(s, PropertyType.STRING));
 
             } catch (RepositoryException re) {
                 throw new EnclosingSAXException(re);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -128,6 +128,10 @@
     public static I18n autocreatedPropertyNeedsDefault;
     public static I18n singleValuedPropertyNeedsSingleValuedDefault;
 
+    public static I18n noDefinition;
+    public static I18n noSnsDefinition;
+    public static I18n missingMandatoryItem;
+
     static {
         try {
             I18n.initialize(JcrI18n.class);

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -37,9 +37,7 @@
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.version.VersionException;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.ValueFactory;
 
 /**
  * @author jverhaeg
@@ -191,88 +189,7 @@
             return;
         }
 
-        Value[] newValues = null;
-        if (values.length != 0) {
-            int numValues = values.length;
-            int valueType = -1;
-            List<Value> valuesList = new ArrayList<Value>(numValues);
-            ValueFactory<?> factory = null;
-            for (int i = 0; i != numValues; ++i) {
-                Value value = values[i];
-                if (value == null) {
-                    // skip null values ...
-                    continue;
-                }
-                if (valueType == -1) {
-                    valueType = value.getType();
-                } else if (value.getType() != valueType) {
-                    // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
-                    StringBuilder sb = new StringBuilder();
-                    sb.append('[');
-                    for (int j = 0; j != values.length; ++j) {
-                        if (j != 0) sb.append(",");
-                        sb.append(values[j].toString());
-                    }
-                    sb.append(']');
-                    String propType = PropertyType.nameFromValue(valueType);
-                    I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
-                    throw new ValueFormatException(msg.text(getName(), values, propType, getPath(), cache.workspaceName()));
-                }
-
-                if (value instanceof JcrValue) {
-                    // just use the value ...
-                    valuesList.add(value);
-                } else {
-                    // This isn't our implementation, so create one for use
-                    if (factory == null) {
-                        int currentType = this.getType();
-                        factory = context().getValueFactories().getValueFactory(PropertyTypeUtil.dnaPropertyTypeFor(currentType));
-                    }
-                    int type = value.getType();
-                    Object data = null;
-                    switch (value.getType()) {
-                        case PropertyType.STRING:
-                            data = value.getString();
-                            break;
-                        case PropertyType.BINARY:
-                            data = value.getStream();
-                            break;
-                        case PropertyType.BOOLEAN:
-                            data = value.getBoolean();
-                            break;
-                        case PropertyType.DATE:
-                            data = value.getDate();
-                            break;
-                        case PropertyType.DOUBLE:
-                            data = value.getDouble();
-                            break;
-                        case PropertyType.LONG:
-                            data = value.getLong();
-                            break;
-                        case PropertyType.NAME:
-                            data = value.getString();
-                            break;
-                        case PropertyType.PATH:
-                            data = value.getString();
-                            break;
-                        case PropertyType.REFERENCE:
-                            data = value.getString();
-                            break;
-                        default:
-                            throw new RepositoryException();
-                    }
-                    valuesList.add(createValue(factory.create(data), type));
-                }
-            }
-            if (valuesList.isEmpty()) {
-                newValues = EMPTY_VALUES;
-            } else {
-                newValues = valuesList.toArray(new Value[valuesList.size()]);
-            }
-        } else {
-            newValues = EMPTY_VALUES;
-        }
-        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), newValues);
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), values, PropertyType.UNDEFINED);
     }
 
     /**
@@ -305,7 +222,8 @@
         } else {
             jcrValues = EMPTY_VALUES;
         }
-        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues);
+        
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues, PropertyType.STRING);
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -215,6 +215,25 @@
                                      defaultPrimaryTypeName, required);
     }
 
+    
+    
+    @Override
+    public int hashCode() {
+        return getId().toString().hashCode();
+    }
+
+    @Override
+    public boolean equals( Object obj ) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        JcrNodeDefinition other = (JcrNodeDefinition)obj;
+        if (id == null) {
+            if (other.id != null) return false;
+        } else if (!id.equals(other.id)) return false;
+        return true;
+    }
+
     /**
      * {@inheritDoc}
      * 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -310,7 +310,25 @@
                 throw new IllegalStateException("Invalid property type: " + type);
         }
     }
+    
+    @Override
+    public int hashCode() {
+        return getId().toString().hashCode();
+    }
 
+    @Override
+    public boolean equals( Object obj ) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        JcrPropertyDefinition other = (JcrPropertyDefinition)obj;
+        if (id == null) {
+            if (other.id != null) return false;
+        } else if (!id.equals(other.id)) return false;
+        return true;
+    }
+    
+
     /**
      * Interface that encapsulates a reusable method that can test values to determine if they match a specific list of
      * constraints for the semantics associated with a single {@link PropertyType}.

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-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -180,6 +180,10 @@
     JcrWorkspace workspace() {
         return this.workspace;
     }
+    
+    Graph.Batch createBatch() {
+        return graph.batch();
+    }
 
     /**
      * {@inheritDoc}

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-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -34,6 +34,7 @@
 import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Workspace;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
@@ -46,11 +47,9 @@
 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.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.io.GraphImporter;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
@@ -58,8 +57,15 @@
 import org.jboss.dna.graph.property.PropertyFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.basic.GraphNamespaceRegistry;
+import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
+import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
 import org.jboss.dna.jcr.JcrRepository.Options;
 import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
 
 /**
  * @author John Verhaeg
@@ -224,8 +230,8 @@
     /**
      * {@inheritDoc}
      */
-    public final ObservationManager getObservationManager() {
-        throw new UnsupportedOperationException();
+    public final ObservationManager getObservationManager() throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
     }
 
     /**
@@ -292,17 +298,16 @@
      * 
      * @see javax.jcr.Workspace#getImportContentHandler(java.lang.String, int)
      */
-    @SuppressWarnings( "unused" )
     public ContentHandler getImportContentHandler( String parentAbsPath,
                                                    int uuidBehavior )
         throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, AccessDeniedException,
         RepositoryException {
-        CheckArg.isNotEmpty(parentAbsPath, "parentAbsPath");
-        // Create a graph importer, which can return the content handler that can be used by the caller
-        // to call the handler's event methods to create content...
-        GraphImporter importer = new GraphImporter(graph);
-        Path parentPath = context.getValueFactories().getPathFactory().create(parentAbsPath);
-        return importer.getHandlerForImportingXml(Location.create(parentPath), false);
+
+        CheckArg.isNotNull(parentAbsPath, "parentAbsPath");
+
+        Path parentPath = this.context.getValueFactories().getPathFactory().create(parentAbsPath);
+
+        return new JcrContentHandler(this.session, parentPath, uuidBehavior, SaveMode.WORKSPACE);
     }
 
     /**
@@ -310,19 +315,33 @@
      * 
      * @see javax.jcr.Workspace#importXML(java.lang.String, java.io.InputStream, int)
      */
-    @SuppressWarnings( "unused" )
     public void importXML( String parentAbsPath,
                            InputStream in,
                            int uuidBehavior )
         throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException,
         InvalidSerializedDataException, LockException, AccessDeniedException, RepositoryException {
-        // try {
-        // graph.importXmlFrom(in).into(parentAbsPath);
-        // } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-        // throw new PathNotFoundException(e.getMessage(), e);
-        // } catch (SAXException err) {
-        // }
-        throw new UnsupportedOperationException();
+
+        CheckArg.isNotNull(parentAbsPath, "parentAbsPath");
+        CheckArg.isNotNull(in, "in");
+
+        try {
+            XMLReader parser = XMLReaderFactory.createXMLReader();
+            parser.setContentHandler(getImportContentHandler(parentAbsPath, uuidBehavior));
+            parser.parse(new InputSource(in));
+        } catch (EnclosingSAXException ese) {
+            Exception cause = ese.getException();
+            if (cause instanceof ItemExistsException) {
+                throw (ItemExistsException)cause;
+            } else if (cause instanceof ConstraintViolationException) {
+                throw (ConstraintViolationException)cause;
+            }
+            throw new RepositoryException(cause);
+        } catch (SAXParseException se) {
+            throw new InvalidSerializedDataException(se);
+        } catch (SAXException se) {
+            throw new RepositoryException(se);
+        }
+
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -355,12 +355,205 @@
     }
 
     /**
+     * Checks that the child items of the node are consistent with the definitions required by the node's primary type and mixin
+     * types (if any).
+     * <p>
+     * This method first checks that all of the child nodes and properties for the node have definitions based on the current
+     * primary and mixin node types for the node as held in the node type registry. The method then checks that all mandatory (and
+     * non-protected) items are populated.
+     * </p>
+     * 
+     * @param nodeUuid the UUID of the node to check
+     * @param checkSns if true indicates that this method should distinguish between child nodes that have no matching definition
+     *        and child nodes that would have a definition that would match if it allowed same-name siblings. This flag determines
+     *        which exception type should be thrown in that case.
+     * @throws ItemExistsException if checkSns is true and there is no definition that allows same-name siblings for one of the
+     *         node's child nodes and the node already has a child node with the given name
+     * @throws ConstraintViolationException if one of the node's properties or child nodes does not have a matching definition for
+     *         the name and type among the node's primary and mixin types; this should only occur if type definitions have been
+     *         modified since the node was loaded or modified.
+     * @throws RepositoryException if any other error occurs
+     */
+    private void checkAgainstTypeDefinitions( UUID nodeUuid,
+                                              boolean checkSns )
+        throws ConstraintViolationException, ItemExistsException, RepositoryException {
+
+        NodeInfo nodeInfo = findNodeInfo(nodeUuid);
+        AbstractJcrNode node = findJcrNode(nodeUuid);
+
+        Name primaryTypeName = node.getPrimaryTypeName();
+        List<Name> mixinTypeNames = node.getMixinTypeNames();
+        Set<JcrNodeDefinition> satisfiedChildNodes = new HashSet<JcrNodeDefinition>();
+        Set<JcrPropertyDefinition> satisfiedProperties = new HashSet<JcrPropertyDefinition>();
+
+        for (AbstractJcrProperty property : findJcrPropertiesFor(nodeUuid)) {
+            JcrPropertyDefinition definition = findBestPropertyDefintion(primaryTypeName,
+                                                                         mixinTypeNames,
+                                                                         property.property(),
+                                                                         property.getType(),
+                                                                         false);
+            if (definition == null) {
+                throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
+                                                                                 property.getName(),
+                                                                                 node.getPath(),
+                                                                                 primaryTypeName,
+                                                                                 mixinTypeNames));
+            }
+
+            satisfiedProperties.add(definition);
+        }
+
+        Children children = nodeInfo.getChildren();
+        for (ChildNode child : children) {
+            int snsCount = children.getCountOfSameNameSiblingsWithName(child.getName());
+            NodeInfo childInfo = findNodeInfo(child.getUuid());
+            JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
+                                                                               mixinTypeNames,
+                                                                               child.getName(),
+                                                                               childInfo.getPrimaryTypeName(),
+                                                                               snsCount,
+                                                                               false);
+            if (definition == null) {
+                if (checkSns && snsCount > 1) {
+                    definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
+                                                                     mixinTypeNames,
+                                                                     child.getName(),
+                                                                     childInfo.getPrimaryTypeName(),
+                                                                     1,
+                                                                     false);
+
+                    if (definition != null) {
+                        throw new ItemExistsException(JcrI18n.noSnsDefinition.text(child.getName(),
+                                                                                   node.getPath(),
+                                                                                   primaryTypeName,
+                                                                                   mixinTypeNames));
+                    }
+                }
+                throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
+                                                                                 child.getName(),
+                                                                                 node.getPath(),
+                                                                                 primaryTypeName,
+                                                                                 mixinTypeNames));
+            }
+            satisfiedChildNodes.add(definition);
+        }
+
+        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        for (JcrPropertyDefinition definition : primaryType.getPropertyDefinitions()) {
+            if (definition.isMandatory() && !definition.isProtected() && !satisfiedProperties.contains(definition)) {
+                throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
+                                                                                 definition.getName(),
+                                                                                 node.getPath(),
+                                                                                 primaryTypeName,
+                                                                                 mixinTypeNames));
+            }
+        }
+        for (JcrNodeDefinition definition : primaryType.getChildNodeDefinitions()) {
+            if (definition.isMandatory() && !definition.isProtected() && !satisfiedChildNodes.contains(definition)) {
+                throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
+                                                                                 definition.getName(),
+                                                                                 node.getPath(),
+                                                                                 primaryTypeName,
+                                                                                 mixinTypeNames));
+            }
+        }
+
+        for (Name mixinTypeName : mixinTypeNames) {
+            JcrNodeType mixinType = nodeTypes().getNodeType(mixinTypeName);
+            for (JcrPropertyDefinition definition : mixinType.getPropertyDefinitions()) {
+                if (definition.isMandatory() && !definition.isProtected() && !satisfiedProperties.contains(definition)) {
+                    throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
+                                                                                     definition.getName(),
+                                                                                     node.getPath(),
+                                                                                     primaryTypeName,
+                                                                                     mixinTypeNames));
+                }
+            }
+            for (JcrNodeDefinition definition : mixinType.getChildNodeDefinitions()) {
+                if (definition.isMandatory() && !definition.isProtected() && !satisfiedChildNodes.contains(definition)) {
+                    throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
+                                                                                     definition.getName(),
+                                                                                     node.getPath(),
+                                                                                     primaryTypeName,
+                                                                                     mixinTypeNames));
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Find the best definition for the child node with the given name on the node with the given UUID.
+     * 
+     * @param nodeUuid the parent node; may not be null
+     * @param newNodeName the name of the potential new child node; may not be null
+     * @param newNodePrimaryTypeName the primary type of the potential new child node; may not be null
+     * @return the definition that best fits the new node name and type
+     * @throws ItemExistsException if there is no definition that allows same-name siblings for the name and type and the parent
+     *         node already has a child node with the given name
+     * @throws ConstraintViolationException if there is no definition for the name and type among the parent node's primary and
+     *         mixin types
+     * @throws RepositoryException if any other error occurs
+     */
+    protected JcrNodeDefinition findBestNodeDefinition( UUID nodeUuid,
+                                                        Name newNodeName,
+                                                        Name newNodePrimaryTypeName )
+        throws ItemExistsException, ConstraintViolationException, RepositoryException {
+        assert (nodeUuid != null);
+        assert (newNodeName != null);
+
+        NodeInfo nodeInfo = findNodeInfo(nodeUuid);
+        AbstractJcrNode node = findJcrNode(nodeUuid);
+
+        Name primaryTypeName = node.getPrimaryTypeName();
+        List<Name> mixinTypeNames = node.getMixinTypeNames();
+
+        Children children = nodeInfo.getChildren();
+        int snsCount = children.getCountOfSameNameSiblingsWithName(newNodeName);
+        JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
+                                                                           mixinTypeNames,
+                                                                           newNodeName,
+                                                                           newNodePrimaryTypeName,
+                                                                           snsCount,
+                                                                           true);
+        if (definition == null) {
+            if (snsCount > 1) {
+                definition = nodeTypes().findChildNodeDefinition(primaryTypeName,
+                                                                 mixinTypeNames,
+                                                                 newNodeName,
+                                                                 newNodePrimaryTypeName,
+                                                                 1,
+                                                                 true);
+
+                if (definition != null) {
+                    throw new ItemExistsException(JcrI18n.noSnsDefinition.text(newNodeName,
+                                                                               node.getPath(),
+                                                                               primaryTypeName,
+                                                                               mixinTypeNames));
+                }
+            }
+
+            throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
+                                                                             newNodeName,
+                                                                             node.getPath(),
+                                                                             primaryTypeName,
+                                                                             mixinTypeNames));
+        }
+
+        return definition;
+    }
+
+    /**
      * Save any changes that have been accumulated by this session.
      * 
      * @throws RepositoryException if any error resulting while saving the changes to the repository
      */
     public void save() throws RepositoryException {
         if (operations.isExecuteRequired()) {
+            for (UUID changedUuid : changedNodes.keySet()) {
+                checkAgainstTypeDefinitions(changedUuid, false);
+            }
+
             // Execute the batched operations ...
             try {
                 operations.execute();
@@ -453,7 +646,7 @@
                     for (ChildNode childNode : changedNode.getChildren()) {
                         uuidsUnderBranch.add(childNode.getUuid());
                     }
-                    
+
                     Collection<UUID> peers = changedNode.getPeers();
                     if (peers != null) peersToCheck.addAll(peers);
                 }
@@ -468,7 +661,7 @@
                     throw new ConstraintViolationException();
                 }
             }
-            
+
             // Now execute the branch ...
             Graph.Batch branchBatch = store.batch(new BatchRequestBuilder(branchRequests));
             try {
@@ -653,7 +846,9 @@
     /**
      * Obtain an {@link NodeEditor editor} that can be used to manipulate the properties or children on the node identified by the
      * supplied UUID. The node must exist prior to this call, either as a node that exists in the workspace or as a node that was
-     * created within this session but not yet persited to the workspace.
+     * created within this session but not yet persisted to the workspace. This method returns an editor that batches all changes
+     * in transient storage from where they can be persisted to the repository by {@link javax.jcr.Session#save() saving the
+     * session} or by {@link javax.jcr.Item#save() saving an ancestor}.
      * 
      * @param uuid the UUID of the node that is to be changed; may not be null and must represent an <i>existing</i> node
      * @return the editor; never null
@@ -662,6 +857,26 @@
      * @throws RepositoryException if any other error occurs while reading information from the repository
      */
     public NodeEditor getEditorFor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+        return getEditorFor(uuid, this.operations);
+    }
+
+    /**
+     * Obtain an {@link NodeEditor editor} that can be used to manipulate the properties or children on the node identified by the
+     * supplied UUID. The node must exist prior to this call, either as a node that exists in the workspace or as a node that was
+     * created within this session but not yet persisted to the workspace.
+     * 
+     * @param uuid the UUID of the node that is to be changed; may not be null and must represent an <i>existing</i> node
+     * @param operationsBatch the {@link Graph.Batch} to use for batching operations. This should be populated for direct
+     *        persistence (q.v. section 7.1.3.7 of the JCR 1.0.1 specification) and should be null to use session-based
+     *        persistence.
+     * @return the editor; never null
+     * @throws ItemNotFoundException if no such node could be found in the session or workspace
+     * @throws InvalidItemStateException if the item has been marked for deletion within this session
+     * @throws RepositoryException if any other error occurs while reading information from the repository
+     */
+    public NodeEditor getEditorFor( UUID uuid,
+                                    Graph.Batch operationsBatch )
+        throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         // See if we already have something in the changed nodes ...
         ChangedNodeInfo info = changedNodes.get(uuid);
         Location currentLocation = null;
@@ -679,7 +894,7 @@
             // compute the current location ...
             currentLocation = Location.create(getPathFor(info), uuid);
         }
-        return new NodeEditor(info, currentLocation);
+        return new NodeEditor(info, currentLocation, operationsBatch == null ? this.operations : operationsBatch);
     }
 
     /**
@@ -688,14 +903,49 @@
     public final class NodeEditor {
         private final ChangedNodeInfo node;
         private final Location currentLocation;
+        private final Graph.Batch operations;
 
         protected NodeEditor( ChangedNodeInfo node,
-                              Location currentLocation ) {
+                              Location currentLocation,
+                              Graph.Batch operations ) {
             this.node = node;
             this.currentLocation = currentLocation;
+            this.operations = operations;
         }
 
         /**
+         * Checks whether there is an existing property with this name that does not match the given cardinality. If such a
+         * property exists, a {@code javax.jcr.ValueFormatException} is thrown, as per section 7.1.5 of the JCR 1.0.1
+         * specification.
+         * 
+         * @param propertyName the name of the property
+         * @param isMultiple whether the property must have multiple values
+         * @throws javax.jcr.ValueFormatException if the property exists but has the opposite cardinality
+         * @throws RepositoryException if any other error occurs
+         */
+        private void checkCardinalityOfExistingProperty( Name propertyName,
+                                                         boolean isMultiple )
+            throws javax.jcr.ValueFormatException, RepositoryException {
+            // Check for existing single-valued property - can't set multiple values on single-valued property
+            PropertyInfo propInfo = this.node.getProperty(propertyName);
+            if (propInfo != null && propInfo.isMultiValued() != isMultiple) {
+                NamespaceRegistry namespaces = SessionCache.this.namespaces;
+                String workspaceName = SessionCache.this.workspaceName();
+                if (isMultiple) {
+                    I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
+                    throw new javax.jcr.ValueFormatException(msg.text(propertyName.getString(namespaces),
+                                                                      getPathFor(this.node).getString(namespaces),
+                                                                      workspaceName));
+                }
+                I18n msg = JcrI18n.unableToSetMultiValuedPropertyUsingSingleValue;
+                throw new javax.jcr.ValueFormatException(msg.text(getPathFor(this.node).getString(namespaces),
+                                                                  propertyName,
+                                                                  workspaceName));
+            }
+
+        }
+
+        /**
          * Set the value for the property. If the property does not exist, it will be added. If the property does exist, the
          * existing values will be replaced with the supplied value.
          * 
@@ -704,20 +954,23 @@
          * @return the identifier for the property; never null
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
+         * @throws RepositoryException if any other error occurs
          */
         public PropertyId setProperty( Name name,
-                                       JcrValue value ) throws ConstraintViolationException {
+                                       JcrValue value ) throws ConstraintViolationException, RepositoryException {
             return setProperty(name, value, true);
         }
 
         public PropertyId setProperty( Name name,
                                        JcrValue value,
-                                       boolean skipProtected ) throws ConstraintViolationException {
+                                       boolean skipProtected ) throws ConstraintViolationException, RepositoryException {
             assert name != null;
             assert value != null;
             JcrPropertyDefinition definition = null;
             PropertyId id = null;
 
+            checkCardinalityOfExistingProperty(name, false);
+
             // Look for an existing property ...
             PropertyInfo existing = node.getProperty(name);
             if (existing != null) {
@@ -789,13 +1042,18 @@
          * @param name the property name; may not be null
          * @param values new property values, all of which must have the same {@link Value#getType() property type}; may not be
          *        null but may be empty
+         * @param valueType
          * @return the identifier for the property; never null
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
+         * @throws javax.jcr.ValueFormatException
+         * @throws RepositoryException
          */
         public PropertyId setProperty( Name name,
-                                       Value[] values ) throws ConstraintViolationException {
-            return setProperty(name, values, true);
+                                       Value[] values,
+                                       int valueType )
+            throws ConstraintViolationException, RepositoryException, javax.jcr.ValueFormatException {
+            return setProperty(name, values, valueType, true);
         }
 
         /**
@@ -807,16 +1065,66 @@
          *        null but may be empty
          * @param skipProtected if true, attempts to set protected properties will fail. If false, attempts to set protected
          *        properties will be allowed.
+         * @param valueType
          * @return the identifier for the property; never null
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
+         * @throws javax.jcr.ValueFormatException
+         * @throws RepositoryException
          */
         public PropertyId setProperty( Name name,
                                        Value[] values,
-                                       boolean skipProtected ) throws ConstraintViolationException {
+                                       int valueType,
+                                       boolean skipProtected )
+            throws ConstraintViolationException, RepositoryException, javax.jcr.ValueFormatException {
             assert name != null;
             assert values != null;
-            int numValues = values.length;
+
+            // TODO: Re-add following line!
+            // checkCardinalityOfExistingProperty(name, true);
+
+            int len = values.length;
+            Value[] newValues = null;
+            if (len == 0) {
+                newValues = JcrMultiValueProperty.EMPTY_VALUES;
+            } else {
+                List<Value> valuesWithDesiredType = new ArrayList<Value>(len);
+                int expectedType = -1;
+                for (int i = 0; i != len; ++i) {
+                    Value value = values[i];
+
+                    if (value == null) continue;
+                    if (expectedType == -1) {
+                        expectedType = value.getType();
+                    } else if (value.getType() != expectedType) {
+                        // Make sure the type of each value is the same, as per Javadoc in section 7.1.5 of the JCR 1.0.1 spec
+                        StringBuilder sb = new StringBuilder();
+                        sb.append('[');
+                        for (int j = 0; j != values.length; ++j) {
+                            if (j != 0) sb.append(",");
+                            sb.append(values[j].toString());
+                        }
+                        sb.append(']');
+                        String propType = PropertyType.nameFromValue(expectedType);
+                        I18n msg = JcrI18n.allPropertyValuesMustHaveSameType;
+                        NamespaceRegistry namespaces = SessionCache.this.namespaces;
+                        String path = getPathFor(node.getUuid()).getString(namespaces);
+                        String workspaceName = SessionCache.this.workspaceName();
+                        throw new javax.jcr.ValueFormatException(msg.text(name, values, propType, path, workspaceName));
+                    }
+                    if (value.getType() != valueType && valueType != PropertyType.UNDEFINED) {
+                        value = ((JcrValue)value).asType(valueType);
+                    }
+                    valuesWithDesiredType.add(value);
+                }
+                if (valuesWithDesiredType.isEmpty()) {
+                    newValues = JcrMultiValueProperty.EMPTY_VALUES;
+                } else {
+                    newValues = valuesWithDesiredType.toArray(new Value[valuesWithDesiredType.size()]);
+                }
+            }
+
+            int numValues = newValues.length;
             JcrPropertyDefinition definition = null;
             PropertyId id = null;
 
@@ -836,14 +1144,14 @@
                         // Just use the definition as is ...
                     } else {
                         // Use the property type for the first non-null value ...
-                        int type = values[0].getType();
+                        int type = newValues[0].getType();
                         if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != type) {
                             // The property type is not right, so we have to check if we can cast.
                             // It's easier and could save more work if we just find a new property definition that works ...
                             definition = null;
                         } else {
                             // The types match, so see if the value satisfies the constraints ...
-                            if (!definition.satisfiesConstraints(values)) definition = null;
+                            if (!definition.satisfiesConstraints(newValues)) definition = null;
                         }
                     }
                 }
@@ -856,21 +1164,21 @@
                 definition = nodeTypes().findPropertyDefinition(node.getPrimaryTypeName(),
                                                                 node.getMixinTypeNames(),
                                                                 name,
-                                                                values,
+                                                                newValues,
                                                                 skipProtected);
                 if (definition == null) {
                     throw new ConstraintViolationException();
                 }
             }
             // Create the DNA property ...
-            int type = values.length != 0 ? values[0].getType() : definition.getRequiredType();
-            Object[] objValues = new Object[values.length];
+            int type = newValues.length != 0 ? newValues[0].getType() : definition.getRequiredType();
+            Object[] objValues = new Object[newValues.length];
             int propertyType = definition.getRequiredType();
             if (propertyType == PropertyType.UNDEFINED || propertyType == type) {
                 // Can use the values as is ...
                 propertyType = type;
                 for (int i = 0; i != numValues; ++i) {
-                    objValues[i] = ((JcrValue)values[i]).value();
+                    objValues[i] = ((JcrValue)newValues[i]).value();
                 }
             } else {
                 // A cast is required ...
@@ -878,7 +1186,7 @@
                 org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
                 ValueFactory<?> factory = factories().getValueFactory(dnaPropertyType);
                 for (int i = 0; i != numValues; ++i) {
-                    objValues[i] = factory.create(((JcrValue)values[i]).value());
+                    objValues[i] = factory.create(((JcrValue)newValues[i]).value());
                 }
             }
             Property dnaProp = propertyFactory.create(name, objValues);
@@ -920,9 +1228,11 @@
          * @throws ConstraintViolationException if moving the node into this node violates this node's definition
          * @throws RepositoryException if any other error occurs while reading information from the repository
          */
-        public ChildNode moveToBeChild( UUID nodeUuid, Name newNodeName )
+        public ChildNode moveToBeChild( UUID nodeUuid,
+                                        Name newNodeName )
             throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
 
+            // UUID nodeUuid = child.nodeUuid;
             if (nodeUuid.equals(node.getUuid()) || isAncestor(nodeUuid)) {
                 Path pathOfNode = getPathFor(nodeUuid);
                 Path thisPath = currentLocation.getPath();
@@ -931,33 +1241,19 @@
             }
 
             // Is the node already a child?
-            ChildNode child = node.getChildren().getChild(nodeUuid);
-            if (child != null) return child;
+            ChildNode existingChild = node.getChildren().getChild(nodeUuid);
+            if (existingChild != null) return existingChild;
 
+            JcrNodeDefinition definition = findBestNodeDefinition(node.getUuid(), newNodeName, null);
+
             // Get an editor for the child (in its current location) and one for its parent ...
-            NodeEditor existingNodeEditor = getEditorFor(nodeUuid);
-            ChangedNodeInfo existingNodeInfo = existingNodeEditor.node;
-            UUID existingParent = existingNodeInfo.getParent();
-            NodeEditor existingParentEditor = getEditorFor(existingParent);
-            ChangedNodeInfo existingParentInfo = existingParentEditor.node;
+            NodeEditor newChildEditor = getEditorFor(nodeUuid);
 
-            // Verify that this node's definition allows the specified child ...
-            Name childName = existingParentInfo.getChildren().getChild(nodeUuid).getName();
-            int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(childName);
-            JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(node.getPrimaryTypeName(),
-                                                                               node.getMixinTypeNames(),
-                                                                               childName,
-                                                                               childName,
-                                                                               numSns,
-                                                                               true);
-            if (definition == null) {
-                throw new ConstraintViolationException();
-            }
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING,
-                                                  definition.getId().getString());
+                    JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
+                                                                                                                 .getString());
                     setProperty(DnaLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
@@ -967,27 +1263,85 @@
                     node.setDefinitionId(definition.getId());
 
                     // And remove the property from the info ...
-                    existingNodeEditor.removeProperty(DnaLexicon.NODE_DEFINITON);
+                    newChildEditor.removeProperty(DnaLexicon.NODE_DEFINITON);
                 }
             }
 
             // Remove the node from the current parent and add it to this ...
-            child = existingParentInfo.removeChild(nodeUuid, pathFactory);
-            ChildNode newChild = node.addChild(newNodeName, child.getUuid(), pathFactory);
+            ChangedNodeInfo newChildInfo = newChildEditor.node;
+            UUID existingParent = newChildInfo.getParent();
 
+            ChangedNodeInfo existingParentInfo = getEditorFor(existingParent).node;
+            existingChild = existingParentInfo.removeChild(nodeUuid, pathFactory);
+            ChildNode newChild = node.addChild(newNodeName, existingChild.getUuid(), pathFactory);
+
             // Set the child's changed representation to point to this node as its parent ...
-            existingNodeInfo.setParent(node.getUuid());
-            
+            newChildInfo.setParent(node.getUuid());
+
             // Set up the peer relationship between the two nodes that must be saved together
             node.addPeer(existingParent);
             existingParentInfo.addPeer(node.getUuid());
-            
+
+            // Set up the peer relationship between the two nodes that must be saved together
+            node.addPeer(existingParent);
+            existingParentInfo.addPeer(node.getUuid());
+
             // Now, record the operation to do this ...
-            operations.move(existingNodeEditor.currentLocation).into(currentLocation);
-            
+            operations.move(newChildEditor.currentLocation).into(currentLocation);
+
             return newChild;
         }
 
+        public void addMixin( JcrNodeType mixinCandidateType ) throws javax.jcr.ValueFormatException, RepositoryException {
+            PropertyInfo existingMixinProperty = node.getProperty(JcrLexicon.MIXIN_TYPES);
+
+            // getProperty(JcrLexicon.MIXIN_TYPES);
+            Value[] existingMixinValues;
+            if (existingMixinProperty != null) {
+                existingMixinValues = findJcrProperty(existingMixinProperty.getPropertyId()).getValues();
+            } else {
+                existingMixinValues = new Value[0];
+            }
+
+            Value[] newMixinValues = new Value[existingMixinValues.length + 1];
+            System.arraycopy(existingMixinValues, 0, newMixinValues, 0, existingMixinValues.length);
+            newMixinValues[newMixinValues.length - 1] = new JcrValue(factories(), SessionCache.this, PropertyType.NAME,
+                                                                     mixinCandidateType.getInternalName());
+
+            findJcrProperty(setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, PropertyType.NAME, false));
+
+            // ------------------------------------------------------------------------------
+            // Create any auto-created properties/nodes from new type
+            // ------------------------------------------------------------------------------
+
+            for (JcrPropertyDefinition propertyDefinition : mixinCandidateType.propertyDefinitions()) {
+                if (propertyDefinition.isAutoCreated() && !propertyDefinition.isProtected()) {
+                    if (null == findJcrProperty(new PropertyId(node.getUuid(), propertyDefinition.getInternalName()))) {
+                        assert propertyDefinition.getDefaultValues() != null;
+                        if (propertyDefinition.isMultiple()) {
+                            setProperty(propertyDefinition.getInternalName(),
+                                        propertyDefinition.getDefaultValues(),
+                                        propertyDefinition.getRequiredType());
+                        } else {
+                            assert propertyDefinition.getDefaultValues().length == 1;
+                            setProperty(propertyDefinition.getInternalName(), (JcrValue)propertyDefinition.getDefaultValues()[0]);
+                        }
+                    }
+                }
+            }
+
+            for (JcrNodeDefinition nodeDefinition : mixinCandidateType.childNodeDefinitions()) {
+                if (nodeDefinition.isAutoCreated() && !nodeDefinition.isProtected()) {
+                    Name nodeName = nodeDefinition.getInternalName();
+                    if (!node.getChildren().getChildren(nodeName).hasNext()) {
+                        assert nodeDefinition.getDefaultPrimaryType() != null;
+                        createChild(nodeName, (UUID)null, ((JcrNodeType)nodeDefinition.getDefaultPrimaryType()).getInternalName());
+                    }
+                }
+            }
+
+        }
+
         /**
          * Create a new node as a child of this node, using the supplied name and (optionally) the supplied UUID.
          * 
@@ -1172,7 +1526,7 @@
     }
 
     /**
-     * Find the best property definition in this node's
+     * Find the best property definition in this node's primary type and mixin types.
      * 
      * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null
      * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-23 18:45:00 UTC (rev 853)
@@ -112,3 +112,7 @@
 cannotRedefineProperty=Cannot redefine property '{0}' with new type '{1}' when existing property with same name in type '{2}' has incompatible type '{3}'
 autocreatedPropertyNeedsDefault=Auto-created property '{0}' in type '{1}' must specify a default value
 singleValuedPropertyNeedsSingleValuedDefault=Single-valued property '{0}' in type '{1}' cannot have multiple default values
+
+noDefinition=Cannot find a definition for the {0} named '{1}' on the node at '{2}' with primary type '{3}' and mixin types: {4}
+noSnsDefinition=Cannot find a definition that allows same-name siblings for the child node named '{0}' on the node at '{1}' with primary type '{2}' and mixin types: {3} and a child node already exists with this name
+missingMandatoryItem=The mandatory {0} named '{1}' defined in type '{2}' is missing from the node at '{3}'

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -35,11 +35,13 @@
 import java.util.Map;
 import java.util.Properties;
 import javax.jcr.Credentials;
+import javax.jcr.PropertyType;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 import org.apache.jackrabbit.test.JCRTestSuite;
 import org.apache.jackrabbit.test.RepositoryStub;
 import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.DocumentViewImportTest;
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
 import org.apache.jackrabbit.test.api.NodeAddMixinTest;
 import org.apache.jackrabbit.test.api.NodeCanAddMixinTest;
@@ -50,6 +52,8 @@
 import org.apache.jackrabbit.test.api.PropertyItemIsNewTest;
 import org.apache.jackrabbit.test.api.PropertyTest;
 import org.apache.jackrabbit.test.api.RepositoryLoginTest;
+import org.apache.jackrabbit.test.api.SerializationTest;
+import org.apache.jackrabbit.test.api.SessionTest;
 import org.apache.jackrabbit.test.api.SessionUUIDTest;
 import org.apache.jackrabbit.test.api.SetPropertyBooleanTest;
 import org.apache.jackrabbit.test.api.SetPropertyCalendarTest;
@@ -58,8 +62,6 @@
 import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
 import org.apache.jackrabbit.test.api.SetPropertyLongTest;
 import org.apache.jackrabbit.test.api.SetPropertyNodeTest;
-import org.apache.jackrabbit.test.api.SetPropertyStringTest;
-import org.apache.jackrabbit.test.api.SetPropertyValueTest;
 import org.apache.jackrabbit.test.api.SetValueBinaryTest;
 import org.apache.jackrabbit.test.api.SetValueBooleanTest;
 import org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest;
@@ -77,6 +79,7 @@
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
 import org.jboss.dna.graph.ExecutionContext;
@@ -189,7 +192,7 @@
             addTestSuite(AddNodeTest.class);
             addTestSuite(NamespaceRegistryTest.class);
             // addTestSuite(ReferencesTest.class);
-            // addTestSuite(SessionTest.class);
+            addTestSuite(SessionTest.class);
             addTestSuite(SessionUUIDTest.class);
             // addTestSuite(NodeTest.class);
             // addTestSuite(NodeUUIDTest.class);
@@ -213,8 +216,8 @@
             addTestSuite(SetPropertyInputStreamTest.class);
             addTestSuite(SetPropertyLongTest.class);
             addTestSuite(SetPropertyNodeTest.class);
-            addTestSuite(SetPropertyStringTest.class);
-            addTestSuite(SetPropertyValueTest.class);
+            // addTestSuite(SetPropertyStringTest.class);
+            // addTestSuite(SetPropertyValueTest.class);
             addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
             // addTestSuite(SetPropertyAssumeTypeTest.class);
 
@@ -234,7 +237,7 @@
             addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
             addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
             addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
-            // addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
             // addTestSuite(WorkspaceCopyReferenceableTest.class);
             // addTestSuite(WorkspaceCopySameNameSibsTest.class);
             // addTestSuite(WorkspaceCopyTest.class);
@@ -248,8 +251,8 @@
             // addTestSuite(ImpersonateTest.class);
             // addTestSuite(CheckPermissionTest.class);
 
-            // addTestSuite(DocumentViewImportTest.class);
-            // addTestSuite(SerializationTest.class);
+            addTestSuite(DocumentViewImportTest.class);
+            addTestSuite(SerializationTest.class);
 
             addTestSuite(ValueFactoryTest.class);
         }
@@ -264,7 +267,7 @@
             // We currently don't pass the tests in those suites that are commented out
             // See https://jira.jboss.org/jira/browse/DNA-285
 
-            // addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
+            addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
             // addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
             // addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
             addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
@@ -456,10 +459,51 @@
                                                                     TestLexicon.REFERENCEABLE_UNSTRUCTURED,
                                                                     Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
                                                                     NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES,
-                                                                    NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
-            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {referenceableUnstructured, noSameNameSibs}));
+            JcrNodeType nodeWithMandatoryProperty = new JcrNodeType(
+                                                                    context,
+                                                                    NO_NODE_TYPE_MANAGER,
+                                                                    TestLexicon.NODE_WITH_MANDATORY_PROPERTY,
+                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                    NO_PRIMARY_ITEM_NAME,
+                                                                    NO_CHILD_NODES,
+                                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                                         context,
+                                                                                                                                         null,
+                                                                                                                                         TestLexicon.MANDATORY_STRING,
+                                                                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                                                         false,
+                                                                                                                                         true,
+                                                                                                                                         false,
+                                                                                                                                         NO_DEFAULT_VALUES,
+                                                                                                                                         PropertyType.UNDEFINED,
+                                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                                         false)}),
+                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
+            JcrNodeType nodeWithMandatoryChild = new JcrNodeType(
+                                                                 context,
+                                                                 NO_NODE_TYPE_MANAGER,
+                                                                 TestLexicon.NODE_WITH_MANDATORY_CHILD,
+                                                                 Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                 NO_PRIMARY_ITEM_NAME,
+                                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                              context,
+                                                                                                                              null,
+                                                                                                                              TestLexicon.MANDATORY_CHILD,
+                                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                              false,
+                                                                                                                              true,
+                                                                                                                              false,
+                                                                                                                              false,
+                                                                                                                              JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                              new JcrNodeType[] {base}),}),
+                                                                 NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {referenceableUnstructured, noSameNameSibs,
+                nodeWithMandatoryProperty, nodeWithMandatoryChild,}));
+
         }
 
         /**

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-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -27,10 +27,12 @@
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.stub;
+import java.io.ByteArrayInputStream;
 import java.util.HashMap;
 import java.util.Map;
 import javax.jcr.NamespaceRegistry;
 import javax.jcr.Node;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryManager;
 import org.jboss.dna.graph.ExecutionContext;
@@ -166,7 +168,7 @@
         assertThat(workspace.getNodeTypeManager(), is(notNullValue()));
     }
 
-    @Test( expected = UnsupportedOperationException.class )
+    @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotAllowGetObservationManager() throws Exception {
         workspace.getObservationManager();
     }
@@ -224,9 +226,14 @@
         assertThat(workspace.getSession(), is(notNullValue()));
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowImportXml() throws Exception {
-        workspace.importXML(null, null, 0);
+    @Test
+    public void shouldAllowImportXml() throws Exception {
+        String inputData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            + "<sv:node xmlns:jcr=\"http://www.jcp.org/jcr/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" "
+            + "xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" sv:name=\"workspaceTestNode\">"
+            + "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\">"
+            + "<sv:value>nt:unstructured</sv:value></sv:property></sv:node>";
+        workspace.importXML("/", new ByteArrayInputStream(inputData.getBytes()), 0);
     }
 
     @Test( expected = IllegalArgumentException.class )

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-23 18:45:00 UTC (rev 853)
@@ -47,7 +47,12 @@
     public static final Name CONSTRAINED_REFERENCE = new BasicName(Namespace.URI, "constrainedReference");
     public static final Name CONSTRAINED_STRING = new BasicName(Namespace.URI, "constrainedString");
 
+    public static final Name MANDATORY_STRING = new BasicName(Namespace.URI, "mandatoryString");
+    public static final Name MANDATORY_CHILD = new BasicName(Namespace.URI, "mandatoryChild");
+
     public static final Name REFERENCEABLE_UNSTRUCTURED = new BasicName(Namespace.URI, "referenceableUnstructured");
     public static final Name NO_SAME_NAME_SIBS = new BasicName(Namespace.URI, "noSameNameSibs");
+    public static final Name NODE_WITH_MANDATORY_PROPERTY = new BasicName(Namespace.URI, "nodeWithMandatoryProperty");
+    public static final Name NODE_WITH_MANDATORY_CHILD = new BasicName(Namespace.URI, "nodeWithMandatoryChild");
 
 }

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-23 18:21:01 UTC (rev 852)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-23 18:45:00 UTC (rev 853)
@@ -22,6 +22,8 @@
 javax.jcr.tck.referenceableNodeTestNode=referenceableNodeTestNode
 javax.jcr.tck.orderChildrenTestNode=orderChildrenTestNode
 javax.jcr.tck.namespaceTestNode=namespaceTestNode
+javax.jcr.tck.sameNameSibsTrueNodeType=nt\:unstructured
+javax.jcr.tck.sameNameSibsFalseNodeType=dnatest\:noSameNameSibs
 javax.jcr.tck.sameNameSibsFalseChildNodeDefinition=dnatest\:noSameNameSibs
 javax.jcr.tck.stringTestProperty=stringTestProperty
 javax.jcr.tck.binaryTestProperty=binaryTestProperty
@@ -33,4 +35,9 @@
 javax.jcr.tck.pathTestProperty=pathTestProperty
 javax.jcr.tck.referenceTestProperty=referenceTestProperty
 javax.jcr.tck.multiValueTestProperty=multiValueTestProperty
-javax.jcr.tck.NodeTest.testAddNodeItemExistsException.nodetype=dnatest\:noSameNameSibs
\ No newline at end of file
+javax.jcr.tck.NodeTest.testAddNodeItemExistsException.nodetype=dnatest\:noSameNameSibs
+javax.jcr.tck.NodeOrderableChildNodesTest.nodetype2=dnatest\:referenceableUnstructured
+javax.jcr.tck.SessionTest.testSaveContstraintViolationException.nodetype2=dnatest\:nodeWithMandatoryProperty
+javax.jcr.tck.NodeTest.testRemoveMandatoryNode.nodetype2=dnatest\:nodeWithMandatoryChild
+javax.jcr.tck.NodeTest.testRemoveMandatoryNode.nodename3=dnatest\:mandatoryChild
+javax.jcr.tck.NodeTest.testSaveContstraintViolationException.nodetype2=dnatest\:nodeWithMandatoryProperty



From dna-commits at lists.jboss.org  Thu Apr 23 15:37:09 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 15:37:09 -0400
Subject: [dna-commits] DNA SVN: r854 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lx4jN-0005MZ-1m@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 15:37:08 -0400 (Thu, 23 Apr 2009)
New Revision: 854

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
Log:
DNA-364 Session.move Is Not Implemented

Applied the patch.  Some changes were required, as the before code was a little different than the patch was expecting.

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-04-23 18:45:00 UTC (rev 853)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-23 19:37:08 UTC (rev 854)
@@ -180,7 +180,7 @@
     JcrWorkspace workspace() {
         return this.workspace;
     }
-    
+
     Graph.Batch createBatch() {
         return graph.batch();
     }
@@ -619,6 +619,7 @@
 
         try {
             XMLReader parser = XMLReaderFactory.createXMLReader();
+
             parser.setContentHandler(getImportContentHandler(parentAbsPath, uuidBehavior));
             parser.parse(new InputSource(in));
         } catch (EnclosingSAXException ese) {
@@ -706,7 +707,7 @@
             throw new ItemExistsException();
         }
 
-        newParentNode.editor().moveToBeChild(sourceNode.nodeUuid, newNodeName.getName());
+        newParentNode.editor().moveToBeChild(sourceNode, newNodeName.getName());
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-23 18:45:00 UTC (rev 853)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-23 19:37:08 UTC (rev 854)
@@ -1219,7 +1219,7 @@
          * Move the child specified by the supplied UUID to be a child of this node, appending the child to the end of the current
          * list of children. This method automatically disconnects the node from its current parent.
          * 
-         * @param nodeUuid the UUID of the existing node; may not be null
+         * @param child the UUID of the existing node; may not be null
          * @param newNodeName
          * @return the representation of the newly-added child, which includes the {@link ChildNode#getSnsIndex()
          *         same-name-sibling index}
@@ -1228,11 +1228,11 @@
          * @throws ConstraintViolationException if moving the node into this node violates this node's definition
          * @throws RepositoryException if any other error occurs while reading information from the repository
          */
-        public ChildNode moveToBeChild( UUID nodeUuid,
+        public ChildNode moveToBeChild( AbstractJcrNode child,
                                         Name newNodeName )
             throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
 
-            // UUID nodeUuid = child.nodeUuid;
+            UUID nodeUuid = child.nodeUuid;
             if (nodeUuid.equals(node.getUuid()) || isAncestor(nodeUuid)) {
                 Path pathOfNode = getPathFor(nodeUuid);
                 Path thisPath = currentLocation.getPath();
@@ -1241,8 +1241,9 @@
             }
 
             // Is the node already a child?
+            boolean nameDoesNotChange = newNodeName == null || newNodeName.equals(child.path().getLastSegment());
             ChildNode existingChild = node.getChildren().getChild(nodeUuid);
-            if (existingChild != null) return existingChild;
+            if (existingChild != null && nameDoesNotChange) return existingChild;
 
             JcrNodeDefinition definition = findBestNodeDefinition(node.getUuid(), newNodeName, null);
 
@@ -1287,7 +1288,11 @@
             existingParentInfo.addPeer(node.getUuid());
 
             // Now, record the operation to do this ...
-            operations.move(newChildEditor.currentLocation).into(currentLocation);
+            if (nameDoesNotChange) {
+                operations.move(newChildEditor.currentLocation).into(currentLocation);
+            } else {
+                operations.move(newChildEditor.currentLocation).as(newNodeName).into(currentLocation);
+            }
 
             return newChild;
         }
@@ -1592,19 +1597,18 @@
         JcrNodeDefinition definition = nodeTypes().getNodeDefinition(nodeDefinitionId);
         assert definition != null;
 
-        if (root == null) {
-            // Need to determine if this is the root node ...
-            if (location.getPath().isRoot()) {
-                // It is a root node ...
-                JcrRootNode node = new JcrRootNode(this, uuid);
-                jcrNodes.put(uuid, node);
-                root = uuid;
-                return node;
-            }
+        // Need to determine if this is the root node ...
+        if (location.getPath().isRoot()) {
+            // It is a root node ...
+            JcrRootNode node = new JcrRootNode(this, uuid);
+            jcrNodes.put(uuid, node);
+            root = uuid;
+            return node;
         }
 
         // It is not a root node ...
         JcrNode node = new JcrNode(this, uuid);
+        assert !uuid.equals(root);
         jcrNodes.put(uuid, node);
         return node;
     }

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-04-23 18:45:00 UTC (rev 853)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-04-23 19:37:08 UTC (rev 854)
@@ -30,6 +30,7 @@
 import static org.hamcrest.core.IsNull.nullValue;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.io.ByteArrayInputStream;
@@ -46,6 +47,7 @@
 import javax.jcr.Item;
 import javax.jcr.NamespaceException;
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Repository;
@@ -509,4 +511,18 @@
         node.getUUID();
     }
 
+    @Test
+    public void shouldMoveToNewName() throws Exception {
+        session.move("/a/b/c", "/a/b/d");
+        
+        session.getRootNode().getNode("a").getNode("b").getNode("d");
+        try {
+            session.getRootNode().getNode("a").getNode("b").getNode("c");
+
+            fail("Node still exists at /a/b/c after move");
+        }
+        catch (PathNotFoundException e) {
+            // Expected
+        }
+    }
 }



From dna-commits at lists.jboss.org  Thu Apr 23 16:35:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 16:35:30 -0400
Subject: [dna-commits] DNA SVN: r855 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lx5dq-0007iW-N9@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 16:35:30 -0400 (Thu, 23 Apr 2009)
New Revision: 855

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
Log:
DNA-382 Workspace.move Is Not Supported

Applied the patch to implement Workspace.move

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-04-23 19:37:08 UTC (rev 854)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-23 20:35:30 UTC (rev 855)
@@ -349,30 +349,31 @@
      * 
      * @see javax.jcr.Workspace#move(java.lang.String, java.lang.String)
      */
-    @SuppressWarnings( "unused" )
     public void move( String srcAbsPath,
                       String destAbsPath ) throws PathNotFoundException, RepositoryException {
-        CheckArg.isNotEmpty(srcAbsPath, "srcAbsPath");
-        CheckArg.isNotEmpty(destAbsPath, "destAbsPath");
+        CheckArg.isNotNull(srcAbsPath, "srcAbsPath");
+        CheckArg.isNotNull(destAbsPath, "destAbsPath");
 
-        // Create the paths ...
-        PathFactory factory = context.getValueFactories().getPathFactory();
-        Path srcPath = null;
-        Path destPath = null;
-        try {
-            srcPath = factory.create(srcAbsPath);
-        } catch (ValueFormatException e) {
-            throw new RepositoryException(JcrI18n.invalidPathParameter.text(srcAbsPath, "srcAbsPath"), e);
+        // Use the session's execution context so that we get the transient namespace mappings
+        PathFactory pathFactory = session.getExecutionContext().getValueFactories().getPathFactory();
+        Path destPath = pathFactory.create(destAbsPath);
+
+        Path.Segment newNodeName = destPath.getSegment(destPath.size() - 1);
+        // Doing a literal test here because the path factory will canonicalize "/node[1]" to "/node"
+        if (destAbsPath.endsWith("]")) {
+            throw new RepositoryException();
         }
-        try {
-            destPath = factory.create(destAbsPath);
-        } catch (ValueFormatException e) {
-            throw new RepositoryException(JcrI18n.invalidPathParameter.text(destAbsPath, "destAbsPath"), e);
+
+        AbstractJcrNode sourceNode = session.getNode(pathFactory.create(srcAbsPath));
+        AbstractJcrNode newParentNode = session.getNode(destPath.getParent());
+
+        if (newParentNode.hasNode(newNodeName.getString(session.getExecutionContext().getNamespaceRegistry()))) {
+            throw new ItemExistsException();
         }
 
-        // Perform the copy operation, but use the "to" form (not the "into", which takes the parent) ...
-        // graph.move(srcPath).to(destPath);
-        throw new UnsupportedOperationException();
+        Graph.Batch operations = session.createBatch();
+        newParentNode.editorFor(operations).moveToBeChild(sourceNode, newNodeName.getName());
+        operations.execute();
     }
 
     /**

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-04-23 19:37:08 UTC (rev 854)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-04-23 20:35:30 UTC (rev 855)
@@ -178,7 +178,6 @@
         assertThat(workspace.getQueryManager(), notNullValue());
     }
 
-    @Test
     public void shouldCreateQuery() throws Exception {
         String statement = "Some query syntax";
 
@@ -241,8 +240,8 @@
         workspace.move(null, null);
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowMoveFromPathToAnotherPathInSameWorkspace() throws Exception {
+    @Test
+    public void shouldAllowMoveFromPathToAnotherPathInSameWorkspace() throws Exception {
         workspace.move("/a/b", "/b/b-copy");
     }
 



From dna-commits at lists.jboss.org  Thu Apr 23 17:51:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 23 Apr 2009 17:51:50 -0400
Subject: [dna-commits] DNA SVN: r856 - in trunk/dna-repository/src:
	test/java/org/jboss/dna/repository and 1 other directory.
Message-ID: <E1Lx6pi-0006qS-NX@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-23 17:51:50 -0400 (Thu, 23 Apr 2009)
New Revision: 856

Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
Log:
DNA-58 Create repository configuration and component

Minor change to allow the configuration to have more than one PathExpression in any sequencer.

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 20:35:30 UTC (rev 855)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-23 21:51:50 UTC (rev 856)
@@ -21,8 +21,10 @@
  */
 package org.jboss.dna.repository;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.component.ClassLoaderFactory;
@@ -737,6 +739,7 @@
 
     protected class GraphSequencerDetails implements SequencerDetails {
         private final Path path;
+        private final List<String> compiledExpressions = new ArrayList<String>();
 
         protected GraphSequencerDetails( Path path ) {
             assert path != null;
@@ -780,7 +783,9 @@
          */
         public SequencerDetails sequencingFrom( PathExpression expression ) {
             CheckArg.isNotNull(expression, "expression");
-            configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(expression.getExpression());
+            String compiledExpression = expression.getExpression();
+            if (!compiledExpressions.contains(compiledExpression)) compiledExpressions.add(compiledExpression);
+            configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(compiledExpressions);
             return this;
         }
 

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 20:35:30 UTC (rev 855)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-23 21:51:50 UTC (rev 856)
@@ -253,8 +253,8 @@
                      .usingClass(MockSequencerA.class)
                      .named("The (Main) Sequencer")
                      .describedAs("Mock Sequencer A")
-                     // .sequencingFrom("/foo/source")
-                     // .andOutputtingTo("/foo/target")
+                     .sequencingFrom("/foo/source")
+                     .andOutputtingTo("/foo/target")
                      .sequencingFrom("/bar/source")
                      .andOutputtingTo("/bar/target")
                      .and()
@@ -276,7 +276,7 @@
                                                                                MockSequencerA.class.getName()));
         System.out.println(subgraph.getNode("/dna:sequencers/sequencerA").getProperty(DnaLexicon.PATH_EXPRESSIONS));
         assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.PATH_EXPRESSIONS,
-        // "/foo/source => /foo/target",
+                                                                               "/foo/source => /foo/target",
                                                                                "/bar/source => /bar/target"));
     }
 
@@ -286,6 +286,14 @@
         configuration.addMimeTypeDetector("detector")
                      .usingClass(ExtensionBasedMimeTypeDetector.class)
                      .describedAs("default detector");
+        configuration.addSequencer("sequencerA")
+                     .usingClass(MockSequencerA.class)
+                     .named("The (Main) Sequencer")
+                     .describedAs("Mock Sequencer A")
+                     .sequencingFrom("/foo/source")
+                     .andOutputtingTo("/foo/target")
+                     .sequencingFrom("/bar/source")
+                     .andOutputtingTo("/bar/target");
         configuration.save();
 
         // Verify that the graph has been updated correctly ...
@@ -302,5 +310,15 @@
         assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.DESCRIPTION, "default detector"));
         assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"),
                    hasProperty(DnaLexicon.CLASSNAME, ExtensionBasedMimeTypeDetector.class.getName()));
+        assertThat(subgraph.getNode("/dna:sequencers").getChildren(), hasChild(segment("sequencerA")));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), is(notNullValue()));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.READABLE_NAME, "The (Main) Sequencer"));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.DESCRIPTION, "Mock Sequencer A"));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.CLASSNAME,
+                                                                               MockSequencerA.class.getName()));
+        System.out.println(subgraph.getNode("/dna:sequencers/sequencerA").getProperty(DnaLexicon.PATH_EXPRESSIONS));
+        assertThat(subgraph.getNode("/dna:sequencers/sequencerA"), hasProperty(DnaLexicon.PATH_EXPRESSIONS,
+                                                                               "/foo/source => /foo/target",
+                                                                               "/bar/source => /bar/target"));
     }
 }



From dna-commits at lists.jboss.org  Mon Apr 27 14:45:16 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 27 Apr 2009 14:45:16 -0400
Subject: [dna-commits] DNA SVN: r857 - in trunk/dna-jcr/src:
	main/java/org/jboss/dna/jcr/cache and 1 other directories.
Message-ID: <E1LyVpM-0000Bx-J1@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-27 14:45:16 -0400 (Mon, 27 Apr 2009)
New Revision: 857

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaIntLexicon.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-374 Multiplicity of some properties may change when saved and reloaded from the graph

Added a patch that fixes this issue by maintaining a hidden property to store the names of the properties that use multi-valued definitions but that have only one value.  This property is persisted as required, but is also cleaned up if it is empty or no longer needed.

The patch also moves the "dna:nodeDefinition" and this new property into a new "dnaint" namespace.  This allows some of the code to just check the namespace to know whether properties should be hidden from the JCR view.

Committed the patch, which also added in a number of commented-out TCK unit tests.

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaIntLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaIntLexicon.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaIntLexicon.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ *
+ * 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;
+
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * A lexicon of internal and implementation-specific information
+ */
+public class DnaIntLexicon {
+
+    public static class Namespace {
+        public static final String URI = "http://www.jboss.org/dna/internal/1.0";
+        public static final String PREFIX = "dnaint";
+    }
+
+    public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
+    public static final Name MULTI_VALUED_PROPERTIES = new BasicName(Namespace.URI, "multiValuedProperties");
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -31,9 +31,8 @@
  */
 public class DnaLexicon extends org.jboss.dna.repository.DnaLexicon {
 
-    public static final Name DEFINED = new BasicName(Namespace.URI, "defined");
+    public static final Name BASE = new BasicName(Namespace.URI, "base");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
-    public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
     public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");
     public static final Name ROOT = new BasicName(Namespace.URI, "root");
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -65,25 +65,17 @@
         // Value trueValue = new JcrValue(context.getValueFactories(), null, PropertyType.BOOLEAN, Boolean.TRUE);
         Value ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
 
-        JcrNodeType defined = new JcrNodeType(
-                                              context,
-                                              NO_NODE_TYPE_MANAGER,
-                                              DnaLexicon.DEFINED,
-                                              NO_SUPERTYPES,
-                                              DnaLexicon.NODE_DEFINITON,
-                                              NO_CHILD_NODES,
-                                              Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                   context,
-                                                                                                                   null,
-                                                                                                                   DnaLexicon.NODE_DEFINITON,
-                                                                                                                   OnParentVersionBehavior.INITIALIZE.getJcrValue(),
-                                                                                                                   false,
-                                                                                                                   false,
-                                                                                                                   true,
-                                                                                                                   NO_DEFAULT_VALUES,
-                                                                                                                   PropertyType.STRING,
-                                                                                                                   NO_CONSTRAINTS,
-                                                                                                                   false),}),
+        JcrNodeType defined = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.BASE, NO_SUPERTYPES,
+                                              DnaIntLexicon.NODE_DEFINITON, NO_CHILD_NODES,
+                                              Arrays.asList(new JcrPropertyDefinition[] {
+                                                  new JcrPropertyDefinition(context, null, DnaIntLexicon.NODE_DEFINITON,
+                                                                            OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                            false, false, true, NO_DEFAULT_VALUES,
+                                                                            PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, DnaIntLexicon.MULTI_VALUED_PROPERTIES,
+                                                                            OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                            false, false, true, NO_DEFAULT_VALUES,
+                                                                            PropertyType.STRING, NO_CONSTRAINTS, true),}),
                                               IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
         // Stubbing in child node and property definitions for now
         JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE,
@@ -636,7 +628,7 @@
         // Disabling version-related types until DNA supports versioning, as per section 4.11 of the 1.0.1 specification
         nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {defined, base, unstructured, childNodeDefinition, file, folder,
             frozenNode, hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType /*, version, versionHistory,
-                                                                                                              versionLabels, versionedChild */}));
+                                                                                                                            versionLabels, versionedChild */}));
 
         JcrNodeType lockable = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES,
                                                NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -71,7 +71,7 @@
     static final String XML_SCHEMA_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema";
     static final String XML_SCHEMA_INSTANCE_NAMESPACE_PREFIX = "xsi";
     static final String XML_SCHEMA_INSTANCE_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema-instance";
-    
+
     static final Set<String> STANDARD_BUILT_IN_PREFIXES;
     static final Set<String> STANDARD_BUILT_IN_URIS;
     static final Map<String, String> STANDARD_BUILT_IN_NAMESPACES_BY_PREFIX;
@@ -90,6 +90,7 @@
         namespaces.put(XML_SCHEMA_NAMESPACE_PREFIX, XML_SCHEMA_NAMESPACE_URI);
         namespaces.put(XML_SCHEMA_INSTANCE_NAMESPACE_PREFIX, XML_SCHEMA_INSTANCE_NAMESPACE_URI);
         namespaces.put(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
+        namespaces.put(DnaIntLexicon.Namespace.PREFIX, DnaIntLexicon.Namespace.URI);
         // Set up the reverse map for the standard namespaces ...
         Map<String, String> prefixes = new HashMap<String, String>();
         for (Map.Entry<String, String> entry : namespaces.entrySet()) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -53,6 +53,7 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
@@ -128,6 +129,8 @@
      */
     private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
 
+    private static final Set<Name> EMPTY_NAMES = Collections.emptySet();
+
     private final JcrSession session;
     private final String workspaceName;
     protected final ExecutionContext context;
@@ -391,6 +394,7 @@
                                                                          mixinTypeNames,
                                                                          property.property(),
                                                                          property.getType(),
+                                                                         property.property().isSingle(),
                                                                          false);
             if (definition == null) {
                 throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
@@ -748,7 +752,8 @@
         PropertyInfo info = findPropertyInfo(propertyId); // throws PathNotFoundException if node not there
         if (info == null) return null; // no such property on this node
 
-        if (DnaLexicon.NODE_DEFINITON.equals(info.getPropertyName())) return null;
+        // Skip all internal properties ...
+        if (info.getPropertyName().getNamespaceUri().equals(DnaIntLexicon.Namespace.URI)) return null;
 
         // Now create the appropriate JCR Property object ...
         return createAndCacheJcrPropertyFor(info);
@@ -760,7 +765,7 @@
         Set<Name> propertyNames = info.getPropertyNames();
         Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(propertyNames.size());
         for (Name propertyName : propertyNames) {
-            if (!DnaLexicon.NODE_DEFINITON.equals(propertyName)) {
+            if (!propertyName.getNamespaceUri().equals(DnaIntLexicon.Namespace.URI)) {
                 result.add(findJcrProperty(new PropertyId(nodeUuid, propertyName)));
             }
         }
@@ -1080,8 +1085,7 @@
             assert name != null;
             assert values != null;
 
-            // TODO: Re-add following line!
-            // checkCardinalityOfExistingProperty(name, true);
+            checkCardinalityOfExistingProperty(name, true);
 
             int len = values.length;
             Value[] newValues = null;
@@ -1171,8 +1175,8 @@
                 }
             }
             // Create the DNA property ...
-            int type = newValues.length != 0 ? newValues[0].getType() : definition.getRequiredType();
-            Object[] objValues = new Object[newValues.length];
+            int type = numValues != 0 ? newValues[0].getType() : definition.getRequiredType();
+            Object[] objValues = new Object[numValues];
             int propertyType = definition.getRequiredType();
             if (propertyType == PropertyType.UNDEFINED || propertyType == type) {
                 // Can use the values as is ...
@@ -1198,6 +1202,23 @@
             // Finally update the cached information and record the change ...
             node.setProperty(newProperty, factories());
             operations.set(dnaProp).on(currentLocation);
+
+            // If there is a single value, we need to record that this property is actually a multi-valued property definition ...
+            if (numValues == 1) {
+                if (node.setSingleMultiProperty(name)) {
+                    Set<Name> names = node.getSingleMultiPropertyNames();
+                    // Added this property name to the set, so record the change ...
+                    PropertyInfo singleMulti = createSingleMultiplePropertyInfo(node.getUuid(),
+                                                                                node.getPrimaryTypeName(),
+                                                                                node.getMixinTypeNames(),
+                                                                                names);
+                    node.setProperty(singleMulti, factories());
+                    operations.set(singleMulti.getProperty()).on(currentLocation);
+                }
+            } else {
+                removeSingleMultiProperty(node, name);
+            }
+
             return newProperty.getPropertyId();
         }
 
@@ -1208,13 +1229,35 @@
          * @return true if there was a property with the supplied name, or false if no such property existed
          */
         public boolean removeProperty( Name name ) {
-            if (node.removeProperty(name) != null) {
+            PropertyInfo info = node.removeProperty(name);
+            if (info != null) {
                 operations.remove(name).on(currentLocation);
+                // Is this named in the single-multi property names? ...
+                removeSingleMultiProperty(node, name);
                 return true;
             }
             return false;
         }
 
+        private void removeSingleMultiProperty( ChangedNodeInfo node,
+                                                Name propertyName ) {
+            if (node.removeSingleMultiProperty(propertyName)) {
+                Set<Name> names = node.getSingleMultiPropertyNames();
+                if (names == null || names.isEmpty()) {
+                    node.removeProperty(DnaIntLexicon.MULTI_VALUED_PROPERTIES);
+                    operations.remove(DnaIntLexicon.MULTI_VALUED_PROPERTIES).on(currentLocation);
+                } else {
+                    // Added this property name to the set, so record the change ...
+                    PropertyInfo singleMulti = createSingleMultiplePropertyInfo(node.getUuid(),
+                                                                                node.getPrimaryTypeName(),
+                                                                                node.getMixinTypeNames(),
+                                                                                names);
+                    node.setProperty(singleMulti, factories());
+                    operations.set(singleMulti.getProperty()).on(currentLocation);
+                }
+            }
+        }
+
         /**
          * Move the child specified by the supplied UUID to be a child of this node, appending the child to the end of the current
          * list of children. This method automatically disconnects the node from its current parent.
@@ -1255,7 +1298,7 @@
                 try {
                     JcrValue value = new JcrValue(factories(), SessionCache.this, PropertyType.STRING, definition.getId()
                                                                                                                  .getString());
-                    setProperty(DnaLexicon.NODE_DEFINITON, value);
+                    setProperty(DnaIntLexicon.NODE_DEFINITON, value);
                 } catch (ConstraintViolationException e) {
                     // We can't set this property on the node (according to the node definition).
                     // But we still want the node info to have the correct node definition.
@@ -1264,7 +1307,7 @@
                     node.setDefinitionId(definition.getId());
 
                     // And remove the property from the info ...
-                    newChildEditor.removeProperty(DnaLexicon.NODE_DEFINITON);
+                    newChildEditor.removeProperty(DnaIntLexicon.NODE_DEFINITON);
                 }
             }
 
@@ -1428,7 +1471,7 @@
             // Create the properties ...
             Map<Name, PropertyInfo> properties = new HashMap<Name, PropertyInfo>();
             Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
-            Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
+            Property nodeDefinitionProp = propertyFactory.create(DnaIntLexicon.NODE_DEFINITON, definition.getId().getString());
 
             // Now add the "jcr:uuid" property if and only if referenceable ...
             if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) {
@@ -1457,6 +1500,7 @@
                                                                               node.getMixinTypeNames(),
                                                                               primaryTypeProp,
                                                                               PropertyType.NAME,
+                                                                              true,
                                                                               false);
             PropertyDefinitionId primaryTypeDefinitionId = primaryTypeDefn.getId();
             PropertyInfo primaryTypeInfo = new PropertyInfo(new PropertyId(desiredUuid, primaryTypeProp.getName()),
@@ -1469,6 +1513,7 @@
                                                                            node.getMixinTypeNames(),
                                                                            nodeDefinitionProp,
                                                                            PropertyType.STRING,
+                                                                           false,
                                                                            false);
             if (nodeDefnDefn != null) {
                 PropertyDefinitionId nodeDefnDefinitionId = nodeDefnDefn.getId();
@@ -1538,6 +1583,8 @@
      *        to include in the search
      * @param dnaProperty the new property that is to be set on this node
      * @param propertyType the property type; must be a valid {@link PropertyType} value
+     * @param isSingle true if the property definition should be single-valued, or false if the property definition should allow
+     *        multiple values
      * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
      *        this operation is being done from within internal implementations
      * @return the property definition that allows setting this property, or null if there is no such definition
@@ -1546,6 +1593,7 @@
                                                                List<Name> mixinTypeNamesOfParent,
                                                                Property dnaProperty,
                                                                int propertyType,
+                                                               boolean isSingle,
                                                                boolean skipProtected ) {
         JcrPropertyDefinition definition = null;
         if (propertyType == PropertyType.UNDEFINED) {
@@ -1553,7 +1601,7 @@
         }
 
         // If single-valued ...
-        if (dnaProperty.isSingle()) {
+        if (isSingle) {
             // Create a value for the DNA property value ...
             Object value = dnaProperty.getFirstValue();
             Value jcrValue = new JcrValue(factories(), SessionCache.this, propertyType, value);
@@ -2047,7 +2095,7 @@
 
         // Look for a node definition stored on the node ...
         JcrNodeDefinition definition = null;
-        org.jboss.dna.graph.property.Property nodeDefnProperty = graphProperties.get(DnaLexicon.NODE_DEFINITON);
+        org.jboss.dna.graph.property.Property nodeDefnProperty = graphProperties.get(DnaIntLexicon.NODE_DEFINITON);
         if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
             String nodeDefinitionString = stringFactory.create(nodeDefnProperty.getFirstValue());
             NodeDefinitionId id = NodeDefinitionId.fromString(nodeDefinitionString, nameFactory);
@@ -2089,7 +2137,7 @@
         if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
 
         // The process the mixin types ...
-        org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
+        Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
         List<Name> mixinTypeNames = null;
         if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
             for (Object mixinTypeValue : mixinTypesProperty) {
@@ -2102,17 +2150,31 @@
             }
         }
 
+        // Create the set of multi-valued property names ...
+        Set<Name> multiValuedPropertyNames = EMPTY_NAMES;
+        Set<Name> newSingleMultiPropertyNames = null;
+        Property multiValuedPropNamesProp = graphProperties.get(DnaIntLexicon.MULTI_VALUED_PROPERTIES);
+        if (multiValuedPropNamesProp != null && !multiValuedPropNamesProp.isEmpty()) {
+            multiValuedPropertyNames = getSingleMultiPropertyNames(multiValuedPropNamesProp, location.getPath(), uuid);
+        }
+
         // Now create the JCR property object wrappers around the other properties ...
         Map<Name, PropertyInfo> props = new HashMap<Name, PropertyInfo>();
-        for (org.jboss.dna.graph.property.Property dnaProp : graphProperties.values()) {
+        for (Property dnaProp : graphProperties.values()) {
             Name name = dnaProp.getName();
 
+            // Is this is single-valued property?
+            boolean isSingle = dnaProp.isSingle();
+            // Make sure that this isn't a multi-valued property with one value ...
+            if (isSingle && multiValuedPropertyNames.contains(name)) isSingle = false;
+
             // Figure out the JCR property type for this property ...
             int propertyType = PropertyTypeUtil.jcrPropertyTypeFor(dnaProp);
             PropertyDefinition propertyDefinition = findBestPropertyDefintion(primaryTypeName,
                                                                               mixinTypeNames,
                                                                               dnaProp,
                                                                               propertyType,
+                                                                              isSingle,
                                                                               false);
 
             // If there still is no property type defined ...
@@ -2138,6 +2200,12 @@
                 continue;
             }
 
+            // Update the list of single-valued multi-property names ...
+            if (isMultiple && isSingle) {
+                if (newSingleMultiPropertyNames == null) newSingleMultiPropertyNames = new HashSet<Name>();
+                newSingleMultiPropertyNames.add(name);
+            }
+
             // Figure out the property type ...
             int definitionType = propertyDefinition.getRequiredType();
             if (definitionType != PropertyType.UNDEFINED) {
@@ -2173,6 +2241,15 @@
         // Make sure the "dna:uuid" property did not get in there ...
         props.remove(DnaLexicon.UUID);
 
+        // Make sure the single-valued multi-property names are stored as a property ...
+        if (newSingleMultiPropertyNames != null) {
+            PropertyInfo info = createSingleMultiplePropertyInfo(uuid,
+                                                                 primaryTypeName,
+                                                                 mixinTypeNames,
+                                                                 newSingleMultiPropertyNames);
+            props.put(info.getPropertyName(), info);
+        }
+
         // Create the node information ...
         UUID parentUuid = parentInfo != null ? parentInfo.getUuid() : null;
         List<Location> locations = graphNode.getChildren();
@@ -2181,6 +2258,69 @@
         return new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definition.getId(), parentUuid, children, props);
     }
 
+    protected final PropertyInfo createSingleMultiplePropertyInfo( UUID uuid,
+                                                                   Name primaryTypeName,
+                                                                   List<Name> mixinTypeNames,
+                                                                   Set<Name> newSingleMultiPropertyNames ) {
+        int number = newSingleMultiPropertyNames.size();
+        String[] names = new String[number];
+        JcrValue[] values = new JcrValue[number];
+        if (number == 1) {
+            String str = newSingleMultiPropertyNames.iterator().next().getString(namespaces);
+            names[0] = str;
+            values[0] = new JcrValue(factories(), this, PropertyType.STRING, str);
+        } else {
+            int index = 0;
+            for (Name name : newSingleMultiPropertyNames) {
+                String str = name.getString(namespaces);
+                names[index] = str;
+                values[index] = new JcrValue(factories(), this, PropertyType.STRING, str);
+                ++index;
+            }
+        }
+        PropertyDefinition propertyDefinition = nodeTypes().findPropertyDefinition(primaryTypeName,
+                                                                                   mixinTypeNames,
+                                                                                   DnaIntLexicon.MULTI_VALUED_PROPERTIES,
+                                                                                   values,
+                                                                                   false);
+        Property dnaProp = propertyFactory.create(DnaIntLexicon.MULTI_VALUED_PROPERTIES, newSingleMultiPropertyNames.iterator()
+                                                                                                                    .next());
+        PropertyId propId = new PropertyId(uuid, dnaProp.getName());
+        JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+        return new PropertyInfo(propId, defn.getId(), PropertyType.STRING, dnaProp, defn.isMultiple(), true, false);
+    }
+
+    protected final Set<Name> getSingleMultiPropertyNames( Property dnaProperty,
+                                                           Path knownPath,
+                                                           UUID knownUuid ) {
+        Set<Name> multiValuedPropertyNames = new HashSet<Name>();
+        for (Object value : dnaProperty) {
+            try {
+                multiValuedPropertyNames.add(nameFactory.create(value));
+            } catch (ValueFormatException e) {
+                String msg = "{0} value \"{1}\" on {2} in \"{3}\" workspace is not a valid name and is being ignored";
+                String path = null;
+                if (knownPath != null) {
+                    path = knownPath.getString(namespaces);
+                } else {
+                    assert knownUuid != null;
+                    try {
+                        path = getPathFor(knownUuid).getString(namespaces);
+                    } catch (RepositoryException err) {
+                        path = knownUuid.toString();
+                    }
+                }
+                Logger.getLogger(getClass()).trace(e,
+                                                   msg,
+                                                   DnaIntLexicon.MULTI_VALUED_PROPERTIES.getString(namespaces),
+                                                   value,
+                                                   path,
+                                                   workspaceName());
+            }
+        }
+        return multiValuedPropertyNames;
+    }
+
     /**
      * This method finds the {@link NodeInfo} for the node with the supplied UUID and marks it as being deleted, and does the same
      * for all decendants (e.g., children, grandchildren, great-grandchildren, etc.) that have been cached or changed.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -32,6 +32,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
@@ -40,7 +41,7 @@
 import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.Path.Segment;
-import org.jboss.dna.jcr.DnaLexicon;
+import org.jboss.dna.jcr.DnaIntLexicon;
 import org.jboss.dna.jcr.NodeDefinitionId;
 
 /**
@@ -49,6 +50,7 @@
  * Each instance maintains a reference to the original (usually immutable) NodeInfo representation that was probably read from the
  * repository.
  */
+ at NotThreadSafe
 public class ChangedNodeInfo implements NodeInfo {
 
     protected static final PropertyInfo DELETED_PROPERTY = null;
@@ -88,6 +90,8 @@
 
     private List<UUID> peers;
 
+    private Set<Name> singleMultiPropertyNames;
+
     /**
      * Create an immutable NodeInfo instance.
      * 
@@ -127,6 +131,19 @@
         peers.add(peerUuid);
     }
 
+    public boolean setSingleMultiProperty( Name name ) {
+        if (singleMultiPropertyNames == null) singleMultiPropertyNames = new HashSet<Name>();
+        return singleMultiPropertyNames.add(name);
+    }
+
+    public boolean removeSingleMultiProperty( Name name ) {
+        return singleMultiPropertyNames == null ? false : singleMultiPropertyNames.remove(name);
+    }
+
+    public Set<Name> getSingleMultiPropertyNames() {
+        return singleMultiPropertyNames;
+    }
+
     /**
      * Return the original node information. May be null if this is a new node.
      * 
@@ -213,8 +230,8 @@
 
     /**
      * Set the identifier of the node definition for this node. This should normally be changed by
-     * {@link #setProperty(PropertyInfo, ValueFactories) setting} the {@link DnaLexicon#NODE_DEFINITON} property. However, since
-     * that property is not always allowed, this method provides a way to set it locally (without requiring a property).
+     * {@link #setProperty(PropertyInfo, ValueFactories) setting} the {@link DnaIntLexicon#NODE_DEFINITON} property. However,
+     * since that property is not always allowed, this method provides a way to set it locally (without requiring a property).
      * 
      * @param definitionId the new property definition identifier; may not be null
      * @see #setProperty(PropertyInfo, ValueFactories)
@@ -404,7 +421,7 @@
             for (Object value : newProperty.getProperty()) {
                 changedMixinTypeNames.add(nameFactory.create(value));
             }
-        } else if (name.equals(DnaLexicon.NODE_DEFINITON)) {
+        } else if (name.equals(DnaIntLexicon.NODE_DEFINITON)) {
             ValueFactory<String> stringFactory = factories.getStringFactory();
             String value = stringFactory.create(newProperty.getProperty().getFirstValue());
             changedDefinitionId = NodeDefinitionId.fromString(value, factories.getNameFactory());

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -62,6 +62,8 @@
 import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
 import org.apache.jackrabbit.test.api.SetPropertyLongTest;
 import org.apache.jackrabbit.test.api.SetPropertyNodeTest;
+import org.apache.jackrabbit.test.api.SetPropertyStringTest;
+import org.apache.jackrabbit.test.api.SetPropertyValueTest;
 import org.apache.jackrabbit.test.api.SetValueBinaryTest;
 import org.apache.jackrabbit.test.api.SetValueBooleanTest;
 import org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest;
@@ -216,8 +218,8 @@
             addTestSuite(SetPropertyInputStreamTest.class);
             addTestSuite(SetPropertyLongTest.class);
             addTestSuite(SetPropertyNodeTest.class);
-            // addTestSuite(SetPropertyStringTest.class);
-            // addTestSuite(SetPropertyValueTest.class);
+            addTestSuite(SetPropertyStringTest.class);
+            addTestSuite(SetPropertyValueTest.class);
             addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
             // addTestSuite(SetPropertyAssumeTypeTest.class);
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-04-23 21:51:50 UTC (rev 856)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-04-27 18:45:16 UTC (rev 857)
@@ -188,6 +188,8 @@
                     }
                 } else if (propertyName.equals(JcrLexicon.PRIMARY_TYPE)) {
                     // This is okay
+                } else if (propertyName.equals(DnaIntLexicon.MULTI_VALUED_PROPERTIES)) {
+                    // This is okay
                 } else {
                     fail("missing property \"" + propertyName + "\" on " + dnaNode);
                 }



From dna-commits at lists.jboss.org  Mon Apr 27 14:58:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 27 Apr 2009 14:58:30 -0400
Subject: [dna-commits] DNA SVN: r858 - in trunk: dna-jcr/src/test/resources
	and 3 other directories.
Message-ID: <E1LyW2A-0003bF-2N@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-27 14:58:29 -0400 (Mon, 27 Apr 2009)
New Revision: 858

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
Removed:
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
   trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties
Modified:
   trunk/dna-jcr-tck/pom.xml
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-383 dna-jcr-tck Project Keeps Getting Out Of Sync With dna-jcr Project

Applied the patch, which removes the redundant files in dna-jcr-tck and fixes the POM.

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-27 18:58:29 UTC (rev 858)
@@ -0,0 +1,328 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.net.URI;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import javax.jcr.Credentials;
+import javax.jcr.PropertyType;
+import org.apache.jackrabbit.test.RepositoryStub;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrMixLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+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.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.io.GraphImporter;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.JcrRepository.Options;
+
+/**
+ * Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
+ */
+public class InMemoryRepositoryStub extends RepositoryStub {
+    private JcrRepository repository;
+    protected InMemoryRepositorySource source;
+    protected AccessControlContext accessControlContext = AccessController.getContext();
+
+    private Credentials superUserCredentials = new Credentials() {
+        private static final long serialVersionUID = 1L;
+
+        @SuppressWarnings( "unused" )
+        public AccessControlContext getAccessControlContext() {
+            return accessControlContext;
+        }
+    };
+
+    private Credentials readWriteCredentials = new Credentials() {
+        private static final long serialVersionUID = 1L;
+
+        @SuppressWarnings( "unused" )
+        public AccessControlContext getAccessControlContext() {
+            return accessControlContext;
+        }
+    };
+
+    private Credentials readOnlyCredentials = new Credentials() {
+        private static final long serialVersionUID = 1L;
+
+        @SuppressWarnings( "unused" )
+        public AccessControlContext getAccessControlContext() {
+            return accessControlContext;
+        }
+    };
+
+    protected ExecutionContext executionContext = new ExecutionContext();
+
+    protected RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
+        public RepositoryConnection createConnection( String sourceName ) {
+            return source.getConnection();
+        }
+    };
+
+    public InMemoryRepositoryStub( Properties env ) {
+        super(env);
+
+        // Create the in-memory (DNA) repository
+        source = new InMemoryRepositorySource();
+
+        // Various calls will fail if you do not set a non-null name for the source
+        source.setName("TestRepositorySource");
+
+        // Make sure the path to the namespaces exists ...
+        Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
+        graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
+
+        // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
+        Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
+
+        repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(), null,
+                                       options);
+        RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
+
+        // Set up some sample nodes in the graph to match the expected test configuration
+        try {
+            nodeTypes.registerNodeTypes(new TckTestNodeTypeSource(executionContext, nodeTypes));
+
+            executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
+
+            Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
+            GraphImporter importer = new GraphImporter(graph);
+
+            URI xmlContent = new File("src/test/resources/repositoryForTckTests.xml").toURI();
+            Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
+            batch.execute();
+
+        } catch (Exception ex) {
+            // The TCK tries to quash this exception. Print it out to be more obvious.
+            ex.printStackTrace();
+            throw new IllegalStateException("Repository initialization failed.", ex);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.jackrabbit.test.RepositoryStub#getSuperuserCredentials()
+     */
+    @Override
+    public Credentials getSuperuserCredentials() {
+        return superUserCredentials;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
+     */
+    @Override
+    public Credentials getReadOnlyCredentials() {
+        return readOnlyCredentials;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
+     */
+    @Override
+    public Credentials getReadWriteCredentials() {
+        return readWriteCredentials;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
+     */
+    @Override
+    public JcrRepository getRepository() {
+        return repository;
+    }
+
+    class TckTestNodeTypeSource extends AbstractJcrNodeTypeSource {
+        /** The list of node types. */
+        private final List<JcrNodeType> nodeTypes;
+
+        TckTestNodeTypeSource( ExecutionContext context,
+                               RepositoryNodeTypeManager nodeTypeManager ) {
+            super(null);
+
+            nodeTypes = new ArrayList<JcrNodeType>();
+
+            JcrNodeType base = nodeTypeManager.getNodeType(JcrNtLexicon.BASE);
+
+            if (base == null) {
+                String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.NO_SAME_NAME_SIBS.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            JcrNodeType referenceable = nodeTypeManager.getNodeType(JcrMixLexicon.REFERENCEABLE);
+
+            if (referenceable == null) {
+                String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            JcrNodeType unstructured = nodeTypeManager.getNodeType(JcrNtLexicon.UNSTRUCTURED);
+
+            if (unstructured == null) {
+                String baseTypeName = JcrNtLexicon.UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
+                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+            }
+
+            // Stubbing in child node and property definitions for now
+            JcrNodeType noSameNameSibs = new JcrNodeType(
+                                                         context,
+                                                         NO_NODE_TYPE_MANAGER,
+                                                         TestLexicon.NO_SAME_NAME_SIBS,
+                                                         Arrays.asList(new JcrNodeType[] {base}),
+                                                         NO_PRIMARY_ITEM_NAME,
+                                                         Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                      context,
+                                                                                                                      null,
+                                                                                                                      ALL_NODES,
+                                                                                                                      OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      false,
+                                                                                                                      JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                      new JcrNodeType[] {base}),}),
+                                                         NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType referenceableUnstructured = new JcrNodeType(
+                                                                    context,
+                                                                    NO_NODE_TYPE_MANAGER,
+                                                                    TestLexicon.REFERENCEABLE_UNSTRUCTURED,
+                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                    NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES,
+                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+            JcrNodeType nodeWithMandatoryProperty = new JcrNodeType(
+                                                                    context,
+                                                                    NO_NODE_TYPE_MANAGER,
+                                                                    TestLexicon.NODE_WITH_MANDATORY_PROPERTY,
+                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                    NO_PRIMARY_ITEM_NAME,
+                                                                    NO_CHILD_NODES,
+                                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                                         context,
+                                                                                                                                         null,
+                                                                                                                                         TestLexicon.MANDATORY_STRING,
+                                                                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                                                         false,
+                                                                                                                                         true,
+                                                                                                                                         false,
+                                                                                                                                         NO_DEFAULT_VALUES,
+                                                                                                                                         PropertyType.UNDEFINED,
+                                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                                         false)}),
+                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
+            JcrNodeType nodeWithMandatoryChild = new JcrNodeType(
+                                                                 context,
+                                                                 NO_NODE_TYPE_MANAGER,
+                                                                 TestLexicon.NODE_WITH_MANDATORY_CHILD,
+                                                                 Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
+                                                                 NO_PRIMARY_ITEM_NAME,
+                                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                              context,
+                                                                                                                              null,
+                                                                                                                              TestLexicon.MANDATORY_CHILD,
+                                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                              false,
+                                                                                                                              true,
+                                                                                                                              false,
+                                                                                                                              false,
+                                                                                                                              JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                              new JcrNodeType[] {base}),}),
+                                                                 NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+            JcrNodeType unorderableUnstructured = new JcrNodeType(
+                                                                  context,
+                                                                  NO_NODE_TYPE_MANAGER,
+                                                                  TestLexicon.UNORDERABLE_UNSTRUCTURED,
+                                                                  Arrays.asList(new JcrNodeType[] {base}),
+                                                                  NO_PRIMARY_ITEM_NAME,
+                                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                               context,
+                                                                                                                               null,
+                                                                                                                               ALL_NODES,
+                                                                                                                               OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                               false,
+                                                                                                                               false,
+                                                                                                                               false,
+                                                                                                                               true,
+                                                                                                                               TestLexicon.UNORDERABLE_UNSTRUCTURED,
+                                                                                                                               new JcrNodeType[] {base}),}),
+                                                                  Arrays.asList(new JcrPropertyDefinition[] {
+                                                                      new JcrPropertyDefinition(
+                                                                                                context,
+                                                                                                null,
+                                                                                                ALL_NODES,
+                                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                false, false, false,
+                                                                                                NO_DEFAULT_VALUES,
+                                                                                                PropertyType.UNDEFINED,
+                                                                                                NO_CONSTRAINTS, false),
+                                                                      new JcrPropertyDefinition(
+                                                                                                context,
+                                                                                                null,
+                                                                                                ALL_NODES,
+                                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                false, false, false,
+                                                                                                NO_DEFAULT_VALUES,
+                                                                                                PropertyType.UNDEFINED,
+                                                                                                NO_CONSTRAINTS, true),}),
+                                                                  NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {referenceableUnstructured, noSameNameSibs,
+                nodeWithMandatoryProperty, nodeWithMandatoryChild, unorderableUnstructured,}));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
+        }
+    }
+}


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

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-04-27 18:58:29 UTC (rev 858)
@@ -23,23 +23,9 @@
  */
 package org.jboss.dna.jcr;
 
-import java.io.File;
-import java.net.URI;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import javax.jcr.Credentials;
-import javax.jcr.PropertyType;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 import org.apache.jackrabbit.test.JCRTestSuite;
-import org.apache.jackrabbit.test.RepositoryStub;
 import org.apache.jackrabbit.test.api.AddNodeTest;
 import org.apache.jackrabbit.test.api.DocumentViewImportTest;
 import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
@@ -83,18 +69,8 @@
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.JcrMixLexicon;
-import org.jboss.dna.graph.JcrNtLexicon;
-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.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.io.GraphImporter;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Options;
 
 /**
  * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests. Note that technically these are not the
@@ -244,7 +220,7 @@
             // addTestSuite(WorkspaceCopySameNameSibsTest.class);
             // addTestSuite(WorkspaceCopyTest.class);
             addTestSuite(WorkspaceCopyVersionableTest.class);
-            // addTestSuite(WorkspaceMoveReferenceableTest.class);
+            addTestSuite(WorkspaceMoveReferenceableTest.class);
             // addTestSuite(WorkspaceMoveSameNameSibsTest.class);
             // addTestSuite(WorkspaceMoveTest.class);
             addTestSuite(WorkspaceMoveVersionableTest.class);
@@ -275,249 +251,4 @@
             addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
         }
     }
-
-    /**
-     * Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
-     */
-    public static class InMemoryRepositoryStub extends RepositoryStub {
-        private JcrRepository repository;
-        protected InMemoryRepositorySource source;
-        protected AccessControlContext accessControlContext = AccessController.getContext();
-
-        private Credentials superUserCredentials = new Credentials() {
-            private static final long serialVersionUID = 1L;
-
-            @SuppressWarnings( "unused" )
-            public AccessControlContext getAccessControlContext() {
-                return accessControlContext;
-            }
-        };
-
-        private Credentials readWriteCredentials = new Credentials() {
-            private static final long serialVersionUID = 1L;
-
-            @SuppressWarnings( "unused" )
-            public AccessControlContext getAccessControlContext() {
-                return accessControlContext;
-            }
-        };
-
-        private Credentials readOnlyCredentials = new Credentials() {
-            private static final long serialVersionUID = 1L;
-
-            @SuppressWarnings( "unused" )
-            public AccessControlContext getAccessControlContext() {
-                return accessControlContext;
-            }
-        };
-
-        protected ExecutionContext executionContext = new ExecutionContext();
-
-        protected RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
-            public RepositoryConnection createConnection( String sourceName ) {
-                return source.getConnection();
-            }
-        };
-
-        public InMemoryRepositoryStub( Properties env ) {
-            super(env);
-
-            // Create the in-memory (DNA) repository
-            source = new InMemoryRepositorySource();
-
-            // Various calls will fail if you do not set a non-null name for the source
-            source.setName("TestRepositorySource");
-
-            // Make sure the path to the namespaces exists ...
-            Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
-            graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
-
-            // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
-            Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
-
-            repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(),
-                                           null, options);
-            RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
-
-            // Set up some sample nodes in the graph to match the expected test configuration
-            try {
-                nodeTypes.registerNodeTypes(new TckTestNodeTypeSource(executionContext, nodeTypes));
-
-                executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
-
-                Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
-                GraphImporter importer = new GraphImporter(graph);
-
-                URI xmlContent = new File("src/test/resources/repositoryForTckTests.xml").toURI();
-                Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
-                batch.execute();
-
-            } catch (Exception ex) {
-                // The TCK tries to quash this exception. Print it out to be more obvious.
-                ex.printStackTrace();
-                throw new IllegalStateException("Repository initialization failed.", ex);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.jackrabbit.test.RepositoryStub#getSuperuserCredentials()
-         */
-        @Override
-        public Credentials getSuperuserCredentials() {
-            return superUserCredentials;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
-         */
-        @Override
-        public Credentials getReadOnlyCredentials() {
-            return readOnlyCredentials;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
-         */
-        @Override
-        public Credentials getReadWriteCredentials() {
-            return readWriteCredentials;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
-         */
-        @Override
-        public JcrRepository getRepository() {
-            return repository;
-        }
-
-    }
-
-    static class TckTestNodeTypeSource extends AbstractJcrNodeTypeSource {
-        /** The list of node types. */
-        private final List<JcrNodeType> nodeTypes;
-
-        TckTestNodeTypeSource( ExecutionContext context,
-                               RepositoryNodeTypeManager nodeTypeManager ) {
-            super(null);
-
-            nodeTypes = new ArrayList<JcrNodeType>();
-
-            JcrNodeType base = nodeTypeManager.getNodeType(JcrNtLexicon.BASE);
-
-            if (base == null) {
-                String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
-                String namespaceTypeName = TestLexicon.NO_SAME_NAME_SIBS.getString(context.getNamespaceRegistry());
-                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
-            }
-
-            JcrNodeType referenceable = nodeTypeManager.getNodeType(JcrMixLexicon.REFERENCEABLE);
-
-            if (referenceable == null) {
-                String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry());
-                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
-                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
-            }
-
-            JcrNodeType unstructured = nodeTypeManager.getNodeType(JcrNtLexicon.UNSTRUCTURED);
-
-            if (unstructured == null) {
-                String baseTypeName = JcrNtLexicon.UNSTRUCTURED.getString(context.getNamespaceRegistry());
-                String namespaceTypeName = TestLexicon.REFERENCEABLE_UNSTRUCTURED.getString(context.getNamespaceRegistry());
-                throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
-            }
-
-            // Stubbing in child node and property definitions for now
-            JcrNodeType noSameNameSibs = new JcrNodeType(
-                                                         context,
-                                                         NO_NODE_TYPE_MANAGER,
-                                                         TestLexicon.NO_SAME_NAME_SIBS,
-                                                         Arrays.asList(new JcrNodeType[] {base}),
-                                                         NO_PRIMARY_ITEM_NAME,
-                                                         Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                                      context,
-                                                                                                                      null,
-                                                                                                                      ALL_NODES,
-                                                                                                                      OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                                                                      false,
-                                                                                                                      false,
-                                                                                                                      false,
-                                                                                                                      false,
-                                                                                                                      JcrNtLexicon.UNSTRUCTURED,
-                                                                                                                      new JcrNodeType[] {base}),}),
-                                                         NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
-
-            JcrNodeType referenceableUnstructured = new JcrNodeType(
-                                                                    context,
-                                                                    NO_NODE_TYPE_MANAGER,
-                                                                    TestLexicon.REFERENCEABLE_UNSTRUCTURED,
-                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
-                                                                    NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES,
-                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
-
-            JcrNodeType nodeWithMandatoryProperty = new JcrNodeType(
-                                                                    context,
-                                                                    NO_NODE_TYPE_MANAGER,
-                                                                    TestLexicon.NODE_WITH_MANDATORY_PROPERTY,
-                                                                    Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
-                                                                    NO_PRIMARY_ITEM_NAME,
-                                                                    NO_CHILD_NODES,
-                                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                                         context,
-                                                                                                                                         null,
-                                                                                                                                         TestLexicon.MANDATORY_STRING,
-                                                                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
-                                                                                                                                         false,
-                                                                                                                                         true,
-                                                                                                                                         false,
-                                                                                                                                         NO_DEFAULT_VALUES,
-                                                                                                                                         PropertyType.UNDEFINED,
-                                                                                                                                         NO_CONSTRAINTS,
-                                                                                                                                         false)}),
-                                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
-
-            JcrNodeType nodeWithMandatoryChild = new JcrNodeType(
-                                                                 context,
-                                                                 NO_NODE_TYPE_MANAGER,
-                                                                 TestLexicon.NODE_WITH_MANDATORY_CHILD,
-                                                                 Arrays.asList(new JcrNodeType[] {unstructured, referenceable}),
-                                                                 NO_PRIMARY_ITEM_NAME,
-                                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                                              context,
-                                                                                                                              null,
-                                                                                                                              TestLexicon.MANDATORY_CHILD,
-                                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                                                                              false,
-                                                                                                                              true,
-                                                                                                                              false,
-                                                                                                                              false,
-                                                                                                                              JcrNtLexicon.UNSTRUCTURED,
-                                                                                                                              new JcrNodeType[] {base}),}),
-                                                                 NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
-
-            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {referenceableUnstructured, noSameNameSibs,
-                nodeWithMandatoryProperty, nodeWithMandatoryChild,}));
-
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
-         */
-        @Override
-        public Collection<JcrNodeType> getDeclaredNodeTypes() {
-            return nodeTypes;
-        }
-
-    }
-
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-04-27 18:58:29 UTC (rev 858)
@@ -28,7 +28,6 @@
 
 /**
  * Namespace and names for DNA testing.
- *
  */
 public class TestLexicon {
 
@@ -55,4 +54,5 @@
     public static final Name NODE_WITH_MANDATORY_PROPERTY = new BasicName(Namespace.URI, "nodeWithMandatoryProperty");
     public static final Name NODE_WITH_MANDATORY_CHILD = new BasicName(Namespace.URI, "nodeWithMandatoryChild");
 
+    public static final Name UNORDERABLE_UNSTRUCTURED = new BasicName(Namespace.URI, "unorderableUnstructured");
 }

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-04-27 18:58:29 UTC (rev 858)
@@ -1,4 +1,4 @@
-javax.jcr.tck.repository_stub_impl=org.jboss.dna.jcr.JcrTckTest$InMemoryRepositoryStub
+javax.jcr.tck.repository_stub_impl=org.jboss.dna.jcr.InMemoryRepositoryStub
 javax.jcr.tck.testroot=/testroot
 javax.jcr.tck.nodename1=node1
 javax.jcr.tck.nodename2=node2
@@ -41,3 +41,5 @@
 javax.jcr.tck.NodeTest.testRemoveMandatoryNode.nodetype2=dnatest\:nodeWithMandatoryChild
 javax.jcr.tck.NodeTest.testRemoveMandatoryNode.nodename3=dnatest\:mandatoryChild
 javax.jcr.tck.NodeTest.testSaveContstraintViolationException.nodetype2=dnatest\:nodeWithMandatoryProperty
+javax.jcr.tck.NodeOrderableChildNodesTest.testOrderBeforeUnsupportedRepositoryOperationException.nodetype2=dnatest\:unorderableUnstructured
+javax.jcr.tck.NodeOrderableChildNodesTest.testOrderBeforeUnsupportedRepositoryOperationException.nodetype3=dnatest\:unorderableUnstructured
\ No newline at end of file

Modified: trunk/dna-jcr-tck/pom.xml
===================================================================
--- trunk/dna-jcr-tck/pom.xml	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr-tck/pom.xml	2009-04-27 18:58:29 UTC (rev 858)
@@ -49,6 +49,13 @@
       <artifactId>dna-jcr</artifactId>
       <version>${pom.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-jcr</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
     <!-- 
     Testing (note the scope)
     -->

Deleted: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-04-27 18:58:29 UTC (rev 858)
@@ -1,155 +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;
-
-import java.io.File;
-import java.net.URI;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Properties;
-import javax.jcr.Credentials;
-import javax.jcr.Repository;
-import org.apache.jackrabbit.test.RepositoryStub;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.JcrNtLexicon;
-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.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.io.GraphImporter;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Options;
-
-/**
- * Class with TCK repository stub. This class does not contain any tests.
- */
-public class InMemoryRepositoryStub extends RepositoryStub {
-    private Repository repository;
-    protected RepositoryConnection connection;
-    protected AccessControlContext accessControlContext = AccessController.getContext();
-
-    private Credentials credentials = new Credentials() {
-        private static final long serialVersionUID = 1L;
-
-        @SuppressWarnings( "unused" )
-        public AccessControlContext getAccessControlContext() {
-            return accessControlContext;
-        }
-    };
-
-    protected ExecutionContext executionContext = new ExecutionContext() {
-
-        @Override
-        public ExecutionContext create( AccessControlContext accessControlContext ) {
-            return executionContext;
-        }
-    };
-
-    protected RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
-        public RepositoryConnection createConnection( String sourceName ) {
-            return connection;
-        }
-    };
-
-    public InMemoryRepositoryStub( Properties env ) {
-        super(env);
-
-        // Create the in-memory (DNA) repository
-        InMemoryRepositorySource source = new InMemoryRepositorySource();
-
-        // Various calls will fail if you do not set a non-null name for the source
-        source.setName("TestRepositorySource");
-
-        // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
-        Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "true");
-        connection = source.getConnection();
-        repository = new JcrRepository(executionContext.create(accessControlContext), connectionFactory, source.getName(), null,
-                                       options);
-
-        // Make sure the path to the namespaces exists ...
-        Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
-        graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
-
-        // Set up some sample nodes in the graph to match the expected test configuration
-        try {
-
-            // TODO: Should there be an easier way to define these since they will be needed for all JCR repositories?
-            executionContext.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
-            executionContext.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
-            executionContext.getNamespaceRegistry().register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
-            executionContext.getNamespaceRegistry().register(JcrSvLexicon.Namespace.PREFIX, JcrSvLexicon.Namespace.URI);
-
-            Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
-            GraphImporter importer = new GraphImporter(graph);
-
-            URI xmlContent = new File("src/test/resources/repositoryForTckTests.xml").toURI();
-            Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
-            batch.execute();
-
-        } catch (Exception ex) {
-            // The TCK tries to quash this exception. Print it out to be more obvious.
-            ex.printStackTrace();
-            throw new IllegalStateException("Repository initialization failed.", ex);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.apache.jackrabbit.test.RepositoryStub#getSuperuserCredentials()
-     */
-    @Override
-    public Credentials getSuperuserCredentials() {
-        // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
-        // SimpleCredentials.
-        return credentials;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
-     */
-    @Override
-    public Credentials getReadOnlyCredentials() {
-        // TODO: Why must we override this method? The default TCK implementation just returns a particular instance of
-        // SimpleCredentials.
-        return credentials;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
-     */
-    @Override
-    public Repository getRepository() {
-        return repository;
-    }
-
-}

Deleted: trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties	2009-04-27 18:45:16 UTC (rev 857)
+++ trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties	2009-04-27 18:58:29 UTC (rev 858)
@@ -1,9 +0,0 @@
-javax.jcr.tck.repository_stub_impl=org.jboss.dna.jcr.InMemoryRepositoryStub
-javax.jcr.tck.testroot=/testroot
-javax.jcr.tck.nodename1=node1
-javax.jcr.tck.nodename2=node2
-javax.jcr.tck.nodename3=node3
-javax.jcr.tck.nodename4=node4
-javax.jcr.tck.propertyname1=prop1
-javax.jcr.tck.propertyname2=prop2
-javax.jcr.tck.workspacename=



From dna-commits at lists.jboss.org  Mon Apr 27 21:50:54 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 27 Apr 2009 21:50:54 -0400
Subject: [dna-commits] DNA SVN: r859 - in trunk:
	docs/examples/gettingstarted/sequencers/src/main/java/org/jboss/example/dna/sequencer
	and 4 other directories.
Message-ID: <E1LycTG-00031D-NX@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-27 21:50:54 -0400 (Mon, 27 Apr 2009)
New Revision: 859

Removed:
   trunk/extensions/dna-sequencer-java/src/test/java/org/jboss/dna/jackrabbitcdnsupport/
Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java
   trunk/docs/examples/gettingstarted/sequencers/src/main/java/org/jboss/example/dna/sequencer/SequencingClient.java
   trunk/docs/examples/gettingstarted/sequencers/src/test/java/org/jboss/example/dna/sequencer/SequencingClientTest.java
   trunk/extensions/dna-sequencer-java/pom.xml
   trunk/extensions/dna-sequencer-java/src/main/java/org/jboss/dna/sequencer/java/JavaMetadataSequencer.java
Log:
DNA-384 Trouble compiling Java sequencer using Eclipse JDT and its dependencies

Adjusted the Eclipse JDT dependency fragments, removing some of its dependencies that remain unused in the "dna-sequencer-java" project.

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java	2009-04-27 18:58:29 UTC (rev 858)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencerConfig.java	2009-04-28 01:50:54 UTC (rev 859)
@@ -39,11 +39,30 @@
 
     private final Set<SequencerPathExpression> pathExpressions;
 
-    public SequencerConfig( String name, String description, Map<String, Object> properties, String classname, String[] classpath, String... pathExpressions ) {
+    public SequencerConfig( String name,
+                            String description,
+                            String classname,
+                            String[] classpath,
+                            String... pathExpressions ) {
+        this(name, description, System.currentTimeMillis(), null, classname, classpath, pathExpressions);
+    }
+
+    public SequencerConfig( String name,
+                            String description,
+                            Map<String, Object> properties,
+                            String classname,
+                            String[] classpath,
+                            String... pathExpressions ) {
         this(name, description, System.currentTimeMillis(), properties, classname, classpath, pathExpressions);
     }
 
-    public SequencerConfig( String name, String description, long timestamp, Map<String, Object> properties, String classname, String[] classpath, String... pathExpressions ) {
+    public SequencerConfig( String name,
+                            String description,
+                            long timestamp,
+                            Map<String, Object> properties,
+                            String classname,
+                            String[] classpath,
+                            String... pathExpressions ) {
         super(name, description, timestamp, properties, classname, classpath);
         this.pathExpressions = buildPathExpressionSet(pathExpressions);
     }

Modified: trunk/docs/examples/gettingstarted/sequencers/src/main/java/org/jboss/example/dna/sequencer/SequencingClient.java
===================================================================
--- trunk/docs/examples/gettingstarted/sequencers/src/main/java/org/jboss/example/dna/sequencer/SequencingClient.java	2009-04-27 18:58:29 UTC (rev 858)
+++ trunk/docs/examples/gettingstarted/sequencers/src/main/java/org/jboss/example/dna/sequencer/SequencingClient.java	2009-04-28 01:50:54 UTC (rev 859)
@@ -168,7 +168,8 @@
 
                 try {
                     // Register the node types (only valid the first time) ...
-                    JackrabbitNodeTypeManager mgr = (JackrabbitNodeTypeManager)this.keepAliveSession.getWorkspace().getNodeTypeManager();
+                    JackrabbitNodeTypeManager mgr = (JackrabbitNodeTypeManager)this.keepAliveSession.getWorkspace()
+                                                                                                    .getNodeTypeManager();
                     mgr.registerNodeTypes(cndFile.openStream(), JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
                 } catch (RepositoryException e) {
                     if (!e.getMessage().contains("already exists")) throw e;
@@ -287,9 +288,11 @@
         this.sequencingService.getAdministrator().shutdown();
         this.sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
 
-        // Shut down the observation service ...
-        this.observationService.getAdministrator().shutdown();
-        this.observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+        if (this.observationService != null) {
+            // Shut down the observation service ...
+            this.observationService.getAdministrator().shutdown();
+            this.observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+        }
     }
 
     /**
@@ -396,7 +399,8 @@
                             // import informations
                             javaElements = new ArrayList<Properties>();
                             try {
-                                for (NodeIterator singleImportIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:singleImport").getNodes(); singleImportIterator.hasNext();) {
+                                for (NodeIterator singleImportIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:singleImport")
+                                                                                            .getNodes(); singleImportIterator.hasNext();) {
                                     Node javasingleTypeImportDeclarationNode = singleImportIterator.nextNode();
                                     javaElements.add(extractJavaInfo(javasingleTypeImportDeclarationNode));
                                 }
@@ -407,7 +411,8 @@
 
                             javaElements = new ArrayList<Properties>();
                             try {
-                                for (NodeIterator javaImportOnDemandIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:importOnDemand").getNodes(); javaImportOnDemandIterator.hasNext();) {
+                                for (NodeIterator javaImportOnDemandIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:importOnDemand")
+                                                                                                  .getNodes(); javaImportOnDemandIterator.hasNext();) {
                                     Node javaImportOnDemandtDeclarationNode = javaImportOnDemandIterator.nextNode();
                                     javaElements.add(extractJavaInfo(javaImportOnDemandtDeclarationNode));
                                 }
@@ -424,7 +429,8 @@
 
                             // field member informations
                             javaElements = new ArrayList<Properties>();
-                            for (NodeIterator javaFieldTypeIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:field/java:fieldType").getNodes(); javaFieldTypeIterator.hasNext();) {
+                            for (NodeIterator javaFieldTypeIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:field/java:fieldType")
+                                                                                         .getNodes(); javaFieldTypeIterator.hasNext();) {
                                 Node rootFieldTypeNode = javaFieldTypeIterator.nextNode();
                                 if (rootFieldTypeNode.hasNode("java:primitiveType")) {
                                     Node javaPrimitiveTypeNode = rootFieldTypeNode.getNode("java:primitiveType");
@@ -449,7 +455,8 @@
 
                             // constructor informations
                             javaElements = new ArrayList<Properties>();
-                            for (NodeIterator javaConstructorIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:constructor").getNodes(); javaConstructorIterator.hasNext();) {
+                            for (NodeIterator javaConstructorIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:constructor")
+                                                                                           .getNodes(); javaConstructorIterator.hasNext();) {
                                 Node javaConstructor = javaConstructorIterator.nextNode();
                                 javaElements.add(extractJavaInfo(javaConstructor));
                             }
@@ -457,7 +464,8 @@
 
                             // method informations
                             javaElements = new ArrayList<Properties>();
-                            for (NodeIterator javaMethodIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:method").getNodes(); javaMethodIterator.hasNext();) {
+                            for (NodeIterator javaMethodIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:method")
+                                                                                      .getNodes(); javaMethodIterator.hasNext();) {
                                 Node javaMethod = javaMethodIterator.nextNode();
                                 javaElements.add(extractJavaInfo(javaMethod));
                             }

Modified: trunk/docs/examples/gettingstarted/sequencers/src/test/java/org/jboss/example/dna/sequencer/SequencingClientTest.java
===================================================================
--- trunk/docs/examples/gettingstarted/sequencers/src/test/java/org/jboss/example/dna/sequencer/SequencingClientTest.java	2009-04-27 18:58:29 UTC (rev 858)
+++ trunk/docs/examples/gettingstarted/sequencers/src/test/java/org/jboss/example/dna/sequencer/SequencingClientTest.java	2009-04-28 01:50:54 UTC (rev 859)
@@ -31,7 +31,6 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import org.jboss.dna.common.util.FileUtil;
-import org.jboss.example.dna.sequencer.SequencingClient;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -65,9 +64,15 @@
 
     @After
     public void afterEach() throws Exception {
-        client.shutdownDnaServices();
-        client.shutdownRepository();
-        FileUtil.delete("target/repositoryData");
+        try {
+            client.shutdownDnaServices();
+        } finally {
+            try {
+                client.shutdownRepository();
+            } finally {
+                FileUtil.delete("target/repositoryData");
+            }
+        }
     }
 
     @Test

Modified: trunk/extensions/dna-sequencer-java/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-java/pom.xml	2009-04-27 18:58:29 UTC (rev 858)
+++ trunk/extensions/dna-sequencer-java/pom.xml	2009-04-28 01:50:54 UTC (rev 859)
@@ -87,49 +87,49 @@
 			<groupId>net.jcip</groupId>
 			<artifactId>jcip-annotations</artifactId>
 		</dependency>
-		<dependency>
-			<groupId>org.eclipse.jdt</groupId>
-			<artifactId>core</artifactId>
-			<version>3.2.3.v_686_R32x</version>
-		</dependency>
-		<dependency>
-			<groupId>org.eclipse.core</groupId>
-			<artifactId>runtime</artifactId>
-			<version>3.3.100-v20070530</version>
-			<!-- Exclude this problem library -->
-			<exclusions>
-				<exclusion>
-					<groupId>org.eclipse.equinox</groupId>
-					<artifactId>app</artifactId>
-				</exclusion>
-			</exclusions>
-		</dependency>
-		<dependency>
-			<groupId>org.eclipse.core</groupId>
-			<artifactId>resources</artifactId>
-			<version>3.3.0-v20070604</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-io</groupId>
-			<artifactId>commons-io</artifactId>
-			<version>1.4</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-lang</groupId>
-			<artifactId>commons-lang</artifactId>
-			<version>2.4</version>
-		</dependency>
 		<!-- 
-			Apache Jackrabbit (JCR Implementation)
+			Eclipse Java Development Tools (JDT) parser and its (many) required libraries
 		-->
-		<dependency>
-			<groupId>org.apache.jackrabbit</groupId>
-			<artifactId>jackrabbit-api</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.jackrabbit</groupId>
-			<artifactId>jackrabbit-core</artifactId>
-		</dependency>
+    <dependency>
+      <groupId>org.eclipse.jdt</groupId>
+      <artifactId>core</artifactId>
+      <version>3.4.2.v_883_R34x</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.core</groupId>
+      <artifactId>resources</artifactId>
+      <version>[3.2.0,4.0.0)</version>
+      <exclusions>
+        <exclusion>
+			    <groupId>org.eclipse.core.runtime.compatibility</groupId>
+			    <artifactId>auth</artifactId>
+        </exclusion>
+        <exclusion>
+			    <groupId>org.eclipse.equinox</groupId>
+			    <artifactId>app</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse</groupId>
+      <artifactId>osgi</artifactId>
+      <version>[3.2.0,4.0.0)</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.core</groupId>
+      <artifactId>runtime</artifactId>
+      <version>[3.2.0,4.0.0)</version>
+      <exclusions>
+        <exclusion>
+			    <groupId>org.eclipse.core.runtime.compatibility</groupId>
+			    <artifactId>auth</artifactId>
+        </exclusion>
+        <exclusion>
+			    <groupId>org.eclipse.equinox</groupId>
+			    <artifactId>app</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
 	</dependencies>
 	<reporting>
 		<plugins>

Modified: trunk/extensions/dna-sequencer-java/src/main/java/org/jboss/dna/sequencer/java/JavaMetadataSequencer.java
===================================================================
--- trunk/extensions/dna-sequencer-java/src/main/java/org/jboss/dna/sequencer/java/JavaMetadataSequencer.java	2009-04-27 18:58:29 UTC (rev 858)
+++ trunk/extensions/dna-sequencer-java/src/main/java/org/jboss/dna/sequencer/java/JavaMetadataSequencer.java	2009-04-28 01:50:54 UTC (rev 859)
@@ -26,7 +26,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
-import org.apache.commons.lang.StringUtils;
 import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
@@ -200,7 +199,8 @@
             // sequence package declaration of a unit.
             PackageMetadata packageMetadata = javaMetadata.getPackageMetadata();
             if (packageMetadata != null) {
-                if (StringUtils.isNotEmpty(packageMetadata.getName())) {
+                String packageName = packageMetadata.getName();
+                if (packageName != null && packageName.length() != 0) {
 
                     Path javaPackageDeclarationChildNode = pathFactory.create(JAVA_COMPILATION_UNIT_NODE + SLASH
                                                                               + JAVA_PACKAGE_CHILD_NODE + SLASH



From dna-commits at lists.jboss.org  Tue Apr 28 11:04:22 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 28 Apr 2009 11:04:22 -0400
Subject: [dna-commits] DNA SVN: r860 - in trunk/docs:
	reference/src/main/docbook/en-US/content and 1 other directory.
Message-ID: <E1Lyor8-0006V8-Gd@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-28 11:04:22 -0400 (Tue, 28 Apr 2009)
New Revision: 860

Modified:
   trunk/docs/gettingstarted/src/main/docbook/en-US/content/introduction.xml
   trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml
   trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml
   trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
   trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
   trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml
Log:
DNA-385 Documentation Updates for 0.4

Applied the patch that updated some of the content in the Getting Started and Reference Guide documents.

Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/content/introduction.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/content/introduction.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/content/introduction.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -95,7 +95,7 @@
   </para>
   <para> JBoss DNA has other features as well.  You can create federated repositories that dynamically merge the information 
 	  from multiple databases, services, applications, and other JCR repositories.  JBoss DNA also will allow you to 
-    create customized views based upon the type of data and the role of the user that is accessing the data. And yet another is
+    create customized views based upon the type of data and the role of the user that is accessing the data. And yet another goal is
     to create a REST-ful API to allow the JCR content to be accessed easily by other applications written in other languages.
   </para>
   <para>

Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -32,7 +32,7 @@
     <para>JBoss DNA is a repository and set of tools that make it easy to capture, version, analyze, and understand the
       fundamental building blocks of information. As models, service and process definitions, schemas, source code, and other
       artifacts are added to the repository, JBoss DNA "sequences" the makeup of these components and extracts their structure
-      and interdependencies. The JBoss DNA web application allows end users to access, visualize, and edit this information in
+      and interdependencies. The JBoss DNA web application will allow end users to access, visualize, and edit this information in
       the terminology and structure they are familiar with. Such domain-specific solutions can be easily created with little or
       no programming.</para>
     <para> JBoss DNA supports the Java Content Repository (JCR) standard and is able to provide a single integrated view of
@@ -356,10 +356,16 @@
       <title>Connecting to information sources</title>
       <para>
         JBoss DNA uses connectors to interact with different information sources to get at the content
-        in those systems. Some ideas for connectors include:</para>
+        in those systems. We already have some connectors, including:</para>
       <itemizedlist>
         <listitem>
           <para>
+            <emphasis role="strong">In-Memory Connector</emphasis>
+            - Uses a Map instance as a repository. This is a simple connector that works really well for small and transient graphs.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">JBoss Cache Connector</emphasis>
             - Uses a JBoss Cache instance as a repository.  JBoss Cache is a powerful cache capable of persisting the information
  						and being clustered for concurrent use by multiple processes.
@@ -372,28 +378,22 @@
 						This is a powerful connector that is discussed in more detail in the <link linkend="federated_repositories">next section</link>.
           </para>
         </listitem>
-			</itemizedlist>
-			<para>There are also a number of connectors that are planned:</para>
-      <itemizedlist>
         <listitem>
           <para>
-            <emphasis role="strong">JCR Repository Connector</emphasis>
-            - Connect to and interact with other JCR repositories.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">File System Connector</emphasis>
             - Expose the files and directories on a file system through JCR.
           </para>
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">Maven 2 Repository Connector</emphasis>
-            - Access and expose the contents of a Maven 2 repository (either on the local file system or via HTTP) through
-            JCR.
+            <emphasis role="strong">JDBC Storage Connector</emphasis>
+            - Store and access information in a relational database through JPA. Also useful for persisting information in the federated
+            repository not stored elsewhere.
           </para>
         </listitem>
+      </itemizedlist>
+			<para>We're working on a few other connectors:</para>
+      <itemizedlist>
         <listitem>
           <para>
             <emphasis role="strong">JDBC Metadata Connector</emphasis>
@@ -402,35 +402,44 @@
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">UDDI Connector</emphasis>
-            - Interact with UDDI registries to integrate their content into a repository.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">SVN Connector</emphasis>
             - Interact with Subversion software configuration management (SCM) repositories to expose the managed resources
             through JCR. Consider using the
             <ulink url="http://svnkit.com/">SVNkit</ulink>
             (dual license) library for an API into Subversion.
           </para>
+        </listitem>        
+      </itemizedlist>
+			<para>And even more connectors that are planned:</para>
+      <itemizedlist>
+        <listitem>
+          <para>
+            <emphasis role="strong">JCR Repository Connector</emphasis>
+            - Connect to and interact with other JCR repositories.
+          </para>
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">CVS Connector</emphasis>
-            - Interact with CVS software configuration management (SCM) repositories to expose the managed resources through
+            <emphasis role="strong">Maven 2 Repository Connector</emphasis>
+            - Access and expose the contents of a Maven 2 repository (either on the local file system or via HTTP) through
             JCR.
           </para>
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">JDBC Storage Connector</emphasis>
-            - Store and access information in a relational database. Also useful for persisting information in the federated
-            repository not stored elsewhere.
+            <emphasis role="strong">UDDI Connector</emphasis>
+            - Interact with UDDI registries to integrate their content into a repository.
           </para>
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">CVS Connector</emphasis>
+            - Interact with CVS software configuration management (SCM) repositories to expose the managed resources through
+            JCR.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">Distributed Database Connector</emphasis>
             - Store and access information in a
             <ulink url="http://www.hypertable.org/">Hypertable</ulink>

Modified: trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -50,7 +50,8 @@
 		<note>
 			<para>
 				You should be able to use the <ulink url="http://java.sun.com/javase/downloads/index.jsp">latest JDK</ulink>,
-				which is currently JDK 6.  We periodically try to build JBoss DNA using JDK 6, but it's not our official JDK (yet).
+				which is currently JDK 6.  It is possible to build JBoss DNA using JDK 6 without any code changes, but it's 
+				not our official JDK (yet).
 			</para>
 			<para>
 				Why do we build using JDK 5 and not 6? The main reason is that if we were to use JDK 6, then JBoss DNA couldn't really be used in any

Modified: trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -179,7 +179,7 @@
 	  </para>
 	  <para> JBoss DNA has other features as well.  You can create federated repositories that dynamically merge the information 
 		  from multiple databases, services, applications, and other JCR repositories.  JBoss DNA also will allow you to 
-	    create customized views based upon the type of data and the role of the user that is accessing the data. And yet another is
+	    create customized views based upon the type of data and the role of the user that is accessing the data. And yet another goal is
 	    to create a REST-ful API to allow the JCR content to be accessed easily by other applications written in other languages.
 	  </para>
 	</sect1>
@@ -289,15 +289,30 @@
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-cnd</emphasis>
+            provides a utilities for reading and writing files in the CND (Compact Node Definition) format.
+          </para>
+        </listitem>        
+        <listitem>
+          <para>
             <emphasis role="strong">dna-jcr</emphasis>
             provides the JBoss DNA implementation of the JCR API, which relies upon a repository connector, such as the
             Federation Connector (see
             <code>dna-connector-federation</code>
-            ).
+            ).  As DNA does not fully implement the JCR 1.0.1 specification, there are a series of tests that are currently commented 
+            out in this module.  The <code>dna-jcr-tck</code> module contains all of these tests.
           </para>
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-jcr-tck</emphasis>
+            provides a separate testing project that executes all JackRabbit JCR TCK tests on a nightly basis to track implementation
+            progress against the JCR 1.0 specification.  This module will likely be retired when the <code>dna-jcr</code> implementation
+            is complete.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">dna-integration-tests</emphasis>
             provides a home for all of the integration tests that involve more components that just unit tests. Integration
             tests are often more complicated, take longer, and involve testing the integration and functionality of many
@@ -313,7 +328,7 @@
       <itemizedlist>
         <listitem>
           <para>
-            <emphasis role="strong">dna-maven-classloader</emphasis>
+            <emphasis role="strong">dna-classloader-maven</emphasis>
             is a small library that provides a
             <code>ClassLoaderFactory</code>
             implementation that can create
@@ -324,6 +339,12 @@
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-common-jdbc</emphasis>
+            contains several helpful utility classes for interacting with JDBC connections.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">dna-connector-federation</emphasis>
             is a DNA repository connector that federates, integrates and caches information from multiple sources (via other
             repository connectors).
@@ -331,6 +352,21 @@
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-connector-filesystem</emphasis>
+            is a DNA repository connector that provides read-only access to file systems, allowing their structure and data to be 
+            viewed as repository content.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">dna-connector-jdbc-metadata</emphasis>
+            is a prototype DNA repository connector that provides read-only access to metadata from relational databases through a JDBC 
+            connection.
+                        <emphasis>This is still under development.</emphasis>
+          </para>
+        </listitem>        
+        <listitem>
+          <para>
             <emphasis role="strong">dna-connector-jbosscache</emphasis>
             is a DNA repository connector that manages content within a
             <ulink url="http://www.jboss.org/jbosscache/">JBoss Cache</ulink>
@@ -340,6 +376,13 @@
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-connector-store-jpa</emphasis>
+            is a DNA sequencer that provides for persistent storage and access of DNA content in a relational database.  This connector
+            is based on JPA technology.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">dna-connector-svn</emphasis>
             is a prototype DNA sequencer that obtains content from a Subversion repository, providing that content in
 						the form of <code>nt:file</code> and <code>nt:folder</code> nodes.
@@ -390,8 +433,7 @@
         <listitem>
           <para>
             <emphasis role="strong">dna-sequencer-cnd</emphasis>
-            is a prototype DNA sequencer that extracts JCR node definitions from JCR Compact Node Definition (CND) files.
-						<emphasis>This is still under development.</emphasis>
+            is a DNA sequencer that extracts JCR node definitions from JCR Compact Node Definition (CND) files.
           </para>
         </listitem>
         <listitem>
@@ -403,6 +445,12 @@
         </listitem>
         <listitem>
           <para>
+            <emphasis role="strong">dna-sequencer-java</emphasis>
+            is a DNA sequencer that extracts the structure (methods, fields) from Java source files.
+          </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>

Modified: trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -35,7 +35,7 @@
     <ulink url="&JSR170;">Content Repository for Java technology API</ulink>
     provides a standard Java API for working with content repositories. Abbreviated "JCR", this API was developed as part of the
     Java Community Process under <ulink url="&JSR170;">JSR-170</ulink> (JCR 1.0) and is being revised under <ulink url="&JSR283;">JSR-283</ulink>.
-		JBoss DNA provides a JCR 1.0 implementation that allows you to work with the contents of a repository using the
+		JBoss DNA provides a partial JCR 1.0 implementation that allows you to work with the contents of a repository using the
 		JCR API.  For information about how to use the JCR API, please see the <ulink url="&JSR170;">JSR-170</ulink> specification.
   </para>
   <sect1 id="jcr-repositories">

Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml	2009-04-28 01:50:54 UTC (rev 859)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml	2009-04-28 15:04:22 UTC (rev 860)
@@ -341,17 +341,12 @@
     <sect2 id="dna-sequencer-msoffice">
       <title>Microsoft Office document sequencer</title>
       <para>
-      	This sequencer is included in JBoss DNA and processes Microsoft Office documents, including Excel spreadsheets
-				and PowerPoint presentations.  With presentations, the sequencer extracts the slides, titles, text and slide thumbnails.
+      	This sequencer is included in JBoss DNA and processes Microsoft Office documents, including Word documents, Excel spreadsheets,
+				and PowerPoint presentations.  With documents, the sequencer extracts the internal structure based on Heading styles.
+				With presentations, the sequencer extracts the slides, titles, text and slide thumbnails.
 				With spreadsheets, the sequencer extracts the names of the sheets.  And, the sequencer extracts for all the files the
 				general file information, including the name of the author, title, keywords, subject, comments, and various dates.
       </para>
-			<note>
-				<para>
-					Currently, Word documents are not supported.  For more information and the latest status, see
-					<ulink url="&JIRA;-153">DNA-153</ulink>.
-				</para>
-			</note>
 			<para>
 				To use this sequencer, simply include the <code>dna-sequencer-msoffice</code> JAR and all of the
 				<ulink url="http://poi.apache.org/">POI</ulink> JARs
@@ -441,10 +436,24 @@
     <sect2 id="dna-sequencer-cnd">
       <title>JCR Compact Node Definition (CND) file sequencer</title>
       <para>
-      	This sequencer is incomplete and is not currently usable.  The purpose is to sequence JCR Compact Node Definition (CND) files
-				to extract the node definitions with their property definitions, and inserting these into the repository using JCR standard notation.
+      	This sequencer processes JCR Compact Node Definition (CND) files
+				to extract the node definitions with their property definitions, and inserts these into the repository using JCR standard notation.
       </para>
     </sect2>
+        <sect2 id="dna-sequencer-jbpm-jpdl">
+      <title>jBPM JPDL file sequencer</title>
+      <para>
+        This sequencer is incomplete and is not currently usable.  The purpose is to sequence jBPM JPDL files to identify metadata
+        about processes, their actors, and their dependencies.
+      </para>
+    </sect2>
+    <sect2 id="dna-sequencer-xml">
+      <title>XML file sequencer</title>
+      <para>
+        This sequencer stores the structure and data of an XML file into the repository.
+      </para>
+    </sect2> 
+    
   </sect1>
   <sect1 id="custom-sequencers">
 	  <title>Creating custom sequencers</title>



From dna-commits at lists.jboss.org  Tue Apr 28 15:33:36 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 28 Apr 2009 15:33:36 -0400
Subject: [dna-commits] DNA SVN: r861 -
	trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
Message-ID: <E1Lyt3g-0001Ic-32@committer01.frg.pub.inap.atl.jboss.com>

Author: spagop
Date: 2009-04-28 15:33:35 -0400 (Tue, 28 Apr 2009)
New Revision: 861

Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
Log:
patched of BCar

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-04-28 15:04:22 UTC (rev 860)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-04-28 19:33:35 UTC (rev 861)
@@ -300,12 +300,12 @@
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
 
-        assertThat(prop.satisfiesConstraints(valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("+1945-07-31T01:30:00.000", PropertyType.DATE)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("+0001-08-01T01:30:00.000", PropertyType.DATE)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:30:00.000", PropertyType.DATE)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:31:00.000", PropertyType.DATE)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("+2009-08-01T01:30:00.000", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("-1945-08-01T01:30:00.000Z", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("+1945-07-31T01:30:00.000Z", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("+0001-08-01T01:30:00.000Z", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:30:00.000Z", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:31:00.000Z", PropertyType.DATE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("+2009-08-01T01:30:00.000Z", PropertyType.DATE)), is(true));
     }
 
     @Test
@@ -313,10 +313,10 @@
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
 
-        Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE),
-            valueFor("+2009-08-01T01:30:00.000", PropertyType.DATE),};
+        Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000Z", PropertyType.DATE),
+            valueFor("+2009-08-01T01:30:00.000Z", PropertyType.DATE),};
         assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
-        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("+1975-08-01T01:31:00.000", PropertyType.DATE)}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("+1975-08-01T01:31:00.000Z", PropertyType.DATE)}), is(true));
         assertThat(satisfiesConstraints(prop, values), is(true));
     }
 



From dna-commits at lists.jboss.org  Wed Apr 29 08:58:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 29 Apr 2009 08:58:43 -0400
Subject: [dna-commits] DNA SVN: r862 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory
	and 10 other directories.
Message-ID: <E1Lz9N5-0004fj-RS@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-29 08:58:43 -0400 (Wed, 29 Apr 2009)
New Revision: 862

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.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/SessionCache.java
   trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
   trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositorySourceIntegrationTest.java
Log:
DNA-282 Federation connector improperly handles root-level node with one projection of a non-root node from a single source

Corrected the connector to properly handle root-level nodes in this particular case.  The problem was in how the UUIDs were being handled.  Fixed a couple of other minor issues as well.  

Found another issue while getting the Getting Started repository examples running (see DNA-387).

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-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -182,8 +182,8 @@
     @Override
     public Location with( UUID uuid ) {
         Property idProperty = idProperties.get(0); // fast
+        if (uuid == null) return Location.create(path);
         assert !DnaLexicon.UUID.equals(idProperty.getName());
-        if (uuid == null) return Location.create(path);
         Property newUuidProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
         return Location.create(path, idProperty, newUuidProperty);
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositorySource.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -71,21 +71,26 @@
      */
     public static final int DEFAULT_RETRY_LIMIT = 0;
 
+    /**
+     * The default name for the workspace used by this source, which is a blank string.
+     */
+    public static final String DEFAULT_WORKSPACE_NAME = "";
+
     protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true, false, true,
                                                                                                         true);
 
-    protected static final String ROOT_NODE_UUID = "rootNodeUuid";
-    protected static final String SOURCE_NAME = "sourceName";
-    protected static final String DEFAULT_WORKSPACE_NAME = "defaultWorkspaceName";
-    protected static final String DEFAULT_CACHE_POLICY = "defaultCachePolicy";
-    protected static final String JNDI_NAME = "jndiName";
-    protected static final String RETRY_LIMIT = "retryLimit";
+    protected static final String ROOT_NODE_UUID_ATTR = "rootNodeUuid";
+    protected static final String SOURCE_NAME_ATTR = "sourceName";
+    protected static final String DEFAULT_WORKSPACE_NAME_ATTR = "defaultWorkspaceName";
+    protected static final String DEFAULT_CACHE_POLICY_ATTR = "defaultCachePolicy";
+    protected static final String JNDI_NAME_ATTR = "jndiName";
+    protected static final String RETRY_LIMIT_ATTR = "retryLimit";
 
     @GuardedBy( "sourcesLock" )
     private String name;
     @GuardedBy( "this" )
     private String jndiName;
-    private String defaultWorkspaceName;
+    private String defaultWorkspaceName = DEFAULT_WORKSPACE_NAME;
     private UUID rootNodeUuid = UUID.randomUUID();
     private CachePolicy defaultCachePolicy;
     private final AtomicInteger retryLimit = new AtomicInteger(DEFAULT_RETRY_LIMIT);
@@ -164,7 +169,7 @@
      * @param defaultWorkspaceName the name of the workspace that should be used by default, or null if "" should be used
      */
     public void setDefaultWorkspaceName( String defaultWorkspaceName ) {
-        this.defaultWorkspaceName = defaultWorkspaceName;
+        this.defaultWorkspaceName = defaultWorkspaceName != null ? defaultWorkspaceName : DEFAULT_WORKSPACE_NAME;
     }
 
     /**
@@ -265,16 +270,16 @@
         Reference ref = new Reference(className, factoryClassName, null);
 
         if (getName() != null) {
-            ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+            ref.add(new StringRefAddr(SOURCE_NAME_ATTR, getName()));
         }
         if (getRootNodeUuid() != null) {
-            ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid().toString()));
+            ref.add(new StringRefAddr(ROOT_NODE_UUID_ATTR, getRootNodeUuid().toString()));
         }
         if (getJndiName() != null) {
-            ref.add(new StringRefAddr(JNDI_NAME, getJndiName()));
+            ref.add(new StringRefAddr(JNDI_NAME_ATTR, getJndiName()));
         }
         if (getDefaultWorkspaceName() != null) {
-            ref.add(new StringRefAddr(DEFAULT_WORKSPACE_NAME, getDefaultWorkspaceName()));
+            ref.add(new StringRefAddr(DEFAULT_WORKSPACE_NAME_ATTR, getDefaultWorkspaceName()));
         }
         if (getDefaultCachePolicy() != null) {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -282,13 +287,13 @@
             try {
                 ObjectOutputStream oos = new ObjectOutputStream(baos);
                 oos.writeObject(policy);
-                ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY, baos.toByteArray()));
+                ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY_ATTR, baos.toByteArray()));
             } catch (IOException e) {
                 I18n msg = GraphI18n.errorSerializingInMemoryCachePolicyInSource;
                 throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e);
             }
         }
-        ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+        ref.add(new StringRefAddr(RETRY_LIMIT_ATTR, Integer.toString(getRetryLimit())));
         return ref;
     }
 
@@ -321,12 +326,12 @@
                     }
                 }
             }
-            String sourceName = (String)values.get(SOURCE_NAME);
-            String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID);
-            String jndiName = (String)values.get(JNDI_NAME);
-            String defaultWorkspaceName = (String)values.get(DEFAULT_WORKSPACE_NAME);
-            Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY);
-            String retryLimit = (String)values.get(RETRY_LIMIT);
+            String sourceName = (String)values.get(SOURCE_NAME_ATTR);
+            String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID_ATTR);
+            String jndiName = (String)values.get(JNDI_NAME_ATTR);
+            String defaultWorkspaceName = (String)values.get(DEFAULT_WORKSPACE_NAME_ATTR);
+            Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY_ATTR);
+            String retryLimit = (String)values.get(RETRY_LIMIT_ATTR);
 
             // Create the source instance ...
             InMemoryRepositorySource source = new InMemoryRepositorySource();

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -320,6 +320,7 @@
         if (that instanceof JodaDateTime) {
             return this.instance.compareTo(((JodaDateTime)that).instance);
         }
+        if (that == null) return 1;
         long diff = this.toUtcTimeZone().getMilliseconds() - that.toUtcTimeZone().getMilliseconds();
         return (int)diff;
     }
@@ -406,7 +407,7 @@
      * @see org.jboss.dna.graph.property.DateTime#minus(long, java.util.concurrent.TimeUnit)
      */
     public org.jboss.dna.graph.property.DateTime minus( long timeAmount,
-                                                          TimeUnit unit ) {
+                                                        TimeUnit unit ) {
         CheckArg.isNotNull(unit, "unit");
         return new JodaDateTime(this.instance.minus(TimeUnit.MILLISECONDS.convert(timeAmount, unit)));
     }
@@ -489,7 +490,7 @@
      * @see org.jboss.dna.graph.property.DateTime#plus(long, java.util.concurrent.TimeUnit)
      */
     public org.jboss.dna.graph.property.DateTime plus( long timeAmount,
-                                                         TimeUnit unit ) {
+                                                       TimeUnit unit ) {
         CheckArg.isNotNull(unit, "unit");
         return new JodaDateTime(this.instance.plus(TimeUnit.MILLISECONDS.convert(timeAmount, unit)));
     }

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-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -348,7 +348,7 @@
             // No attribute defines the node name ...
             nodeName = nameFactory.create(uri, localName, decoder);
         } else {
-            typePropertyValue = nameFactory.create(uri, localName, decoder);
+            if (typePropertyValue == null) typePropertyValue = nameFactory.create(uri, localName, decoder);
         }
         if (typeAttribute != null) {
             // A attribute defines the node name. Set the type property, if required

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-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -305,7 +305,7 @@
                                        String workspaceName ) throws RepositoryException {
         // Ensure credentials are either null or provide a JAAS method
         Map<String, Object> sessionAttributes = new HashMap<String, Object>();
-        ExecutionContext execContext;
+        ExecutionContext execContext = null;
         if (credentials == null) {
             execContext = executionContext;
         } else {
@@ -371,7 +371,7 @@
                     // Per JCR 1.0 6.1.1, if the workspaceName is not recognized, a NoSuchWorkspaceException is thrown
                     throw new NoSuchWorkspaceException(JcrI18n.workspaceNameIsInvalid.text(sourceName, workspaceName));
                 }
-                workspaceName = graph.getCurrentWorkspace().getName();
+                graph.useWorkspace(workspaceName);
             } catch (InvalidWorkspaceException e) {
                 throw new NoSuchWorkspaceException(JcrI18n.workspaceNameIsInvalid.text(sourceName, workspaceName), e);
             } catch (RepositorySourceException e) {

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-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -147,6 +147,7 @@
         this.graph = Graph.create(this.repository.getRepositorySourceName(),
                                   this.repository.getConnectionFactory(),
                                   this.executionContext);
+        this.graph.useWorkspace(workspace.getName());
 
         this.cache = new SessionCache(this, workspace.getName(), this.executionContext, this.workspace.nodeTypeManager(),
                                       this.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-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -139,6 +139,8 @@
         Graph namespaceGraph = Graph.create(this.repository.getRepositorySourceName(),
                                             this.repository.getConnectionFactory(),
                                             context);
+        namespaceGraph.useWorkspace(workspaceName);
+
         Name uriProperty = DnaLexicon.NAMESPACE_URI;
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
         Path root = pathFactory.createRootPath();

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -2134,6 +2134,11 @@
 
         // Start with the primary type ...
         JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        if (primaryType == null) {
+            Path path = location.getPath();
+            String msg = JcrI18n.missingNodeTypeForExistingNode.text(primaryTypeName.getString(namespaces), path, workspaceName);
+            throw new RepositorySourceException(msg);
+        }
         if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
 
         // The process the mixin types ...

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -149,16 +149,15 @@
         config.importXmlFrom(location + "/configRepository.xml").into("/");
 
         // Now instantiate the Repository Service ...
-        repositoryService = new RepositoryService(sources, configSource.getName(), "default", context);
+        Path configRoot = context.getValueFactories().getPathFactory().create("/jcr:system");
+        repositoryService = new RepositoryService(sources, configSource.getName(), "default", configRoot, context);
         repositoryService.getAdministrator().start();
 
         // Now import the conten for two of the other in-memory repositories ...
         Graph cars = Graph.create("Cars", sources, context);
-        cars.createWorkspace().named("default");
         cars.importXmlFrom(location + "/cars.xml").into("/");
 
         Graph aircraft = Graph.create("Aircraft", sources, context);
-        aircraft.createWorkspace().named("default");
         aircraft.importXmlFrom(location + "/aircraft.xml").into("/");
     }
 
@@ -249,9 +248,9 @@
                 Session session = null;
                 if (loginContext != null) {
                     Credentials credentials = new JaasCredentials(loginContext);
-                    session = jcrRepository.login(credentials, sourceName);
+                    session = jcrRepository.login(credentials, "default");
                 } else {
-                    session = jcrRepository.login(sourceName);
+                    session = jcrRepository.login("default");
                 }
                 try {
                     // Make the path relative to the root by removing the leading slash(es) ...

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml	2009-04-29 12:58:43 UTC (rev 862)
@@ -24,39 +24,41 @@
   ~ 51 Franklin Street, Fifth Floor
   ~ Boston, MA  02110-1301  USA
   -->
-<dna:system xmlns:dna="http://www.jboss.org/dna/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0">
+<jcr:system xmlns:dna="http://www.jboss.org/dna/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0">
+    <dna:namespaces jcr:primaryType="dna:namespaces">
+    </dna:namespaces>
     <!-- Define the sources from which content is made available -->
-    <dna:sources>
-        <dna:source jcr:name="SourceA" dna:name="Cars" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" dna:retryLimit="3" />
-        <dna:source jcr:name="SourceB" dna:name="Aircraft" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" />
-        <dna:source jcr:name="SourceC" dna:name="Vehicles" dna:classname="org.jboss.dna.connector.federation.FederatedRepositorySource" 
+    <dna:sources jcr:primaryType="nt:unstructured">
+        <dna:source jcr:name="SourceA" jcr:primaryType="nt:unstructured" dna:name="Cars" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" dna:retryLimit="3" defaultWorkspaceName="default"/>
+        <dna:source jcr:name="SourceB" jcr:primaryType="nt:unstructured" dna:name="Aircraft" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" defaultWorkspaceName="default"/>
+        <dna:source jcr:name="SourceC" jcr:primaryType="nt:unstructured" dna:name="Vehicles" dna:classname="org.jboss.dna.connector.federation.FederatedRepositorySource" 
                     dna:repositoryName="Configuration Repository"
                     dna:configurationSourceName="Configuration" 
                     dna:configurationWorkspaceName="default"
-                    dna:configurationSourcePath="/dna:system/dna:federatedRepositories/Vehicles" 
+                    dna:configurationSourcePath="/jcr:system/dna:federatedRepositories/Vehicles" 
                     dna:repositoryConnectionFactoryJndiName="/dna/connectionFactory"
                     dna:executionContextFactoryJndiName="/dna/contextFactory"/>
-        <dna:source jcr:name="SourceD" dna:name="Cache" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" />
+        <dna:source jcr:name="SourceD" jcr:primaryType="nt:unstructured" dna:name="Cache" dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource" defaultWorkspaceName="default"/>
     </dna:sources>
-    <dna:federatedRepositories>
+    <dna:federatedRepositories jcr:primaryType="nt:unstructured">
         <!-- This section defines from where the content of the 'Vehicles' federated repository is obtained.
              The name of this node must match the 'name' of the source listed above. -->
-        <dna:federatedRepository jcr:name="Vehicles">
+        <dna:federatedRepository jcr:name="Vehicles" jcr:primaryType="nt:unstructured">
             <!-- Define the workspace(s) for this repository -->
-            <dna:workspaces>
-                <dna:workspace jcr:name="default">
+            <dna:workspaces jcr:primaryType="nt:unstructured">
+                <dna:workspace jcr:name="default" jcr:primaryType="nt:unstructured">
                     <!-- Define how the content in the 'Cache' source is to map to the federated cache -->
-                    <dna:cache dna:sourceName="Cache" dna:workspaceName="default" dna:projectionRules="/ => /" />
+                    <dna:cache jcr:primaryType="nt:unstructured" dna:sourceName="Cache" dna:workspaceName="default" dna:projectionRules="/ => /" />
                     <!-- Define how the content in the difference sources maps to the federated/unified repository.
                          This example puts the 'Cars' and 'Aircraft' content underneath '/vehicles', but the
                          'Configuration' content (which is defined by this file) will appear under '/'. -->
-                    <dna:projections>
-                        <dna:projection jcr:name="Cars" dna:workspaceName="default"  dna:projectionRules="/Vehicles => /" />
-                        <dna:projection jcr:name="Aircraft" dna:workspaceName="default"  dna:projectionRules="/Vehicles => /" />
-                        <dna:projection jcr:name="Configuration" dna:workspaceName="default"  dna:projectionRules="/ => /" />
+                    <dna:projections jcr:primaryType="nt:unstructured">
+                        <dna:projection jcr:primaryType="nt:unstructured" jcr:name="Cars" dna:workspaceName="default"  dna:projectionRules="/Vehicles => /" />
+                        <dna:projection jcr:primaryType="nt:unstructured" jcr:name="Aircraft" dna:workspaceName="default"  dna:projectionRules="/Vehicles => /" />
+                        <dna:projection jcr:primaryType="nt:unstructured" jcr:name="Configuration" dna:workspaceName="default"  dna:projectionRules="/ => /" />
                     </dna:projections>
                 </dna:workspace>
             </dna:workspaces>
         </dna:federatedRepository>
     </dna:federatedRepositories>
-</dna:system>
\ No newline at end of file
+</jcr:system>
\ No newline at end of file

Modified: trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -109,26 +109,27 @@
     public void shouldHaveContentFromConfigurationRepository() throws Throwable {
         client.startRepositories();
 
-        getNodeInfo("Configuration", "/dna:system");
+        getNodeInfo("Configuration", "/jcr:system");
         assertThat(children, hasItems("dna:sources", "dna:federatedRepositories"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        // assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size() >= 1, is(true));
 
-        getNodeInfo("Configuration", "/dna:system/dna:sources");
+        getNodeInfo("Configuration", "/jcr:system/dna:sources");
         assertThat(children, hasItems("SourceA", "SourceB", "SourceC", "SourceD"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        // assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size() >= 1, is(true));
 
-        getNodeInfo("Configuration", "/dna:system/dna:sources/SourceA");
+        getNodeInfo("Configuration", "/jcr:system/dna:sources/SourceA");
         assertThat(children.size(), is(0));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
+        // assertThat(properties.containsKey("dna:uuid"), is(true));
         assertProperty("dna:classname", org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource.class.getName());
         assertProperty("dna:name", "Cars");
         assertProperty("dna:retryLimit", "3");
-        assertThat(properties.size(), is(5));
+        assertProperty("defaultWorkspaceName", "default");
+        assertThat(properties.size() >= 5, is(true));
     }
 
     @Test
@@ -206,7 +207,7 @@
         client.startRepositories();
 
         getNodeInfo("Vehicles", "/");
-        assertThat(children, hasItems("Vehicles"));
+        assertThat(children, hasItems("Vehicles", "jcr:system"));
         assertThat(properties.containsKey("dna:uuid"), is(true));
         assertThat(properties.size(), is(1));
 
@@ -216,7 +217,7 @@
         assertThat(properties.size(), is(1));
 
         getNodeInfo("Vehicles", "/");
-        assertThat(children, hasItems("Vehicles"));
+        assertThat(children, hasItems("Vehicles", "jcr:system"));
         assertThat(properties.containsKey("dna:uuid"), is(true));
         assertThat(properties.size(), is(1));
 

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -25,12 +25,14 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 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;
 import java.util.concurrent.TimeUnit;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.i18n.I18n;
@@ -78,6 +80,8 @@
 @NotThreadSafe
 public class FederatingRequestProcessor extends RequestProcessor {
 
+    private static final Set<Name> HIDDEN_PROPERTIES = Collections.singleton(DnaLexicon.MERGE_PLAN);
+
     private final Map<String, FederatedWorkspace> workspaces;
     private final FederatedWorkspace defaultWorkspace;
     private final RepositoryConnectionFactory connectionFactory;
@@ -214,6 +218,7 @@
         ReadNodeRequest nodeInfo = getNode(request.at(), workspace);
         if (nodeInfo.hasError()) return;
         for (Property property : nodeInfo.getProperties()) {
+            if (HIDDEN_PROPERTIES.contains(property.getName())) continue;
             request.addProperty(property);
         }
         request.setActualLocationOfNode(nodeInfo.getActualLocationOfNode());
@@ -231,6 +236,7 @@
         ReadNodeRequest nodeInfo = getNode(request.at(), workspace);
         if (nodeInfo.hasError()) return;
         for (Property property : nodeInfo.getProperties()) {
+            if (HIDDEN_PROPERTIES.contains(property.getName())) continue;
             request.addProperty(property);
         }
         for (Location child : nodeInfo.getChildren()) {
@@ -357,8 +363,9 @@
      */
     protected ReadNodeRequest getNode( Location location,
                                        FederatedWorkspace workspace ) throws RepositorySourceException {
+        final ExecutionContext context = getExecutionContext();
+
         // Check the cache first ...
-        final ExecutionContext context = getExecutionContext();
         RepositoryConnection cacheConnection = getConnectionToCacheFor(workspace);
         ReadNodeRequest fromCache = new ReadNodeRequest(location, workspace.getCacheProjection().getWorkspaceName());
         cacheConnection.execute(context, fromCache);
@@ -410,7 +417,7 @@
             if (mergePlan != null) {
                 // We found the merge plan, so check whether it's still valid ...
                 final DateTime now = getCurrentTimeInUtc();
-                if (mergePlan.isExpired(now)) {
+                if (!mergePlan.isExpired(now)) {
                     // It is still valid, so check whether any contribution is from a non-existant projection ...
                     for (Contribution contribution : mergePlan) {
                         if (!workspace.contains(contribution.getSourceName(), contribution.getWorkspaceName())) {
@@ -424,10 +431,10 @@
                 // the valid contributions in the 'contributions' list; any expired contribution
                 // needs to be loaded by adding the name to the 'sourceNames'
                 if (mergePlan.getContributionCount() > 0) {
-                    sourceNames = new HashSet<String>(sourceNames);
+                    sourceNames = new HashSet<String>();
                     for (Contribution contribution : mergePlan) {
-                        if (!contribution.isExpired(now)) {
-                            sourceNames.remove(contribution.getSourceName());
+                        if (contribution.isExpired(now)) {
+                            sourceNames.add(contribution.getSourceName());
                             contributions.add(contribution);
                         }
                     }
@@ -437,7 +444,10 @@
 
         // Get the contributions from the sources given their names ...
         location = fromCache.getActualLocationOfNode();
-        if (location == null) location = fromCache.at(); // not yet in the cache
+        if (location == null) {
+            // Not yet in the cache ...
+            location = fromCache.at();
+        }
         loadContributionsFromSources(location, workspace, sourceNames, contributions); // sourceNames may be null or empty
         FederatedNode mergedNode = createFederatedNode(location, workspace, contributions, true);
         if (mergedNode == null) {
@@ -482,7 +492,7 @@
         // Create the node, and use the existing UUID if one is found in the cache ...
         ExecutionContext context = getExecutionContext();
         assert context != null;
-        FederatedNode mergedNode = new FederatedNode(location, federatedWorkspace.getName());
+        FederatedNode mergedNode = new FederatedNode(location.with((UUID)null), federatedWorkspace.getName());
 
         // Merge the results into a single set of results ...
         assert contributions.size() > 0;
@@ -719,7 +729,21 @@
         assert path != null;
         List<Request> requests = new ArrayList<Request>();
         Name childName = null;
-        if (!path.isRoot()) {
+
+        // If the merged node has a merge plan, then add it to the properties if it is not already there ...
+        Map<Name, Property> properties = mergedNode.getPropertiesByName();
+        MergePlan mergePlan = mergedNode.getMergePlan();
+        if (mergePlan != null) {
+            // Record the merge plan on the merged node ...
+            Property mergePlanProperty = getExecutionContext().getPropertyFactory().create(DnaLexicon.MERGE_PLAN,
+                                                                                           (Object)mergePlan);
+            properties.put(mergePlanProperty.getName(), mergePlanProperty);
+        }
+
+        if (path.isRoot()) {
+            // Set the properties ...
+            requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
+        } else {
             // This is not the root node, so we need to create the node ...
             final Location parentLocation = Location.create(path.getParent());
             childName = path.getLastSegment().getName();

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -303,6 +303,7 @@
     public boolean isExpired( DateTime utcTime ) {
         assert utcTime != null;
         assert utcTime.toUtcTimeZone().equals(utcTime); // check that it is passed UTC time
+        if (expirationTimeInUtc == null) return false;
         return !expirationTimeInUtc.isAfter(utcTime);
     }
 

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -196,7 +196,8 @@
     public boolean isExpired( DateTime utcTime ) {
         assert utcTime != null;
         assert utcTime.toUtcTimeZone().equals(utcTime); // check that it is passed UTC time
-        return utcTime.isAfter(getExpirationTimeInUtc());
+        DateTime expirationTimeInUtc = getExpirationTimeInUtc();
+        return expirationTimeInUtc == null ? false : utcTime.isAfter(expirationTimeInUtc);
     }
 
     /**

Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositorySourceIntegrationTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositorySourceIntegrationTest.java	2009-04-28 19:33:35 UTC (rev 861)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositorySourceIntegrationTest.java	2009-04-29 12:58:43 UTC (rev 862)
@@ -51,7 +51,6 @@
 import org.jboss.dna.graph.property.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
 import org.mockito.MockitoAnnotations;
@@ -227,7 +226,6 @@
         }
     }
 
-    @Ignore
     @Test
     public void shouldProvideReadAccessToContentFederatedFromOneSourceThatMatchesTheContentFromTheSource() throws Exception {
         // Set up the configuration to use a single source.
@@ -292,7 +290,6 @@
         assertChildren(source, "/b/c");
     }
 
-    @Ignore
     @Test
     public void shouldProvideReadAccessToContentFederatedFromMultipleSources() throws Exception {
         // Set up the configuration to use multiple sources.
@@ -312,12 +309,12 @@
         batch.create("/repos/RepoX/dna:workspaces/fedSpace/dna:projections/projection1")
              .with(FederatedLexicon.PROJECTION_RULES, "/ => /s1")
              .with(FederatedLexicon.SOURCE_NAME, "source 1")
-             .with(FederatedLexicon.WORKSPACE_NAME, "s1 workspace")
+             .with(FederatedLexicon.WORKSPACE_NAME, "s1Space")
              .and();
         batch.create("/repos/RepoX/dna:workspaces/fedSpace/dna:projections/projection2")
              .with(FederatedLexicon.PROJECTION_RULES, "/ => /s2")
              .with(FederatedLexicon.SOURCE_NAME, "source 2")
-             .with(FederatedLexicon.WORKSPACE_NAME, "s2 worskspace")
+             .with(FederatedLexicon.WORKSPACE_NAME, "s2 workspace")
              .and();
         batch.execute();
 



From dna-commits at lists.jboss.org  Wed Apr 29 10:14:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 29 Apr 2009 10:14:34 -0400
Subject: [dna-commits] DNA SVN: r863 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LzAYU-00059f-7t@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-29 10:14:34 -0400 (Wed, 29 Apr 2009)
New Revision: 863

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
Log:
DNA-386 AbstractJcrNode Javadoc Throws Clause Are Out Of Sync

Removed the "@throws UnsupportedOperationException always" line from the JavaDoc of several methods.  These lines were leftovers from when the methods were indeed not implemented.

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-04-29 12:58:43 UTC (rev 862)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-29 14:14:34 UTC (rev 863)
@@ -126,8 +126,8 @@
             throw new RepositoryException(msg);
         }
     }
-    
-    final NodeEditor editorFor(Graph.Batch operations) throws RepositoryException {
+
+    final NodeEditor editorFor( Graph.Batch operations ) throws RepositoryException {
         try {
             return cache.getEditorFor(nodeUuid, operations);
         } catch (ItemNotFoundException err) {
@@ -137,7 +137,7 @@
             String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
             throw new RepositoryException(msg);
         }
-    }    
+    }
 
     final JcrValue valueFrom( int propertyType,
                               Object value ) {
@@ -241,7 +241,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getPrimaryNodeType()
      */
     public JcrNodeType getPrimaryNodeType() throws RepositoryException {
@@ -252,11 +251,10 @@
     Name getPrimaryTypeName() throws RepositoryException {
         return nodeInfo().getPrimaryTypeName();
     }
-    
+
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
     public NodeType[] getMixinNodeTypes() throws RepositoryException {
@@ -564,7 +562,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getNodes(java.lang.String)
      */
     public NodeIterator getNodes( String namePattern ) throws RepositoryException {
@@ -944,17 +941,17 @@
                     NodeInfo grandparentInfo;
                     if (parentPath.size() > 1) {
                         // Per the TCK, if relPath references a property, then we have to throw a ConstraintViolationException
-                        // So, if we can't find the parent, try for the parent's parent and see if the last segment of the parent's
+                        // So, if we can't find the parent, try for the parent's parent and see if the last segment of the
+                        // parent's
                         // path contains a property ...
                         Path grandparentPath = parentPath.getParent();
                         assert grandparentPath != null;
-                        
+
                         grandparentInfo = cache.findNodeInfo(nodeUuid, grandparentPath); // throws PathNotFoundException
-                    }
-                    else {
+                    } else {
                         grandparentInfo = this.nodeInfo();
                     }
-                    
+
                     if (grandparentInfo.getProperty(parentPath.getLastSegment().getName()) != null) {
                         // Need to throw a ConstraintViolationException since the request was to add a child to
                         // a property ...
@@ -999,8 +996,7 @@
         throw new UnsupportedOperationException();
     }
 
-    protected final Property removeExistingValuedProperty( String name )
-        throws ConstraintViolationException, RepositoryException {
+    protected final Property removeExistingValuedProperty( String name ) throws ConstraintViolationException, RepositoryException {
         PropertyId id = new PropertyId(nodeUuid, nameFrom(name));
         AbstractJcrProperty property = cache.findJcrProperty(id);
         if (property != null) {
@@ -1133,7 +1129,9 @@
             return removeExistingValuedProperty(name);
         }
 
-        return cache.findJcrProperty(editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values), PropertyType.UNDEFINED));
+        return cache.findJcrProperty(editor().setProperty(nameFrom(name),
+                                                          valuesFrom(PropertyType.STRING, values),
+                                                          PropertyType.UNDEFINED));
     }
 
     /**
@@ -1306,8 +1304,7 @@
     public final boolean isModified() {
         try {
             return nodeInfo().isModified();
-        }
-        catch (RepositoryException re) {
+        } catch (RepositoryException re) {
             throw new IllegalStateException(re);
         }
     }
@@ -1320,8 +1317,7 @@
     public final boolean isNew() {
         try {
             return nodeInfo().isNew();
-        }
-        catch (RepositoryException re) {
+        } catch (RepositoryException re) {
             throw new IllegalStateException(re);
         }
     }



From dna-commits at lists.jboss.org  Wed Apr 29 10:23:58 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 29 Apr 2009 10:23:58 -0400
Subject: [dna-commits] DNA SVN: r864 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LzAha-0006zK-II@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-29 10:23:58 -0400 (Wed, 29 Apr 2009)
New Revision: 864

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
Log:
DNA-388 AbstractJcrNode.getReferences() is incorrectly implemented

The method now throws an UnsupportedOperationException, since the method is not implemented correctly.  Also commented out some unit tests that were testing these methods.

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-04-29 14:14:34 UTC (rev 863)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-29 14:23:58 UTC (rev 864)
@@ -420,6 +420,8 @@
      * @see javax.jcr.Node#getReferences()
      */
     public final PropertyIterator getReferences() throws RepositoryException {
+        if (true) throw new UnsupportedOperationException();
+        // This implementation is just wrong.
         // Iterate through the properties to see which ones have a REFERENCE type ...
         Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeUuid);
         Collection<AbstractJcrProperty> references = new LinkedList<AbstractJcrProperty>();

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-29 14:14:34 UTC (rev 863)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-04-29 14:23:58 UTC (rev 864)
@@ -25,12 +25,9 @@
 
 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.Mockito.mock;
 import static org.mockito.Mockito.stub;
-import java.util.Arrays;
-import java.util.List;
 import java.util.UUID;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
@@ -39,7 +36,6 @@
 import javax.jcr.NodeIterator;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
@@ -399,49 +395,49 @@
         node.getPrimaryItem();
     }
 
-    @Test
-    public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoProperties() throws Exception {
-        // Set up two properties (one containing references, the other not) ...
-        List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {});
-        stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
-        // Now call the method ...
-        PropertyIterator iter = node.getReferences();
-        assertThat(iter.getSize(), is(0L));
-        assertThat(iter.hasNext(), is(false));
-    }
+    // @Test
+    // public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoProperties() throws Exception {
+    // // Set up two properties (one containing references, the other not) ...
+    // List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {});
+    // stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+    // // Now call the method ...
+    // PropertyIterator iter = node.getReferences();
+    // assertThat(iter.getSize(), is(0L));
+    // assertThat(iter.hasNext(), is(false));
+    // }
+    //
+    // @Test
+    // public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoReferenceProperties() throws Exception {
+    // // Set up two properties (one containing references, the other not) ...
+    // AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
+    // AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
+    // List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
+    // stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+    // stub(propertyA.getType()).toReturn(PropertyType.LONG);
+    // stub(propertyB.getType()).toReturn(PropertyType.BOOLEAN);
+    // // Now call the method ...
+    // PropertyIterator iter = node.getReferences();
+    // assertThat(iter.getSize(), is(0L));
+    // assertThat(iter.hasNext(), is(false));
+    // }
+    //
+    // @Test
+    // public void shouldReturnIteratorFromGetReferencesWhenThereIsAtLeastOneReferenceProperty() throws Exception {
+    // // Set up two properties (one containing references, the other not) ...
+    // AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
+    // AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
+    // List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
+    // stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+    // stub(propertyA.getType()).toReturn(PropertyType.LONG);
+    // stub(propertyB.getType()).toReturn(PropertyType.REFERENCE);
+    // // Now call the method ...
+    // PropertyIterator iter = node.getReferences();
+    // assertThat(iter.getSize(), is(1L));
+    // assertThat(iter.next(), is(sameInstance((Object)propertyB)));
+    // assertThat(iter.hasNext(), is(false));
+    // }
 
     @Test
-    public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoReferenceProperties() throws Exception {
-        // Set up two properties (one containing references, the other not) ...
-        AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
-        AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
-        List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
-        stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
-        stub(propertyA.getType()).toReturn(PropertyType.LONG);
-        stub(propertyB.getType()).toReturn(PropertyType.BOOLEAN);
-        // Now call the method ...
-        PropertyIterator iter = node.getReferences();
-        assertThat(iter.getSize(), is(0L));
-        assertThat(iter.hasNext(), is(false));
-    }
-
-    @Test
-    public void shouldReturnIteratorFromGetReferencesWhenThereIsAtLeastOneReferenceProperty() throws Exception {
-        // Set up two properties (one containing references, the other not) ...
-        AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
-        AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
-        List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
-        stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
-        stub(propertyA.getType()).toReturn(PropertyType.LONG);
-        stub(propertyB.getType()).toReturn(PropertyType.REFERENCE);
-        // Now call the method ...
-        PropertyIterator iter = node.getReferences();
-        assertThat(iter.getSize(), is(1L));
-        assertThat(iter.next(), is(sameInstance((Object)propertyB)));
-        assertThat(iter.hasNext(), is(false));
-    }
-
-    @Test
     public void shouldProvideSession() throws Exception {
         assertThat((JcrSession)node.getSession(), is(session));
     }



From dna-commits at lists.jboss.org  Wed Apr 29 10:31:50 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 29 Apr 2009 10:31:50 -0400
Subject: [dna-commits] DNA SVN: r865 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LzApC-0001e9-3V@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-29 10:31:49 -0400 (Wed, 29 Apr 2009)
New Revision: 865

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
Log:
DNA-376 Add request to change the order of a child node (or nodes) relative to the other children

Added a partial implementation of the AbstractJcrNode.orderBefore(...) method, though this partial implementation is never reached because the method still throws an UnsupportedOperationException.  This partial implementation should be completed when the Graph API has support for reordering children.

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-04-29 14:23:58 UTC (rev 864)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-04-29 14:31:49 UTC (rev 865)
@@ -69,6 +69,7 @@
 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.ValueFactories;
 import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.ChildNode;
@@ -1437,8 +1438,44 @@
      * @see javax.jcr.Node#orderBefore(java.lang.String, java.lang.String)
      */
     public final void orderBefore( String srcChildRelPath,
-                                   String destChildRelPath ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+                                   String destChildRelPath ) throws UnsupportedRepositoryOperationException, RepositoryException {
+        if (true) throw new UnsupportedRepositoryOperationException();
+
+        // This implementation is correct, except for not calling the SessionCache or graph layer to do the re-order
+        if (!getPrimaryNodeType().hasOrderableChildNodes()) {
+            throw new UnsupportedRepositoryOperationException();
+        }
+
+        PathFactory pathFactory = this.cache.pathFactory();
+
+        Path srcPath = pathFactory.create(srcChildRelPath);
+        ChildNode source;
+
+        if (srcPath.isAbsolute() || srcPath.size() != 1) {
+            throw new ItemNotFoundException();
+        }
+        // getLastSegment should return the only segment, since we verified that size() == 1
+        source = nodeInfo().getChildren().getChild(srcPath.getLastSegment());
+        if (source == null) {
+            throw new ItemNotFoundException();
+        }
+
+        Path destPath = null;
+        ChildNode destination = null;
+
+        if (destChildRelPath != null) {
+            destPath = pathFactory.create(destChildRelPath);
+            if (destPath.isAbsolute() || destPath.size() != 1) {
+                throw new ItemNotFoundException();
+            }
+
+            // getLastSegment should return the only segment, since we verified that size() == 1
+            destination = nodeInfo().getChildren().getChild(destPath.getLastSegment());
+            if (destination == null) {
+                throw new ItemNotFoundException();
+            }
+        }
+
     }
 
     protected static List<Object> createPatternsFor( String namePattern ) throws RepositoryException {



From dna-commits at lists.jboss.org  Thu Apr 30 00:32:27 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 30 Apr 2009 00:32:27 -0400
Subject: [dna-commits] DNA SVN: r866 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory
	and 14 other directories.
Message-ID: <E1LzNwh-0004SU-0k@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-30 00:32:26 -0400 (Thu, 30 Apr 2009)
New Revision: 866

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.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/MoveBranchRequest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
   trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientUsingJcrTest.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedWorkspace.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederationI18n.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategy.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java
   trunk/extensions/dna-connector-federation/src/main/resources/org/jboss/dna/connector/federation/FederationI18n.properties
   trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatingRequestProcessorTest.java
   trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategyTest.java
   trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategyTest.java
   trunk/extensions/dna-connector-federation/src/test/resources/log4j.properties
   trunk/pom.xml
Log:
DNA-387 Federation connector does not support updates, which are required when using with the JCR implementation

Added support for writes in limited scenarios (when the nodes being changed are within a single projection), and addressed a couple of issues related to the handling of UUIDs in the federation connector.  Added more unit tests, verifying the behavior is correct.  Also corrected a few errors in CopyBranchRequest, MoveBranchRequest, and CreateNodeRequest.  Finally, the repository examples (from the Getting Started document) were corrected to use our JCR implementation.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -263,7 +263,13 @@
                                 InMemoryNode node ) {
             assert context != null;
             assert node != null;
-            assert getRoot().equals(node) != true;
+            if (getRoot().equals(node)) {
+                nodesByUuid.clear();
+                // Create the root node ...
+                InMemoryNode root = new InMemoryNode(rootNodeUuid);
+                nodesByUuid.put(root.getUuid(), root);
+                return;
+            }
             InMemoryNode parent = node.getParent();
             assert parent != null;
             parent.getChildren().remove(node);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -35,6 +35,7 @@
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
@@ -150,7 +151,7 @@
         }
         UUID uuid = null;
         for (Property property : request.properties()) {
-            if (property.getName().equals(DnaLexicon.UUID)) {
+            if (property.getName().equals(DnaLexicon.UUID) || property.getName().equals(JcrLexicon.UUID)) {
                 uuid = getExecutionContext().getValueFactories().getUuidFactory().create(property.getValues().next());
                 break;
             }

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-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -223,6 +223,7 @@
         if (into.hasPath() && !intoLocation.getPath().getParent().equals(into.getPath())) {
             throw new IllegalArgumentException(GraphI18n.actualLocationIsNotChildOfInputLocation.text(intoLocation, into));
         }
+        this.actualFromLocation = fromLocation;
         this.actualIntoLocation = intoLocation;
     }
 

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-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -168,12 +168,6 @@
         for (Property property : properties) {
             if (property != null) props.add(property);
         }
-        // Add in the location properties ...
-        if (under.hasIdProperties()) {
-            for (Property property : under.getIdProperties()) {
-                if (property != null) props.add(property);
-            }
-        }
         this.properties = Collections.unmodifiableList(props);
     }
 
@@ -207,12 +201,6 @@
             Property property = properties.next();
             if (property != null) props.add(property);
         }
-        // Add in the location properties ...
-        if (under.hasIdProperties()) {
-            for (Property property : under.getIdProperties()) {
-                if (property != null) props.add(property);
-            }
-        }
         this.properties = Collections.unmodifiableList(props);
     }
 

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-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -237,6 +237,7 @@
         if (!actualNewName.equals(expectedNewName)) {
             throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
         }
+        this.actualOldLocation = oldLocation;
         this.actualNewLocation = newLocation;
     }
 
@@ -265,9 +266,8 @@
      */
     public boolean changes( String workspace,
                             Path path ) {
-        return this.workspaceName.equals(workspace) 
-            && (into.hasPath() && into.getPath().isAtOrBelow(path)
-                || from.hasPath() && from.getPath().isAtOrBelow(path)); 
+        return this.workspaceName.equals(workspace)
+               && (into.hasPath() && into.getPath().isAtOrBelow(path) || from.hasPath() && from.getPath().isAtOrBelow(path));
     }
 
     /**

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-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryWorkspaceTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -94,9 +94,11 @@
         assertThat(workspace.getRoot().getUuid(), is(rootUuid));
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowRootToBeRemoved() {
+    @Test
+    public void shouldAllowRootToBeRemoved() {
         workspace.removeNode(context, workspace.getRoot());
+        assertThat(workspace.getRoot().getChildren().size(), is(0));
+        assertThat(workspace.getRoot().getProperties().size(), is(0));
     }
 
     @Test( expected = AssertionError.class )

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-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -141,10 +141,15 @@
                                             context);
         namespaceGraph.useWorkspace(workspaceName);
 
-        Name uriProperty = DnaLexicon.NAMESPACE_URI;
+        // Make sure the "/jcr:system" node exists ...
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
         Path root = pathFactory.createRootPath();
-        Path namespacesPath = context.getValueFactories().getPathFactory().create(root, JcrLexicon.SYSTEM, DnaLexicon.NAMESPACES);
+        Path systemPath = pathFactory.create(root, JcrLexicon.SYSTEM);
+        Property systemPrimaryType = context.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.SYSTEM);
+        namespaceGraph.createIfMissing(systemPath, systemPrimaryType);
+
+        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,
@@ -168,9 +173,7 @@
         this.queryManager = new JcrQueryManager(this.session);
 
         if (Boolean.valueOf(repository.getOptions().get(Options.PROJECT_NODE_TYPES))) {
-            Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(root,
-                                                                                         JcrLexicon.SYSTEM,
-                                                                                         JcrLexicon.NODE_TYPES);
+            Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(systemPath, JcrLexicon.NODE_TYPES);
             repoTypeManager.projectOnto(this.graph, parentOfTypeNodes);
         }
     }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -115,10 +115,6 @@
         // Set up the initial content ...
         graph = Graph.create(source, context);
 
-        // Make sure the path to the namespaces exists ...
-        graph.create("/jcr:system"); // .and().create("/jcr:system/dna:namespaces");
-        graph.set("jcr:primaryType").on("/jcr:system").to(DnaLexicon.SYSTEM);
-
         // Stub out the connection factory ...
         connectionFactory = new RepositoryConnectionFactory() {
             /**
@@ -318,7 +314,9 @@
     public void shouldAllowAdditionIfResidualChildNodeDoesNotConflict() throws Exception {
         graph.create("/a").and().create("/a/" + CHILD_NODE_B);
         graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a").to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
-        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry)).on("/a/" + CHILD_NODE_B).to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
+        graph.set(JcrLexicon.PRIMARY_TYPE.getString(registry))
+             .on("/a/" + CHILD_NODE_B)
+             .to(JcrNtLexicon.UNSTRUCTURED.getString(registry));
 
         Node rootNode = session.getRootNode();
         Node nodeA = rootNode.getNode("a");
@@ -672,4 +670,4 @@
 
     }
 
-}
\ No newline at end of file
+}

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -138,6 +138,7 @@
         // Load into the source manager the repository source for the configuration repository ...
         InMemoryRepositorySource configSource = new InMemoryRepositorySource();
         configSource.setName("Configuration");
+        configSource.setDefaultWorkspaceName("default");
         sources.addSource(configSource);
 
         // For this example, we're using a couple of in-memory repositories (including one for the configuration repository).
@@ -145,7 +146,7 @@
         // populate these repositories here by importing from files. First do the configuration repository ...
         String location = this.userInterface.getLocationOfRepositoryFiles();
         Graph config = Graph.create("Configuration", sources, context);
-        config.createWorkspace().named("default");
+        config.useWorkspace("default");
         config.importXmlFrom(location + "/configRepository.xml").into("/");
 
         // Now instantiate the Repository Service ...
@@ -295,6 +296,8 @@
                             children.add(name);
                         }
                     }
+                } catch (javax.jcr.ItemNotFoundException e) {
+                    return false;
                 } catch (javax.jcr.PathNotFoundException e) {
                     return false;
                 } finally {

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml	2009-04-30 04:32:26 UTC (rev 866)
@@ -24,31 +24,31 @@
   ~ 51 Franklin Street, Fifth Floor
   ~ Boston, MA  02110-1301  USA
   -->
-<Aircraft xmlns:jcr="http://www.jcp.org/jcr/1.0">
+<Aircraft xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
     <Business>
-        <aircraft jcr:name="Gulfstream V" maker="Gulfstream" model="G-V" introduced="1995" range="5800nm" cruiseSpeed="488kt" crew="2" emptyWeight="46200lb" url="http://en.wikipedia.org/wiki/Gulfstream_V"/>
-        <aircraft jcr:name="Learjet 45" maker="Learjet" model="LJ45" introduced="1995" numberBuilt="264+" crew="2" emptyWeight="13695lb" range="2120nm" cruiseSpeed="457kt" url="http://en.wikipedia.org/wiki/Learjet_45"/>
+        <nt:unstructured jcr:name="Gulfstream V" maker="Gulfstream" model="G-V" introduced="1995" range="5800nm" cruiseSpeed="488kt" crew="2" emptyWeight="46200lb" url="http://en.wikipedia.org/wiki/Gulfstream_V"/>
+        <nt:unstructured jcr:name="Learjet 45" maker="Learjet" model="LJ45" introduced="1995" numberBuilt="264+" crew="2" emptyWeight="13695lb" range="2120nm" cruiseSpeed="457kt" url="http://en.wikipedia.org/wiki/Learjet_45"/>
     </Business>
     <Commercial>
-        <aircraft jcr:name="Boeing 777" maker="Boeing" model="777-200LR" introduced="1995" numberBuilt="731+" maxRange="7500nm" emptyWeight="326000lb" cruiseSpeed="560mph" url="http://en.wikipedia.org/wiki/Boeing_777"/>
-        <aircraft jcr:name="Boeing 767" maker="Boeing" model="767-200" introduced="1982" numberBuilt="966+" maxRange="3950nm" emptyWeight="176650lb" cruiseSpeed="530mph"  url="http://en.wikipedia.org/wiki/Boeing_767"/>
-        <aircraft jcr:name="Boeing 787" maker="Boeing" model="787-3" introduced="2009" range="3050nm" emptyWeight="223000lb" cruiseSpeed="561mph" url="http://en.wikipedia.org/wiki/Boeing_787"/>
-        <aircraft jcr:name="Boeing 757" maker="Boeing" model="757-200" introduced="1983" numberBuilt="1050" range="3900nm" maxWeight="255000lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_757"/>
-        <aircraft jcr:name="Airbus A380" maker="Airbus" model="A380-800" introduced="2007" numberBuilt="18" range="8200nm" maxWeight="1235000lb" cruiseSpeed="647mph" url="http://en.wikipedia.org/wiki/Airbus_a380"/>
-        <aircraft jcr:name="Airbus A340" maker="Airbus" model="A340-200" introduced="1993" numberBuilt="354" range="8000nm" maxWeight="606300lb" cruiseSpeed="557mph" url="http://en.wikipedia.org/wiki/Airbus_A-340"/>
-        <aircraft jcr:name="Airbus A310" maker="Airbus" model="A310-200" introduced="1983" numberBuilt="255" cruiseSpeed="850km/h" emptyWeight="176312lb" range="3670nm" url="http://en.wikipedia.org/wiki/Airbus_A-310"/>
-        <aircraft jcr:name="Embraer RJ-175" maker="Embraer" model="ERJ170-200" introduced="2004" range="3334km" cruiseSpeed="481kt" emptyWeight="21810kg" url="http://en.wikipedia.org/wiki/EMBRAER_170"/>
+        <nt:unstructured jcr:name="Boeing 777" maker="Boeing" model="777-200LR" introduced="1995" numberBuilt="731+" maxRange="7500nm" emptyWeight="326000lb" cruiseSpeed="560mph" url="http://en.wikipedia.org/wiki/Boeing_777"/>
+        <nt:unstructured jcr:name="Boeing 767" maker="Boeing" model="767-200" introduced="1982" numberBuilt="966+" maxRange="3950nm" emptyWeight="176650lb" cruiseSpeed="530mph"  url="http://en.wikipedia.org/wiki/Boeing_767"/>
+        <nt:unstructured jcr:name="Boeing 787" maker="Boeing" model="787-3" introduced="2009" range="3050nm" emptyWeight="223000lb" cruiseSpeed="561mph" url="http://en.wikipedia.org/wiki/Boeing_787"/>
+        <nt:unstructured jcr:name="Boeing 757" maker="Boeing" model="757-200" introduced="1983" numberBuilt="1050" range="3900nm" maxWeight="255000lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_757"/>
+        <nt:unstructured jcr:name="Airbus A380" maker="Airbus" model="A380-800" introduced="2007" numberBuilt="18" range="8200nm" maxWeight="1235000lb" cruiseSpeed="647mph" url="http://en.wikipedia.org/wiki/Airbus_a380"/>
+        <nt:unstructured jcr:name="Airbus A340" maker="Airbus" model="A340-200" introduced="1993" numberBuilt="354" range="8000nm" maxWeight="606300lb" cruiseSpeed="557mph" url="http://en.wikipedia.org/wiki/Airbus_A-340"/>
+        <nt:unstructured jcr:name="Airbus A310" maker="Airbus" model="A310-200" introduced="1983" numberBuilt="255" cruiseSpeed="850km/h" emptyWeight="176312lb" range="3670nm" url="http://en.wikipedia.org/wiki/Airbus_A-310"/>
+        <nt:unstructured jcr:name="Embraer RJ-175" maker="Embraer" model="ERJ170-200" introduced="2004" range="3334km" cruiseSpeed="481kt" emptyWeight="21810kg" url="http://en.wikipedia.org/wiki/EMBRAER_170"/>
     </Commercial>
     <Vintage>
-        <aircraft jcr:name="Fokker Trimotor" maker="Fokker" model="F.VII" introduced="1925" cruiseSpeed="170km/h" emptyWeight="3050kg" crew="2" url="http://en.wikipedia.org/wiki/Fokker_trimotor"/>
-        <aircraft jcr:name="P-38 Lightning" maker="Lockheed" model="P-38" designedBy="Kelly Johnson" introduced="1941" numberBuilt="10037" rateOfClimb="4750ft/min" range="1300mi" emptyWeight="12780lb" crew="1" url="http://en.wikipedia.org/wiki/P-38_Lightning"/>
-        <aircraft jcr:name="A6M Zero" maker="Mitsubishi" model="A6M" designedBy="Jiro Horikoshi" introduced="1940" numberBuilt="11000" crew="1" emptyWeight="3704lb" serviceCeiling="33000ft" maxSpeed="331mph" range="1929mi" rateOfClimb="3100ft/min" url="http://en.wikipedia.org/wiki/A6M_Zero"/>
-        <aircraft jcr:name="Bf 109" maker="Messerschmitt" model="Bf 109" introduced="1937" url="http://en.wikipedia.org/wiki/BF_109"/>
-        <aircraft jcr:name="Wright Flyer" maker="Wright Brothers" introduced="1903" range="852ft" maxSpeed="30mph" emptyWeight="605lb" crew="1"/>
+        <nt:unstructured jcr:name="Fokker Trimotor" maker="Fokker" model="F.VII" introduced="1925" cruiseSpeed="170km/h" emptyWeight="3050kg" crew="2" url="http://en.wikipedia.org/wiki/Fokker_trimotor"/>
+        <nt:unstructured jcr:name="P-38 Lightning" maker="Lockheed" model="P-38" designedBy="Kelly Johnson" introduced="1941" numberBuilt="10037" rateOfClimb="4750ft/min" range="1300mi" emptyWeight="12780lb" crew="1" url="http://en.wikipedia.org/wiki/P-38_Lightning"/>
+        <nt:unstructured jcr:name="A6M Zero" maker="Mitsubishi" model="A6M" designedBy="Jiro Horikoshi" introduced="1940" numberBuilt="11000" crew="1" emptyWeight="3704lb" serviceCeiling="33000ft" maxSpeed="331mph" range="1929mi" rateOfClimb="3100ft/min" url="http://en.wikipedia.org/wiki/A6M_Zero"/>
+        <nt:unstructured jcr:name="Bf 109" maker="Messerschmitt" model="Bf 109" introduced="1937" url="http://en.wikipedia.org/wiki/BF_109"/>
+        <nt:unstructured jcr:name="Wright Flyer" maker="Wright Brothers" introduced="1903" range="852ft" maxSpeed="30mph" emptyWeight="605lb" crew="1"/>
     </Vintage>
     <Homebuilt>
-        <aircraft jcr:name="Long-EZ" maker="Rutan Aircraft Factory" model="61" emptyWeight="760lb" fuelCapacity="200L" maxSpeed="185kt" since="1976" range="1200nm" url="http://en.wikipedia.org/wiki/Rutan_Long-EZ"/>
-        <aircraft jcr:name="Cirrus VK-30" maker="Cirrus Design" model="VK-30" emptyWeight="2400lb" maxLoad="1200lb" maxSpeed="250mph" rateOfClimb="1500ft/min" range="1300mi" url="http://en.wikipedia.org/wiki/Cirrus_VK-30"/>
-        <aircraft jcr:name="Van's RV-4" maker="Van's Aircraft" model="RV-4" introduced="1980" emptyWeight="905lb" maxLoad="500lb" maxSpeed="200mph" rateOfClimb="2450ft/min" range="725mi" url="http://en.wikipedia.org/wiki/Van%27s_Aircraft_RV-4"/>
+        <nt:unstructured jcr:name="Long-EZ" maker="Rutan Aircraft Factory" model="61" emptyWeight="760lb" fuelCapacity="200L" maxSpeed="185kt" since="1976" range="1200nm" url="http://en.wikipedia.org/wiki/Rutan_Long-EZ"/>
+        <nt:unstructured jcr:name="Cirrus VK-30" maker="Cirrus Design" model="VK-30" emptyWeight="2400lb" maxLoad="1200lb" maxSpeed="250mph" rateOfClimb="1500ft/min" range="1300mi" url="http://en.wikipedia.org/wiki/Cirrus_VK-30"/>
+        <nt:unstructured jcr:name="Van's RV-4" maker="Van's Aircraft" model="RV-4" introduced="1980" emptyWeight="905lb" maxLoad="500lb" maxSpeed="200mph" rateOfClimb="2450ft/min" range="725mi" url="http://en.wikipedia.org/wiki/Van%27s_Aircraft_RV-4"/>
     </Homebuilt>
 </Aircraft>
\ No newline at end of file

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml	2009-04-30 04:32:26 UTC (rev 866)
@@ -24,25 +24,25 @@
   ~ 51 Franklin Street, Fifth Floor
   ~ Boston, MA  02110-1301  USA
   -->
-<Cars xmlns:jcr="http://www.jcp.org/jcr/1.0">
+<Cars xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
     <Hybrid>
-        <car jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
-        <car jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
-        <car jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
+        <nt:unstructured jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
+        <nt:unstructured jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
+        <nt:unstructured jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
     </Hybrid>
     <Sports>
-        <car jcr:name="Aston Martin DB9" maker="Aston Martin" model="DB9" year="2008" msrp="$171,600" userRating="5" mpgCity="12" mpgHighway="19" lengthInInches="185.5" wheelbaseInInches="108.0" engine="5,935 cc 5.9 liters V 12"/>
-        <car jcr:name="Infiniti G37" maker="Infiniti" model="G37" year="2008" msrp="$34,900" userRating="3.5" valueRating="4" mpgCity="18" mpgHighway="24" />
+        <nt:unstructured jcr:name="Aston Martin DB9" maker="Aston Martin" model="DB9" year="2008" msrp="$171,600" userRating="5" mpgCity="12" mpgHighway="19" lengthInInches="185.5" wheelbaseInInches="108.0" engine="5,935 cc 5.9 liters V 12"/>
+        <nt:unstructured jcr:name="Infiniti G37" maker="Infiniti" model="G37" year="2008" msrp="$34,900" userRating="3.5" valueRating="4" mpgCity="18" mpgHighway="24" />
     </Sports>
     <Luxury>
-        <car jcr:name="Cadillac DTS" maker="Cadillac" model="DTS" year="2008" engine="3.6-liter V6" userRating="0"/>
-        <car jcr:name="Bentley Continental" maker="Bentley" model="Continental" year="2008" msrp="$170,990" mpgCity="10" mpgHighway="17" />
-        <car jcr:name="Lexus IS350" maker="Lexus" model="IS350" year="2008" msrp="$36,305" mpgCity="18" mpgHighway="25" userRating="4" valueRating="5" />
+        <nt:unstructured jcr:name="Cadillac DTS" maker="Cadillac" model="DTS" year="2008" engine="3.6-liter V6" userRating="0"/>
+        <nt:unstructured jcr:name="Bentley Continental" maker="Bentley" model="Continental" year="2008" msrp="$170,990" mpgCity="10" mpgHighway="17" />
+        <nt:unstructured jcr:name="Lexus IS350" maker="Lexus" model="IS350" year="2008" msrp="$36,305" mpgCity="18" mpgHighway="25" userRating="4" valueRating="5" />
     </Luxury>
     <Utility>
-        <car jcr:name="Land Rover LR2" maker="Land Rover" model="LR2" year="2008" msrp="$33,985" userRating="4.5" valueRating="5" mpgCity="16" mpgHighway="23" />
-        <car jcr:name="Land Rover LR3" maker="Land Rover" model="LR3" year="2008" msrp="$48,525" userRating="5" valueRating="2" mpgCity="12" mpgHighway="17" />
-        <car jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
-        <car jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
+        <nt:unstructured jcr:name="Land Rover LR2" maker="Land Rover" model="LR2" year="2008" msrp="$33,985" userRating="4.5" valueRating="5" mpgCity="16" mpgHighway="23" />
+        <nt:unstructured jcr:name="Land Rover LR3" maker="Land Rover" model="LR3" year="2008" msrp="$48,525" userRating="5" valueRating="2" mpgCity="12" mpgHighway="17" />
+        <nt:unstructured jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
+        <nt:unstructured jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
     </Utility>
 </Cars>
\ No newline at end of file

Modified: trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -139,19 +139,32 @@
         getNodeInfo("Cars", "/Cars");
         assertThat(children, hasItems("Hybrid", "Sports", "Luxury", "Utility"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        switch (getApi()) {
+            case DNA:
+                assertThat(properties.containsKey("dna:uuid"), is(true));
+                assertThat(properties.size(), is(2));
+                break;
+            case JCR:
+                assertThat(properties.size(), is(1));
+                break;
+        }
 
         getNodeInfo("Cars", "/Cars/Hybrid");
         assertThat(children, hasItems("Toyota Prius", "Toyota Highlander", "Nissan Altima"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        switch (getApi()) {
+            case DNA:
+                assertThat(properties.containsKey("dna:uuid"), is(true));
+                assertThat(properties.size(), is(2));
+                break;
+            case JCR:
+                assertThat(properties.size(), is(1));
+                break;
+        }
 
         getNodeInfo("Cars", "/Cars/Sports/Aston Martin DB9");
         assertThat(children.size(), is(0));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
         assertProperty("maker", "Aston Martin");
         assertProperty("maker", "Aston Martin");
         assertProperty("model", "DB9");
@@ -163,7 +176,15 @@
         assertProperty("lengthInInches", "185.5");
         assertProperty("wheelbaseInInches", "108.0");
         assertProperty("engine", "5,935 cc 5.9 liters V 12");
-        assertThat(properties.size(), is(12));
+        switch (getApi()) {
+            case DNA:
+                assertThat(properties.containsKey("dna:uuid"), is(true));
+                assertThat(properties.size(), is(12));
+                break;
+            case JCR:
+                assertThat(properties.size(), is(11));
+                break;
+        }
     }
 
     @Test
@@ -173,8 +194,7 @@
         getNodeInfo("Aircraft", "/Aircraft");
         assertThat(children, hasItems("Business", "Commercial", "Vintage", "Homebuilt"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        // assertThat(properties.containsKey("dna:uuid"), is(true)); // not referenceable in JCR
 
         getNodeInfo("Aircraft", "/Aircraft/Commercial");
         assertThat(children, hasItems("Boeing 777",
@@ -186,20 +206,18 @@
                                       "Airbus A310",
                                       "Embraer RJ-175"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
+        // assertThat(properties.containsKey("dna:uuid"), is(true)); // not referenceable in JCR
 
         getNodeInfo("Aircraft", "/Aircraft/Vintage/Wright Flyer");
         assertThat(children.size(), is(0));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
+        // assertThat(properties.containsKey("dna:uuid"), is(true));
         assertProperty("maker", "Wright Brothers");
         assertProperty("introduced", "1903");
         assertProperty("range", "852ft");
         assertProperty("maxSpeed", "30mph");
         assertProperty("emptyWeight", "605lb");
         assertProperty("crew", "1");
-        assertThat(properties.size(), is(8));
     }
 
     @Test
@@ -208,29 +226,20 @@
 
         getNodeInfo("Vehicles", "/");
         assertThat(children, hasItems("Vehicles", "jcr:system"));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(1));
 
         getNodeInfo("Vehicles", "/Vehicles");
         assertThat(children, hasItems("Cars", "Aircraft"));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(1));
 
         getNodeInfo("Vehicles", "/");
         assertThat(children, hasItems("Vehicles", "jcr:system"));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(1));
 
         getNodeInfo("Vehicles", "/Vehicles/Cars/Hybrid");
         assertThat(children, hasItems("Toyota Prius", "Toyota Highlander", "Nissan Altima"));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
-        assertThat(properties.size(), is(2));
 
         getNodeInfo("Vehicles", "/Vehicles/Cars/Sports/Aston Martin DB9");
         assertThat(children.size(), is(0));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
         assertProperty("maker", "Aston Martin");
         assertProperty("maker", "Aston Martin");
         assertProperty("model", "DB9");
@@ -242,19 +251,16 @@
         assertProperty("lengthInInches", "185.5");
         assertProperty("wheelbaseInInches", "108.0");
         assertProperty("engine", "5,935 cc 5.9 liters V 12");
-        assertThat(properties.size(), is(12));
 
         getNodeInfo("Vehicles", "/Vehicles/Aircraft/Vintage/Wright Flyer");
         assertThat(children.size(), is(0));
         assertThat(properties.containsKey("jcr:primaryType"), is(true));
-        assertThat(properties.containsKey("dna:uuid"), is(true));
         assertProperty("maker", "Wright Brothers");
         assertProperty("introduced", "1903");
         assertProperty("range", "852ft");
         assertProperty("maxSpeed", "30mph");
         assertProperty("emptyWeight", "605lb");
         assertProperty("crew", "1");
-        assertThat(properties.size(), is(8));
     }
 
     @Test

Modified: trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientUsingJcrTest.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientUsingJcrTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientUsingJcrTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -39,4 +39,14 @@
     protected Api getApi() {
         return RepositoryClient.Api.JCR;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.example.dna.repository.RepositoryClientTest#shouldHaveContentFromVehiclesRepository()
+     */
+    @Override
+    public void shouldHaveContentFromVehiclesRepository() throws Throwable {
+        super.shouldHaveContentFromVehiclesRepository();
+    }
 }

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedWorkspace.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedWorkspace.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedWorkspace.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -184,6 +184,16 @@
     }
 
     /**
+     * Return the unmodifiable list of projections for the source name.
+     * 
+     * @param sourceName the name of the source
+     * @return the list of projections for this source, or null if there are none
+     */
+    public List<Projection> getProjectionsFor( String sourceName ) {
+        return this.projectionsBySourceName.get(sourceName);
+    }
+
+    /**
      * Determine whether this workspace has a projection supplied contribution
      * 
      * @param sourceName the name of the source

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -28,12 +28,14 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
@@ -43,6 +45,7 @@
 import org.jboss.dna.connector.federation.merge.MergePlan;
 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.NodeConflictBehavior;
 import org.jboss.dna.graph.cache.CachePolicy;
@@ -56,6 +59,7 @@
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
 import org.jboss.dna.graph.request.CompositeRequest;
 import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -70,6 +74,7 @@
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -87,6 +92,7 @@
     private final RepositoryConnectionFactory connectionFactory;
     /** The set of all connections, including the cache connection */
     private final Map<String, RepositoryConnection> connectionsBySourceName;
+    protected final PathFactory pathFactory;
     private Logger logger;
 
     /**
@@ -116,6 +122,7 @@
         this.logger = context.getLogger(getClass());
         this.connectionsBySourceName = new HashMap<String, RepositoryConnection>();
         this.defaultWorkspace = defaultWorkspace; // may be null
+        this.pathFactory = context.getValueFactories().getPathFactory();
     }
 
     protected DateTime getCurrentTimeInUtc() {
@@ -248,31 +255,118 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
      */
     @Override
-    public void process( CopyBranchRequest request ) {
-        throw new UnsupportedOperationException();
+    public void process( CreateNodeRequest request ) {
+        FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        if (workspace == null) return;
+
+        // Can push this down if and only if the entire request is within a single federated source ...
+        SingleProjection projection = asSingleProjection(workspace, request.under(), request);
+        if (projection == null) return;
+
+        // Push down the request ...
+        Location parentLocation = Location.create(projection.pathInSource);
+        String workspaceName = projection.projection.getWorkspaceName();
+        CreateNodeRequest sourceRequest = new CreateNodeRequest(parentLocation, workspaceName, request.named(),
+                                                                request.properties());
+        execute(sourceRequest, projection.projection);
+
+        // Copy/transform the results ...
+        if (sourceRequest.hasError()) {
+            request.setError(sourceRequest.getError());
+        } else {
+            request.setActualLocationOfNode(projection.convertToRepository(sourceRequest.getActualLocationOfNode()));
+        }
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
      */
     @Override
-    public void process( CreateNodeRequest request ) {
-        throw new UnsupportedOperationException();
+    public void process( DeleteBranchRequest request ) {
+        FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        if (workspace == null) return;
+
+        // Can push this down if and only if the entire request is within a single federated source ...
+        SingleProjection projection = asSingleProjection(workspace, request.at(), request);
+        if (projection == null) return;
+
+        // Push down the request ...
+        Location sourceLocation = Location.create(projection.pathInSource);
+        String workspaceName = projection.projection.getWorkspaceName();
+        DeleteBranchRequest sourceRequest = new DeleteBranchRequest(sourceLocation, workspaceName);
+        execute(sourceRequest, projection.projection);
+
+        // Copy/transform the results ...
+        if (sourceRequest.hasError()) {
+            request.setError(sourceRequest.getError());
+        } else {
+            request.setActualLocationOfNode(projection.convertToRepository(sourceRequest.getActualLocationOfNode()));
+        }
+
+        // Delete in the cache ...
+        DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.at(), workspace.getCacheProjection()
+                                                                                          .getWorkspaceName());
+        executeInCache(cacheRequest, workspace);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
      */
     @Override
-    public void process( DeleteBranchRequest request ) {
-        throw new UnsupportedOperationException();
+    public void process( CopyBranchRequest request ) {
+        FederatedWorkspace fromWorkspace = getWorkspace(request, request.fromWorkspace());
+        if (fromWorkspace == null) return;
+        FederatedWorkspace intoWorkspace = getWorkspace(request, request.intoWorkspace());
+        if (intoWorkspace == null) return;
+        if (!fromWorkspace.equals(intoWorkspace)) {
+            // Otherwise there wasn't a single projection with a single path ...
+            String msg = FederationI18n.unableToPerformOperationSpanningWorkspaces.text(fromWorkspace.getName(),
+                                                                                        intoWorkspace.getName());
+            request.setError(new UnsupportedRequestException(msg));
+        }
+
+        // Can push this down if and only if the entire request is within a single federated source ...
+        SingleProjection fromProjection = asSingleProjection(fromWorkspace, request.from(), request);
+        if (fromProjection == null) return;
+        SingleProjection intoProjection = asSingleProjection(intoWorkspace, request.into(), request);
+        if (intoProjection == null) return;
+        if (!intoProjection.projection.equals(fromProjection.projection)) {
+            // Otherwise there wasn't a single projection with a single path ...
+            String msg = FederationI18n.unableToPerformOperationUnlessLocationsAreFromSingleProjection.text(request.from(),
+                                                                                                            request.into(),
+                                                                                                            fromWorkspace.getName(),
+                                                                                                            fromProjection.projection.getRules(),
+                                                                                                            intoProjection.projection.getRules());
+            request.setError(new UnsupportedRequestException(msg));
+        }
+
+        // Push down the request ...
+        Location fromLocation = Location.create(fromProjection.pathInSource);
+        Location intoLocation = Location.create(intoProjection.pathInSource);
+        String workspaceName = fromProjection.projection.getWorkspaceName();
+        CopyBranchRequest sourceRequest = new CopyBranchRequest(fromLocation, workspaceName, intoLocation, workspaceName,
+                                                                request.desiredName(), request.conflictBehavior());
+        execute(sourceRequest, fromProjection.projection);
+
+        // Copy/transform the results ...
+        if (sourceRequest.hasError()) {
+            request.setError(sourceRequest.getError());
+        } else {
+            request.setActualLocations(fromProjection.convertToRepository(sourceRequest.getActualLocationBefore()),
+                                       intoProjection.convertToRepository(sourceRequest.getActualLocationAfter()));
+        }
+
+        // Delete from the cache the parent of the new location ...
+        DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(), fromWorkspace.getCacheProjection()
+                                                                                                .getWorkspaceName());
+        executeInCache(cacheRequest, fromWorkspace);
     }
 
     /**
@@ -282,7 +376,43 @@
      */
     @Override
     public void process( MoveBranchRequest request ) {
-        throw new UnsupportedOperationException();
+        FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        if (workspace == null) return;
+
+        // Can push this down if and only if the entire request is within a single federated source ...
+        SingleProjection fromProjection = asSingleProjection(workspace, request.from(), request);
+        if (fromProjection == null) return;
+        SingleProjection intoProjection = asSingleProjection(workspace, request.into(), request);
+        if (intoProjection == null) return;
+        if (!intoProjection.projection.equals(fromProjection.projection)) {
+            // Otherwise there wasn't a single projection with a single path ...
+            String msg = FederationI18n.unableToPerformOperationUnlessLocationsAreFromSingleProjection.text(request.from(),
+                                                                                                            request.into(),
+                                                                                                            workspace.getName(),
+                                                                                                            fromProjection.projection.getRules(),
+                                                                                                            intoProjection.projection.getRules());
+            request.setError(new UnsupportedRequestException(msg));
+        }
+
+        // Push down the request ...
+        Location fromLocation = Location.create(fromProjection.pathInSource);
+        Location intoLocation = Location.create(intoProjection.pathInSource);
+        String workspaceName = fromProjection.projection.getWorkspaceName();
+        MoveBranchRequest sourceRequest = new MoveBranchRequest(fromLocation, intoLocation, workspaceName, request.desiredName(),
+                                                                request.conflictBehavior());
+        execute(sourceRequest, fromProjection.projection);
+
+        // Copy/transform the results ...
+        if (sourceRequest.hasError()) {
+            request.setError(sourceRequest.getError());
+        } else {
+            request.setActualLocations(fromProjection.convertToRepository(sourceRequest.getActualLocationBefore()),
+                                       intoProjection.convertToRepository(sourceRequest.getActualLocationAfter()));
+        }
+        // Delete from the cache ...
+        DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.from(), workspace.getCacheProjection()
+                                                                                            .getWorkspaceName());
+        executeInCache(cacheRequest, workspace);
     }
 
     /**
@@ -292,7 +422,31 @@
      */
     @Override
     public void process( UpdatePropertiesRequest request ) {
-        throw new UnsupportedOperationException();
+        FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        if (workspace == null) return;
+
+        // Can push this down if and only if the entire request is within a single federated source ...
+        SingleProjection projection = asSingleProjection(workspace, request.on(), request);
+        if (projection == null) return;
+
+        // Push down the request ...
+        Location sourceLocation = Location.create(projection.pathInSource);
+        String workspaceName = projection.projection.getWorkspaceName();
+        UpdatePropertiesRequest sourceRequest = new UpdatePropertiesRequest(sourceLocation, workspaceName, request.properties());
+        execute(sourceRequest, projection.projection);
+
+        // Copy/transform the results ...
+        if (sourceRequest.hasError()) {
+            request.setError(sourceRequest.getError());
+        } else {
+            request.setActualLocationOfNode(projection.convertToRepository(sourceRequest.getActualLocationOfNode()));
+        }
+
+        // Update the cache ...
+        UpdatePropertiesRequest cacheRequest = new UpdatePropertiesRequest(request.on(), workspace.getCacheProjection()
+                                                                                                  .getWorkspaceName(),
+                                                                           request.properties());
+        executeInCache(cacheRequest, workspace);
     }
 
     /**
@@ -305,7 +459,7 @@
         FederatedWorkspace workspace = getWorkspace(request, request.workspaceName());
         if (workspace != null) {
             request.setActualWorkspaceName(workspace.getName());
-            Location root = Location.create(getExecutionContext().getValueFactories().getPathFactory().createRootPath());
+            Location root = Location.create(pathFactory.createRootPath());
             ReadNodeRequest nodeInfo = getNode(root, workspace);
             if (nodeInfo.hasError()) return;
             request.setActualRootLocation(nodeInfo.getActualLocationOfNode());
@@ -353,6 +507,96 @@
         throw new UnsupportedOperationException();
     }
 
+    @Immutable
+    protected class SingleProjection {
+        protected final Projection projection;
+        protected final Path pathInSource;
+        protected final Location federatedLocation;
+
+        protected SingleProjection( Projection projection,
+                                    Path pathInSource,
+                                    Location federatedLocation ) {
+            this.projection = projection;
+            this.federatedLocation = federatedLocation;
+            this.pathInSource = pathInSource;
+        }
+
+        protected Location convertToRepository( Location sourceLocation ) {
+            assert sourceLocation != null;
+            if (sourceLocation.hasPath()) {
+                Set<Path> paths = projection.getPathsInRepository(sourceLocation.getPath(), pathFactory);
+                assert paths.size() == 1;
+                return sourceLocation.with(paths.iterator().next());
+            }
+            return sourceLocation;
+        }
+    }
+
+    protected SingleProjection asSingleProjection( FederatedWorkspace federatedWorkspace,
+                                                   Location location,
+                                                   Request request ) {
+        // Check the cache for this location ...
+        ReadNodeRequest nodeInfo = getNode(location, federatedWorkspace);
+        if (nodeInfo.hasError()) {
+            request.setError(nodeInfo.getError());
+            return null;
+        }
+        Location actualLocation = nodeInfo.getActualLocationOfNode();
+        Path pathInRepository = actualLocation.getPath();
+        assert pathInRepository != null;
+
+        // Get the merge plan for the node ...
+        MergePlan plan = getMergePlan(nodeInfo);
+        assert plan != null;
+        if (plan.getRealContributionCount() == 1) {
+            for (Contribution contribution : plan) {
+                if (contribution.isEmpty() || contribution.isPlaceholder()) continue;
+                for (Projection projection : federatedWorkspace.getProjectionsFor(contribution.getSourceName())) {
+                    Set<Path> paths = projection.getPathsInSource(pathInRepository, pathFactory);
+                    if (paths.size() == 1) {
+                        return new SingleProjection(projection, paths.iterator().next(), actualLocation);
+                    }
+                }
+            }
+        }
+
+        // Otherwise there wasn't a single projection with a single path ...
+        StringBuilder projections = new StringBuilder();
+        boolean first = true;
+        for (Contribution contribution : plan) {
+            if (contribution.isPlaceholder() || contribution.isEmpty()) continue;
+            if (first) first = false;
+            else projections.append(", ");
+            for (Projection projection : federatedWorkspace.getProjectionsFor(contribution.getSourceName())) {
+                Set<Path> paths = projection.getPathsInSource(pathInRepository, pathFactory);
+                if (paths.size() == 1) {
+                    projections.append(FederationI18n.pathInProjection.text(paths.iterator().next(), projection.getRules()));
+                } else {
+                    projections.append(FederationI18n.pathInProjection.text(paths, projection.getRules()));
+                }
+            }
+        }
+        String msg = FederationI18n.unableToPerformOperationUnlessLocationIsFromSingleProjection.text(location,
+                                                                                                      federatedWorkspace.getName(),
+                                                                                                      projections);
+        request.setError(new UnsupportedRequestException(msg));
+        return null;
+    }
+
+    protected void execute( Request request,
+                            Projection projection ) {
+        RepositoryConnection connection = getConnection(projection);
+        connection.execute(getExecutionContext(), request);
+        // Don't need to close, as we'll close all connections when this processor is closed
+    }
+
+    protected void executeInCache( Request request,
+                                   FederatedWorkspace workspace ) {
+        RepositoryConnection connection = getConnectionToCacheFor(workspace);
+        connection.execute(getExecutionContext(), request);
+        // Don't need to close, as we'll close all connections when this processor is closed
+    }
+
     /**
      * Get the node information from the underlying sources or, if possible, from the cache.
      * 
@@ -363,12 +607,9 @@
      */
     protected ReadNodeRequest getNode( Location location,
                                        FederatedWorkspace workspace ) throws RepositorySourceException {
-        final ExecutionContext context = getExecutionContext();
-
         // Check the cache first ...
-        RepositoryConnection cacheConnection = getConnectionToCacheFor(workspace);
         ReadNodeRequest fromCache = new ReadNodeRequest(location, workspace.getCacheProjection().getWorkspaceName());
-        cacheConnection.execute(context, fromCache);
+        executeInCache(fromCache, workspace);
 
         // Look at the cache results from the cache for problems, or if found a plan in the cache look
         // at the contributions. We'll be putting together the set of source names for which we need to
@@ -395,13 +636,19 @@
                         Location locationToLoad = Location.create(pathToLoad);
                         loadContributionsFromSources(locationToLoad, workspace, null, contributions); // sourceNames may be
                         // null or empty
-                        FederatedNode mergedNode = createFederatedNode(locationToLoad, workspace, contributions, true);
+                        FederatedNode mergedNode = createFederatedNode(locationToLoad, workspace, fromCache, contributions, true);
                         if (mergedNode == null) {
                             // No source had a contribution ...
                             I18n msg = FederationI18n.nodeDoesNotExistAtPath;
                             fromCache.setError(new PathNotFoundException(location, ancestor, msg.text(path, ancestor)));
                             return fromCache;
                         }
+                        MergePlan mergePlan = mergedNode.getMergePlan();
+                        if (mergePlan != null) {
+                            Property mergePlanProperty = getExecutionContext().getPropertyFactory().create(DnaLexicon.MERGE_PLAN,
+                                                                                                           (Object)mergePlan);
+                            fromCache.addProperty(mergePlanProperty);
+                        }
                         contributions.clear();
                         // Move to the next child along the path ...
                         pathToLoad = pathToLoad.getParent();
@@ -449,7 +696,7 @@
             location = fromCache.at();
         }
         loadContributionsFromSources(location, workspace, sourceNames, contributions); // sourceNames may be null or empty
-        FederatedNode mergedNode = createFederatedNode(location, workspace, contributions, true);
+        FederatedNode mergedNode = createFederatedNode(location, workspace, fromCache, contributions, true);
         if (mergedNode == null) {
             // No source had a contribution ...
             if (location.hasPath()) {
@@ -467,6 +714,7 @@
 
     protected FederatedNode createFederatedNode( Location location,
                                                  FederatedWorkspace federatedWorkspace,
+                                                 ReadNodeRequest fromCache,
                                                  List<Contribution> contributions,
                                                  boolean updateCache ) throws RepositorySourceException {
         assert location != null;
@@ -492,7 +740,7 @@
         // Create the node, and use the existing UUID if one is found in the cache ...
         ExecutionContext context = getExecutionContext();
         assert context != null;
-        FederatedNode mergedNode = new FederatedNode(location.with((UUID)null), federatedWorkspace.getName());
+        FederatedNode mergedNode = new FederatedNode(location, federatedWorkspace.getName());
 
         // Merge the results into a single set of results ...
         assert contributions.size() > 0;
@@ -502,7 +750,7 @@
         }
         if (updateCache) {
             // Place the results into the cache ...
-            updateCache(federatedWorkspace, mergedNode);
+            updateCache(federatedWorkspace, mergedNode, fromCache);
         }
         // And return the results ...
         return mergedNode;
@@ -525,7 +773,6 @@
                                                  List<Contribution> contributions ) throws RepositorySourceException {
         // At this point, there is no merge plan, so read information from the sources ...
         final ExecutionContext context = getExecutionContext();
-        final PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
         CachePolicy cachePolicy = federatedWorkspace.getCachePolicy();
         // If the location has no path, then we have to submit a request to ALL sources ...
@@ -720,9 +967,9 @@
     }
 
     protected void updateCache( FederatedWorkspace federatedWorkspace,
-                                FederatedNode mergedNode ) throws RepositorySourceException {
+                                FederatedNode mergedNode,
+                                ReadNodeRequest fromCache ) throws RepositorySourceException {
         final ExecutionContext context = getExecutionContext();
-        final RepositoryConnection cacheConnection = getConnectionToCacheFor(federatedWorkspace);
         final Location location = mergedNode.at();
         final Path path = location.getPath();
         final String cacheWorkspace = federatedWorkspace.getCacheProjection().getWorkspaceName();
@@ -733,29 +980,126 @@
         // If the merged node has a merge plan, then add it to the properties if it is not already there ...
         Map<Name, Property> properties = mergedNode.getPropertiesByName();
         MergePlan mergePlan = mergedNode.getMergePlan();
-        if (mergePlan != null) {
+        if (mergePlan != null && !properties.containsKey(DnaLexicon.MERGE_PLAN)) {
             // Record the merge plan on the merged node ...
             Property mergePlanProperty = getExecutionContext().getPropertyFactory().create(DnaLexicon.MERGE_PLAN,
                                                                                            (Object)mergePlan);
             properties.put(mergePlanProperty.getName(), mergePlanProperty);
         }
 
-        if (path.isRoot()) {
-            // Set the properties ...
-            requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
-        } else {
-            // This is not the root node, so we need to create the node ...
+        // Make sure the UUID is being stored ...
+        PropertyFactory propertyFactory = getExecutionContext().getPropertyFactory();
+        Property uuidProperty = properties.get(DnaLexicon.UUID);
+        if (uuidProperty == null) uuidProperty = properties.get(JcrLexicon.UUID);
+        if (uuidProperty == null) {
+            UUID uuid = mergedNode.at().getUuid();
+            if (uuid == null) uuid = UUID.randomUUID();
+            uuidProperty = propertyFactory.create(DnaLexicon.UUID, uuid);
+            properties.put(uuidProperty.getName(), uuidProperty);
+        }
+
+        // Have the children changed ...
+        if (mergedNode.hasError() && !path.isRoot()) {
+            // This is not the root node, so we need to create the node (or replace it if it exists) ...
             final Location parentLocation = Location.create(path.getParent());
             childName = path.getLastSegment().getName();
             requests.add(new CreateNodeRequest(parentLocation, cacheWorkspace, childName, NodeConflictBehavior.REPLACE,
                                                mergedNode.getProperties()));
+            // logger.trace("Adding {0} to cache with properties {1}", location, properties);
+            // Now create all of the children that this federated node knows of ...
+            for (Location child : mergedNode.getChildren()) {
+                childName = child.getPath().getLastSegment().getName();
+                requests.add(new CreateNodeRequest(location, cacheWorkspace, childName, NodeConflictBehavior.APPEND, child));
+                // logger.trace("Caching child of {0} named {1}", location, childName);
+            }
+        } else if (fromCache.getChildren().equals(mergedNode.getChildren())) {
+            // Just update the properties ...
+            requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
+            // logger.trace("Updating cached properties on the root to {0}", properties);
+        } else {
+            // The children have changed, so figure out how ...
+            if (fromCache.getChildren().isEmpty()) {
+                // No children in the cache, so just update the properties of the node ...
+                requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
+                // logger.trace("Updating cached properties on {0} to {1}", location, properties);
+
+                // And create all of the children that this federated node knows of ...
+                for (Location child : mergedNode.getChildren()) {
+                    childName = child.getPath().getLastSegment().getName();
+                    requests.add(new CreateNodeRequest(location, cacheWorkspace, childName, NodeConflictBehavior.APPEND, child));
+                    // logger.trace("Caching child of {0} named {1}", location, childName);
+                }
+            } else if (mergedNode.getChildren().isEmpty()) {
+                // There were children in the cache but not in the merged node, so update the cached properties
+                requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
+
+                // and delete all the children ...
+                for (Location child : fromCache.getChildren()) {
+                    requests.add(new DeleteBranchRequest(child, cacheWorkspace));
+                    // logger.trace("Removing {0} from cache", child);
+                }
+            } else {
+                // There were children in the cache and in the merged node. The easy way is to just remove the
+                // branch from the cache, the create it again ...
+                if (path.isRoot()) {
+                    requests.add(new UpdatePropertiesRequest(location, cacheWorkspace, properties));
+                    // logger.trace("Updating cached properties on {0} to {1}", location, properties);
+
+                    // and delete all the children ...
+                    for (Location child : fromCache.getChildren()) {
+                        requests.add(new DeleteBranchRequest(child, cacheWorkspace));
+                        // logger.trace("Removing child node {0} from cache", child);
+                    }
+
+                    // Now create all of the children that this federated node knows of ...
+                    for (Location child : mergedNode.getChildren()) {
+                        childName = child.getPath().getLastSegment().getName();
+                        requests.add(new CreateNodeRequest(location, cacheWorkspace, childName, NodeConflictBehavior.APPEND,
+                                                           child));
+                        // logger.trace("Caching child of {0} named {1}", location, childName);
+                    }
+                } else {
+                    requests.add(new DeleteBranchRequest(location, cacheWorkspace));
+                    // logger.trace("Replacing node {0} from cache", location);
+
+                    // This is not the root node, so we need to create the node (or replace it if it exists) ...
+                    final Location parentLocation = Location.create(path.getParent());
+                    childName = path.getLastSegment().getName();
+                    requests.add(new CreateNodeRequest(parentLocation, cacheWorkspace, childName, NodeConflictBehavior.REPLACE,
+                                                       mergedNode.getProperties()));
+                    // logger.trace("Adding {0} to cache with properties {1}", location, properties);
+                    // Now create all of the children that this federated node knows of ...
+                    for (Location child : mergedNode.getChildren()) {
+                        childName = child.getPath().getLastSegment().getName();
+                        requests.add(new CreateNodeRequest(location, cacheWorkspace, childName, NodeConflictBehavior.APPEND,
+                                                           child));
+                        // logger.trace("Caching child of {0} named {1}", location, childName);
+                    }
+                }
+            }
         }
 
-        // Now create all of the children that this federated node knows of ...
-        for (Location child : mergedNode.getChildren()) {
-            childName = child.getPath().getLastSegment().getName();
-            requests.add(new CreateNodeRequest(location, cacheWorkspace, childName, NodeConflictBehavior.APPEND));
+        // Execute all the requests ...
+        final RepositoryConnection cacheConnection = getConnectionToCacheFor(federatedWorkspace);
+        cacheConnection.execute(context, CompositeRequest.with(requests));
+
+        // If the children did not have UUIDs, then find the actual locations for each of the cached nodes ...
+        if (requests.size() > 1) {
+            Iterator<Request> requestIter = requests.iterator();
+            requestIter.next(); // Skip the first request, which creates/updates the node (we want children)
+            List<Location> children = mergedNode.getChildren();
+            for (int i = 0; i != children.size(); ++i) {
+                Request request = requestIter.next();
+                while (!(request instanceof CreateNodeRequest)) { // skip non-create requests
+                    request = requestIter.next();
+                }
+                Location actual = ((CreateNodeRequest)request).getActualLocationOfNode();
+                Location child = children.get(i);
+                if (!child.hasIdProperties()) {
+                    assert child.getPath().equals(actual.getPath());
+                    children.set(i, actual);
+                }
+            }
         }
-        cacheConnection.execute(context, CompositeRequest.with(requests));
     }
 }

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederationI18n.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederationI18n.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederationI18n.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -43,6 +43,11 @@
     public static I18n unableToAuthenticateConnectionToFederatedRepository;
     public static I18n repositoryHasBeenShutDown;
     public static I18n errorAddingProjectionRuleParseMethod;
+    public static I18n pathInProjection;
+    public static I18n pathsInProjection;
+    public static I18n unableToPerformOperationUnlessLocationIsFromSingleProjection;
+    public static I18n unableToPerformOperationUnlessLocationsAreFromSingleProjection;
+    public static I18n unableToPerformOperationSpanningWorkspaces;
 
     public static I18n workspaceDoesNotExist;
     public static I18n noDefaultWorkspace;

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -242,6 +242,21 @@
     public abstract int getContributionCount();
 
     /**
+     * Get the number of contributions that are not placeholders or empty.
+     * 
+     * @return the number of real contributions
+     */
+    public int getRealContributionCount() {
+        int count = 0;
+        for (Contribution contribution : this) {
+            if (contribution.isPlaceholder()) continue;
+            if (contribution.isEmpty()) continue;
+            ++count;
+        }
+        return count;
+    }
+
+    /**
      * Get the plan annotation property with the given name. Plan annotations are custom properties that may be set by
      * MergeProcessor implementations to store custom properties on the plan. This method does nothing if the supplied name is
      * null

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategy.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategy.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategy.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -91,26 +91,32 @@
         }
         if (dnaUuidProperty != null) uuidProperty = dnaUuidProperty; // use "dna:uuid" if there is one
 
-        // Look for the UUID property on the properties, and update the federated node ...
-        if (uuidProperty != null && !uuidProperty.isEmpty()) {
-            UUID uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
-            uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); // Use the "dna:uuid" name
-            federatedNode.setActualLocationOfNode(federatedNode.at().with(uuidProperty));
+        if (federatedNode.at().getUuid() != null) {
+            federatedNode.setActualLocationOfNode(federatedNode.at());
         } else {
-            // Make sure there's a UUID for an identification property ...
-            if (location.getUuid() == null) {
-                location = location.with(UUID.randomUUID());
+            // Look for the UUID property on the properties, and update the federated node ...
+            if (uuidProperty != null && !uuidProperty.isEmpty()) {
+                UUID uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
+                uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); // Use the "dna:uuid" name
+                federatedNode.setActualLocationOfNode(federatedNode.at().with(uuidProperty));
+            } else {
+                // Make sure there's a UUID for an identification property ...
+                if (location.getUuid() == null) {
+                    location = location.with(UUID.randomUUID());
+                }
+                // Set the UUID as a property (it wasn't set already) ...
+                uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+                assert uuidProperty != null; // there should be one!
+                federatedNode.addProperty(uuidProperty);
+                federatedNode.setActualLocationOfNode(location);
             }
-            // Set the UUID as a property (it wasn't set already) ...
-            uuidProperty = location.getIdProperty(DnaLexicon.UUID);
-            assert uuidProperty != null; // there should be one!
-            federatedNode.addProperty(uuidProperty);
-            federatedNode.setActualLocationOfNode(location);
         }
 
         // Assign the merge plan ...
         MergePlan mergePlan = MergePlan.create(contributions);
         federatedNode.setMergePlan(mergePlan);
+        Property mergePlanProperty = context.getPropertyFactory().create(DnaLexicon.MERGE_PLAN, (Object)mergePlan);
+        federatedNode.addProperty(mergePlanProperty);
     }
 
     private boolean hasUuidValue( ExecutionContext context,

Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -110,7 +110,7 @@
                     if (!childNames.containsKey(childName)) {
                         childNames.put(childName, 1);
                         Path pathToChild = pathFactory.create(location.getPath(), childName);
-                        federatedNode.addChild(Location.create(pathToChild));
+                        federatedNode.addChild(child.with(pathToChild));
                     }
                 }
             } else {
@@ -140,7 +140,7 @@
                         index = previousValue;
                     }
                     Path pathToChild = pathFactory.create(location.getPath(), childName, index);
-                    federatedNode.addChild(Location.create(pathToChild));
+                    federatedNode.addChild(child.with(pathToChild)); // keep the same identifiers
                 }
 
                 // Add in the properties ...
@@ -152,7 +152,7 @@
 
                     // Record the property ...
                     Property existing = properties.put(property.getName(), property);
-                    if (existing != null) {
+                    if (existing != null && !existing.equals(property)) {
                         // There's already an existing property, so we need to merge them ...
                         Property merged = merge(existing, property, context.getPropertyFactory(), removeDuplicateProperties);
                         properties.put(property.getName(), merged);
@@ -181,6 +181,8 @@
         // Assign the merge plan ...
         MergePlan mergePlan = MergePlan.create(contributions);
         federatedNode.setMergePlan(mergePlan);
+        Property mergePlanProperty = context.getPropertyFactory().create(DnaLexicon.MERGE_PLAN, (Object)mergePlan);
+        properties.put(mergePlanProperty.getName(), mergePlanProperty);
     }
 
     /**

Modified: trunk/extensions/dna-connector-federation/src/main/resources/org/jboss/dna/connector/federation/FederationI18n.properties
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/resources/org/jboss/dna/connector/federation/FederationI18n.properties	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/main/resources/org/jboss/dna/connector/federation/FederationI18n.properties	2009-04-30 04:32:26 UTC (rev 866)
@@ -32,6 +32,11 @@
 unableToAuthenticateConnectionToFederatedRepository = Unable to authenticate "{1}" for federated repository "{0}"
 repositoryHasBeenShutDown = The "{0}" federated repository has been shut down and may no longer be used.
 errorAddingProjectionRuleParseMethod = Error while adding a parsing method for a federation projection rule
+pathInProjection = path "{0}" in projection {1}
+pathsInProjection = paths "{0}" in projection {1}
+unableToPerformOperationUnlessLocationIsFromSingleProjection = The node "{0}" in workspace "{1}" must be within a single projection for this operation to succeed, but is in {2}
+unableToPerformOperationUnlessLocationsAreFromSingleProjection = The nodes "{0}" and "{1}" in workspace "{2}" must both be within a single projection for this operation to succeed, but come from projections "{3}" and "{4}"
+unableToPerformOperationSpanningWorkspaces = Unable to perform operation spanning workspaces ("{0}" and "{1}")
 
 workspaceDoesNotExist = The "{0}" federated repository does not have a workspace named "{1}"
 noDefaultWorkspace = The "{0}" federated repository does not have a default workspace
\ No newline at end of file

Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatingRequestProcessorTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatingRequestProcessorTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatingRequestProcessorTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -41,10 +41,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
-import org.jboss.dna.connector.federation.FederatedWorkspace;
-import org.jboss.dna.connector.federation.FederatingRequestProcessor;
-import org.jboss.dna.connector.federation.Projection;
-import org.jboss.dna.connector.federation.ProjectionParser;
 import org.jboss.dna.connector.federation.contribution.Contribution;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -55,8 +51,19 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 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.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -93,6 +100,7 @@
         cachePolicy = new BasicCachePolicy(219L, TimeUnit.SECONDS);
         cacheSource = new InMemoryRepositorySource();
         cacheSource.setName("Cache");
+        cacheSource.setDefaultWorkspaceName("cacheSpace");
         ProjectionParser ruleParser = ProjectionParser.getInstance();
         cacheProjectionRules = ruleParser.rulesFromStrings(context, "/ => /cache/repo/A");
         cacheProjection = new Projection(cacheSource.getName(), "cacheSpace", cacheProjectionRules);
@@ -128,6 +136,82 @@
         doReturn(cacheSource.getConnection()).when(connectionFactory).createConnection(cacheSource.getName());
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Path path( String path ) {
+        return pathFactory.create(path);
+    }
+
+    protected Path.Segment segment( String path ) {
+        return pathFactory.createSegment(path);
+    }
+
+    protected Property property( String name,
+                                 Object... values ) {
+        return context.getPropertyFactory().create(name(name), values);
+    }
+
+    protected Map<Name, Property> addProperty( Map<Name, Property> properties,
+                                               String name,
+                                               Object... values ) {
+        Property property = property(name, values);
+        properties.put(property.getName(), property);
+        return properties;
+    }
+
+    protected void assertNodeHasChildren( String path,
+                                          String... expectedChildSegments ) {
+        Location parent = Location.create(path(path));
+        ReadAllChildrenRequest request = new ReadAllChildrenRequest(parent, "fedSpace");
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path(path)));
+        List<Location> actualChildLocations = request.getChildren();
+        Path.Segment[] actualChildren = new Path.Segment[actualChildLocations.size()];
+        for (int i = 0; i != actualChildren.length; ++i) {
+            Path actualChildPath = actualChildLocations.get(i).getPath();
+            actualChildren[i] = actualChildPath.getLastSegment();
+            assertThat(actualChildPath.getParent(), is(path(path)));
+        }
+        Path.Segment[] expectedChildren = new Path.Segment[expectedChildSegments.length];
+        for (int i = 0; i != expectedChildren.length; ++i) {
+            expectedChildren[i] = segment(expectedChildSegments[i]);
+        }
+        assertThat(actualChildren, is(expectedChildren));
+    }
+
+    protected void assertNodeHasProperty( String path,
+                                          String propertyName,
+                                          Object... expectedValues ) {
+        Location parent = Location.create(path(path));
+        ReadPropertyRequest request = new ReadPropertyRequest(parent, "fedSpace", name(propertyName));
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path(path)));
+        Object[] actualValues = request.getProperty().getValuesAsArray();
+        String[] actualValuesAsStrings = new String[actualValues.length];
+        for (int i = 0; i != actualValues.length; ++i) {
+            actualValuesAsStrings[i] = context.getValueFactories().getStringFactory().create(actualValues[i]);
+        }
+        String[] expectedValuesAsStrings = new String[expectedValues.length];
+        for (int i = 0; i != expectedValues.length; ++i) {
+            expectedValuesAsStrings[i] = context.getValueFactories().getStringFactory().create(expectedValues[i]);
+        }
+        assertThat(actualValuesAsStrings, is(expectedValuesAsStrings));
+    }
+
+    protected void assertNodeHasNoProperty( String path,
+                                            String propertyName ) {
+        Location parent = Location.create(path(path));
+        ReadAllPropertiesRequest request = new ReadAllPropertiesRequest(parent, "fedSpace");
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path(path)));
+        assertThat(request.getPropertiesByName().containsKey(name(propertyName)), is(false));
+    }
+
     @Test( expected = IllegalArgumentException.class )
     public void shouldFailWhenExecutionContextIsNull() {
         context = null;
@@ -568,9 +652,203 @@
         assertThat(contributions.get(2).isExpired(nowPlus310InUtc), is(true)); // expired by cache @ 219
     }
 
+    /**
+     * Set up the sources such that:
+     * <ul>
+     * <li>Source 1: "{@code /a => /source/one/a}", "{@code /b => /source/one/b}"</li>
+     * <li>Source 2: "{@code /a => /source/two/a}"</li>
+     * <li>Source 1: "{@code / => /}"</li>
+     * </ul>
+     */
+    protected void initializeSourcesWithContent() {
+        // Set up the content of source 1
+        Graph repository1 = Graph.create(source1, context);
+        repository1.createWorkspace().named("workspace1");
+        Graph.Batch batch = repository1.batch();
+        batch.create("/source").and();
+        batch.create("/source/one").and();
+        batch.create("/source/one/a").with("desc", "source 1 node a description").and();
+        batch.create("/source/one/a/nA").with("desc", "source 1 node nA description").and();
+        batch.create("/source/one/a/nB").with("desc", "source 1 node nB description").and();
+        batch.create("/source/one/a/nC").with("desc", "source 1 node nC description").and();
+        batch.create("/source/one/b").with("desc", "source 1 node b description").and();
+        batch.create("/source/one/b/pA").with("desc", "source 1 node pA description").and();
+        batch.create("/source/one/b/pB").with("desc", "source 1 node pB description").and();
+        batch.create("/source/one/b/pC").with("desc", "source 1 node pC description").and();
+        batch.execute();
+
+        // Set up the content of source 2
+        Graph repository2 = Graph.create(source2, context);
+        repository2.createWorkspace().named("workspace2");
+        batch = repository2.batch();
+        batch.create("/source").and();
+        batch.create("/source/two").and();
+        batch.create("/source/two/a").with("desc", "source 2 node a description").and();
+        batch.create("/source/two/a/qA").with("desc", "source 2 node qA description").and();
+        batch.create("/source/two/a/qB").with("desc", "source 2 node qB description").and();
+        batch.create("/source/two/a/qC").with("desc", "source 2 node qC description").and();
+        batch.execute();
+
+        // Set up the content of source 3
+        Graph repository3 = Graph.create(source3, context);
+        repository3.createWorkspace().named("workspace3");
+        batch = repository3.batch();
+        batch.create("/x").and();
+        batch.create("/x/y").with("desc", "y description").and();
+        batch.create("/x/y/zA").with("desc", "zA description").and();
+        batch.create("/x/y/zB").with("desc", "zB description").and();
+        batch.create("/x/y/zC").with("desc", "zC description").and();
+        batch.create("/b").and();
+        batch.create("/b/by").with("desc", "by description").and();
+        batch.create("/b/by/bzA").with("desc", "bzA description").and();
+        batch.create("/b/by/bzB").with("desc", "bzB description").and();
+        batch.create("/a").and();
+        batch.create("/a/ay").with("desc", "ay description").and();
+        batch.create("/a/ay/azA").with("desc", "azA description").and();
+        batch.create("/a/ay/azB").with("desc", "azB description").and();
+        batch.execute();
+
+    }
+
+    @Test( expected = UnsupportedRequestException.class )
+    public void shouldNotCreateNodeIfParentIsOutsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location parent = Location.create(path("/a")); // source 2 or 3?
+        CreateNodeRequest request = new CreateNodeRequest(parent, "fedSpace", name("child"));
+        executor.process(request);
+        if (request.hasError()) throw request.getError();
+    }
+
     @Test
-    public void shouldGetNodeUsingPath() {
+    public void shouldCreateNodeOnlyIfParentIsInsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location parent = Location.create(path("/a/ay")); // source 3
+        CreateNodeRequest request = new CreateNodeRequest(parent, "fedSpace", name("child"));
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path("/a/ay/child")));
+        assertNodeHasChildren("/a/ay", "azA", "azB", "child");
+    }
 
+    @Test( expected = UnsupportedRequestException.class )
+    public void shouldNotDestroyNodeIfOutsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location location = Location.create(path("/a")); // source 2 or 3?
+        DeleteBranchRequest request = new DeleteBranchRequest(location, "fedSpace");
+        executor.process(request);
+        if (request.hasError()) throw request.getError();
     }
 
+    @Test
+    public void shouldDestroyNodeOnlyIfInsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        Location location = Location.create(path("/a/ay")); // source 3
+        DeleteBranchRequest request = new DeleteBranchRequest(location, "fedSpace");
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path("/a/ay")));
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC");
+    }
+
+    @Test( expected = UnsupportedRequestException.class )
+    public void shouldNotUpdateNodeIfOutsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location location = Location.create(path("/a")); // source 2 or 3?
+        Map<Name, Property> properties = new HashMap<Name, Property>();
+        addProperty(properties, "prop1", "value1");
+        addProperty(properties, "prop2", "value2a", "value2b");
+        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, "fedSpace", properties);
+        executor.process(request);
+        if (request.hasError()) throw request.getError();
+    }
+
+    @Test
+    public void shouldUpdateNodeOnlyIfInsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        // assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasProperty("/a/ay", "desc", "ay description");
+        assertNodeHasNoProperty("/a/ay", "prop1");
+        assertNodeHasNoProperty("/a/ay", "prop2");
+        Location location = Location.create(path("/a/ay")); // source 3
+        Map<Name, Property> properties = new HashMap<Name, Property>();
+        addProperty(properties, "desc", "ay description 2");
+        addProperty(properties, "prop1", "value1");
+        addProperty(properties, "prop2", "value2a", "value2b");
+        UpdatePropertiesRequest request = new UpdatePropertiesRequest(location, "fedSpace", properties);
+        executor.process(request);
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationOfNode().getPath(), is(path("/a/ay")));
+        assertNodeHasProperty("/a/ay", "desc", "ay description 2");
+        assertNodeHasProperty("/a/ay", "prop1", "value1");
+        assertNodeHasProperty("/a/ay", "prop2", "value2a", "value2b");
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+    }
+
+    @Test( expected = UnsupportedRequestException.class )
+    public void shouldNotMoveNodeIfParentIsOutsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location from = Location.create(path("/a/ay")); // source 3
+        Location into = Location.create(path("/b")); // source 1 or source 3?
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasChildren("/b", "pA", "pB", "pC", "by");
+        MoveBranchRequest request = new MoveBranchRequest(from, into, "fedSpace");
+        executor.process(request);
+        if (request.hasError()) throw request.getError();
+    }
+
+    @Test
+    public void shouldMoveNodeOnlyIfParentIsInsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location from = Location.create(path("/a/ay")); // source 3
+        Location into = Location.create(path("/b/by")); // source 3
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasChildren("/b", "pA", "pB", "pC", "by");
+        assertNodeHasChildren("/b/by", "bzA", "bzB");
+        MoveBranchRequest request = new MoveBranchRequest(from, into, "fedSpace");
+        executor.process(request);
+        if (request.hasError()) {
+            System.out.println(request.getError().getMessage());
+        }
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationBefore().getPath(), is(path("/a/ay")));
+        assertThat(request.getActualLocationAfter().getPath(), is(path("/b/by/ay")));
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC");
+        assertNodeHasChildren("/b/by", "bzA", "bzB", "ay");
+    }
+
+    @Test( expected = UnsupportedRequestException.class )
+    public void shouldNotCopyNodeIfParentIsOutsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location from = Location.create(path("/a/ay")); // source 3
+        Location into = Location.create(path("/b")); // source 1 or source 3?
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasChildren("/b", "pA", "pB", "pC", "by");
+        CopyBranchRequest request = new CopyBranchRequest(from, "fedSpace", into, "fedSpace");
+        executor.process(request);
+        if (request.hasError()) throw request.getError();
+    }
+
+    @Test
+    public void shouldCopyNodeOnlyIfParentIsInsideSingleProjection() throws Throwable {
+        initializeSourcesWithContent();
+        Location from = Location.create(path("/a/ay")); // source 3
+        Location into = Location.create(path("/b/by")); // source 3
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasChildren("/a/ay", "azA", "azB");
+        assertNodeHasChildren("/b", "pA", "pB", "pC", "by");
+        assertNodeHasChildren("/b/by", "bzA", "bzB");
+        CopyBranchRequest request = new CopyBranchRequest(from, "fedSpace", into, "fedSpace");
+        executor.process(request);
+        if (request.hasError()) {
+            System.out.println(request.getError().getMessage());
+        }
+        assertThat(request.hasError(), is(false));
+        assertThat(request.getActualLocationBefore().getPath(), is(path("/a/ay")));
+        assertThat(request.getActualLocationAfter().getPath(), is(path("/b/by/ay")));
+        assertNodeHasChildren("/a", "nA", "nB", "nC", "qA", "qB", "qC", "ay");
+        assertNodeHasChildren("/b/by", "bzA", "bzB", "ay");
+        assertNodeHasChildren("/b/by/ay", "azA", "azB");
+    }
+
 }

Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategyTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategyTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/OneContributionMergeStrategyTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -104,6 +104,7 @@
         stub(contribution.getProperties()).toReturn(properties.values().iterator());
         strategy.merge(node, contributions, context);
         properties.put(DnaLexicon.UUID, node.getPropertiesByName().get(DnaLexicon.UUID));
+        properties.put(DnaLexicon.MERGE_PLAN, node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN));
         assertThat(node.getPropertiesByName(), is(properties));
     }
 

Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategyTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategyTest.java	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategyTest.java	2009-04-30 04:32:26 UTC (rev 866)
@@ -88,8 +88,9 @@
     public void shouldCombinePropertiesFromOneContribution() {
         addContribution("source1").setProperty("p1", "p1 value");
         strategy.merge(node, contributions, context);
-        assertThat(node.getProperties().size(), is(2));
+        assertThat(node.getProperties().size(), is(3));
         assertThat(node.getPropertiesByName().get(DnaLexicon.UUID), is(notNullValue()));
+        assertThat(node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN), is(notNullValue()));
         assertThat(node.getPropertiesByName().get(name("p1")), is(property("p1", "p1 value")));
     }
 
@@ -122,8 +123,9 @@
         addContribution("source1").setProperty("p1", "p1 value");
         addContribution("source2").setProperty("p2", "p2 value");
         strategy.merge(node, contributions, context);
-        assertThat(node.getProperties().size(), is(3));
+        assertThat(node.getProperties().size(), is(4));
         assertThat(node.getPropertiesByName().get(DnaLexicon.UUID), is(notNullValue()));
+        assertThat(node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN), is(notNullValue()));
         assertThat(node.getPropertiesByName().get(name("p1")), is(property("p1", "p1 value")));
         assertThat(node.getPropertiesByName().get(name("p2")), is(property("p2", "p2 value")));
     }
@@ -133,8 +135,9 @@
         addContribution("source1").setProperty("p1", "p1 value").setProperty("p12", "1", "2", "3");
         addContribution("source2").setProperty("p2", "p2 value").setProperty("p12", "3", "4");
         strategy.merge(node, contributions, context);
-        assertThat(node.getProperties().size(), is(4));
+        assertThat(node.getProperties().size(), is(5));
         assertThat(node.getPropertiesByName().get(DnaLexicon.UUID), is(notNullValue()));
+        assertThat(node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN), is(notNullValue()));
         assertThat(node.getPropertiesByName().get(name("p1")), is(property("p1", "p1 value")));
         assertThat(node.getPropertiesByName().get(name("p2")), is(property("p2", "p2 value")));
         assertThat(node.getPropertiesByName().get(name("p12")), is(property("p12", "1", "2", "3", "4")));
@@ -145,7 +148,8 @@
         addContribution("source1").setProperty("p1", "p1 value").setProperty("p12", "1", "2", "3");
         addContribution("source2").setProperty("p2", "p2 value").setProperty("p12", "3", "4");
         strategy.merge(node, contributions, context);
-        assertThat(node.getProperties().size(), is(4));
+        assertThat(node.getProperties().size(), is(5));
+        assertThat(node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN), is(notNullValue()));
         assertThat(node.getPropertiesByName().get(DnaLexicon.UUID), is(notNullValue()));
         for (Contribution contribution : contributions) {
             Iterator<Property> iter = contribution.getProperties();
@@ -173,8 +177,9 @@
         addContribution("source1").setProperty("p1", "p1 value").setProperty("p12", "1", "2", "3");
         addContribution("source2").setProperty("p2", "p2 value").setProperty("p12", 3, 4);
         strategy.merge(node, contributions, context);
-        assertThat(node.getProperties().size(), is(4));
+        assertThat(node.getProperties().size(), is(5));
         assertThat(node.getPropertiesByName().get(DnaLexicon.UUID), is(notNullValue()));
+        assertThat(node.getPropertiesByName().get(DnaLexicon.MERGE_PLAN), is(notNullValue()));
         assertThat(node.getPropertiesByName().get(name("p1")), is(property("p1", "p1 value")));
         assertThat(node.getPropertiesByName().get(name("p2")), is(property("p2", "p2 value")));
         assertThat(node.getPropertiesByName().get(name("p12")), is(property("p12", "1", "2", "3", 4)));

Modified: trunk/extensions/dna-connector-federation/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/resources/log4j.properties	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/extensions/dna-connector-federation/src/test/resources/log4j.properties	2009-04-30 04:32:26 UTC (rev 866)
@@ -9,7 +9,7 @@
 
 # Set up the default logging to be INFO level, then override specific units
 log4j.logger.org.jboss.dna=INFO
-log4j.logger.org.jboss.dna.connector.federation.executor=INFO
+log4j.logger.org.jboss.dna.connector.federation=TRACE
 
 # Jackrabbit logging
 log4j.logger.org.apache.jackrabbit=WARN, stdout

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-04-29 14:31:49 UTC (rev 865)
+++ trunk/pom.xml	2009-04-30 04:32:26 UTC (rev 866)
@@ -135,7 +135,7 @@
 		<module>extensions/dna-common-jdbc</module> 
 		<module>extensions/dna-connector-jdbc-metadata</module> 
 		<module>dna-integration-tests</module>
-		<!--module>docs/examples/gettingstarted</module-->
+		<module>docs/examples/gettingstarted</module>
 	</modules>
 
 	<profiles>



From dna-commits at lists.jboss.org  Thu Apr 30 11:31:52 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 30 Apr 2009 11:31:52 -0400
Subject: [dna-commits] DNA SVN: r867 - in trunk:
	dna-jcr/src/main/resources/org/jboss/dna/jcr and 2 other
	directories.
Message-ID: <E1LzYEq-0004mx-Q3@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-30 11:31:52 -0400 (Thu, 30 Apr 2009)
New Revision: 867

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
Removed:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
Log:
DNA-58 Create repository configuration and component

Refactored the DnaConfiguration class to use more interfaces and to move those interfaces (and reusable implementations) into a Configurator class that uses generics.  DnaConfiguration instantiates a configurator, but also implements some of the high-level interfaces by delegating them to the wrapped configurator instance.  This allows DnaConfiguration to appear to be doing the configurating, but in reality it is the wrapped builder that is doing it.  Also, DnaConfiguration (or other components) can choose which "aspects" of configuration it is to expose by inheriting controlling which higher-level interfaces are implemented.  Since delegation is used, this works very well.

This is a more complicated design, but it is far more extensible and reusable.  A JcrConfiguration class was also created that works in the same way as DnaConfiguration, except that it uses a different type parameter (so all the interfaces return JcrConfiguration).  It thus gets to reuse all of the Configurator logic.  However, JcrConfiguration doesn't implement all of the "aspects", essentially hiding certain services (e.g., sequencers, since they don't yet work with our implementation).

This change also made a few minor corrections to the JavaDoc and visibility modifiers on the classes in the 'org.jboss.dna.jcr' package.

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ *
+ * 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;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.repository.Configurator;
+import org.jboss.dna.repository.DnaConfiguration;
+import org.jboss.dna.repository.DnaConfigurationException;
+import org.jboss.dna.repository.Configurator.ChooseClass;
+import org.jboss.dna.repository.Configurator.ConfigRepositoryDetails;
+import org.jboss.dna.repository.Configurator.MimeTypeDetectorDetails;
+import org.jboss.dna.repository.Configurator.RepositoryDetails;
+
+/**
+ * A configuration builder for a {@link JcrEngine}.  This class is an internal domain-specific language (DSL),
+ * and is designed to be used in a traditional way or in a method-chained manner:
+ * <pre>
+ * configuration.addRepository("Source1").usingClass(InMemoryRepositorySource.class).describedAs("description");
+ * configuration.addMimeTypeDetector("detector")
+ *              .usingClass(ExtensionBasedMimeTypeDetector.class)
+ *              .describedAs("default detector");
+ * configuration.addSequencer("MicrosoftDocs")
+ *              .usingClass("org.jboss.dna.sequencer.msoffice.MSOfficeMetadataSequencer")
+ *              .loadedFromClasspath()
+ *              .named("Microsoft Document sequencer")
+ *              .describedAs("Our primary sequencer for all .doc files")
+ *              .sequencingFrom("/public//(*.(doc|xml|ppt)[*]/jcr:content[@jcr:data]")
+ *              .andOutputtingTo("/documents/$1");
+ * configuration.save();
+ * </pre>
+ */
+public class JcrConfiguration
+    implements Configurator.Initializer<JcrConfiguration>, /*Configurator.SequencerConfigurator<JcrConfiguration>,*/
+    Configurator.RepositoryConfigurator<JcrConfiguration>, Configurator.MimeDetectorConfigurator<JcrConfiguration>,
+    Configurator.Builder<JcrEngine> {
+
+    private final DnaConfiguration.Builder<JcrConfiguration> builder;
+
+    /**
+     * Create a new configuration for DNA.
+     */
+    public JcrConfiguration() {
+        this(new ExecutionContext());
+    }
+
+    /**
+     * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+     * 
+     * @param context the new context, or null if a default-constructed execution context should be used
+     * @throws IllegalArgumentException if the supplied context reference is null
+     */
+    public JcrConfiguration( ExecutionContext context ) {
+        this.builder = new DnaConfiguration.Builder<JcrConfiguration>(context, this);
+    }
+
+    /**
+     * Get the execution context used by this configurator.
+     * 
+     * @return the execution context; never null
+     */
+    public final ExecutionContext getExecutionContext() {
+        return builder.getExecutionContext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.Initializer#withConfigurationRepository()
+     */
+    public ChooseClass<RepositorySource, ConfigRepositoryDetails<JcrConfiguration>> withConfigurationRepository() {
+        return builder.withConfigurationRepository();
+    }
+
+//    /**
+//     * {@inheritDoc}
+//     * 
+//     * @see org.jboss.dna.repository.Configurator.SequencerConfigurator#addSequencer(java.lang.String)
+//     */
+//    public ChooseClass<Sequencer, SequencerDetails<JcrConfiguration>> addSequencer( String id ) {
+//        CheckArg.isNotEmpty(id, "id");
+//        return builder.addSequencer(id);
+//    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
+     */
+    public ChooseClass<RepositorySource, RepositoryDetails<JcrConfiguration>> addRepository( String id ) {
+        CheckArg.isNotEmpty(id, "id");
+        return builder.addRepository(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(org.jboss.dna.graph.connector.RepositorySource)
+     */
+    public JcrConfiguration addRepository( RepositorySource source ) {
+        CheckArg.isNotNull(source, "source");
+        CheckArg.isNotEmpty(source.getName(), "source.getName()");
+        return builder.addRepository(source);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.MimeDetectorConfigurator#addMimeTypeDetector(java.lang.String)
+     */
+    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails<JcrConfiguration>> addMimeTypeDetector( String id ) {
+        CheckArg.isNotEmpty(id, "id");
+        return builder.addMimeTypeDetector(id);
+    }
+
+    /**
+     * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
+     * 
+     * @return this configuration object for method chaining purposes; never null
+     */
+    public JcrConfiguration save() {
+        return builder.save();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.Builder#build()
+     */
+    public JcrEngine build() throws DnaConfigurationException {
+        save();
+        return new JcrEngine(builder.buildDnaEngine());
+    }
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -31,7 +31,7 @@
 /**
  * 
  */
-public class JcrEmptyNodeIterator implements NodeIterator {
+class JcrEmptyNodeIterator implements NodeIterator {
 
     /**
      * {@inheritDoc}

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -31,7 +31,7 @@
 /**
  * 
  */
-public class JcrEmptyPropertyIterator implements PropertyIterator {
+class JcrEmptyPropertyIterator implements PropertyIterator {
 
     /**
      * {@inheritDoc}

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ * 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;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+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.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.repository.DnaEngine;
+import org.jboss.dna.repository.RepositoryService;
+import org.jboss.dna.repository.observation.ObservationService;
+import org.jboss.dna.repository.sequencer.SequencingService;
+
+/**
+ * The basic component that encapsulates the JBoss DNA services, including the {@link Repository} instances.
+ */
+public class JcrEngine {
+
+    private final DnaEngine dnaEngine;
+    private final Map<String, JcrRepository> repositories;
+    private final Lock repositoriesLock;
+
+    JcrEngine( DnaEngine dnaEngine ) {
+        this.dnaEngine = dnaEngine;
+        this.repositories = new HashMap<String, JcrRepository>();
+        this.repositoriesLock = new ReentrantLock();
+    }
+
+    /**
+     * Get the problems that were encountered when setting up this engine from the configuration.
+     * 
+     * @return the problems, which may be empty but will never be null
+     */
+    public Problems getProblems() {
+        return dnaEngine.getProblems();
+    }
+
+    /**
+     * Get the execution context for this engine. This context can be used to create additional (perhaps narrowed) contexts.
+     * 
+     * @return the engine's execution context; never null
+     */
+    public final ExecutionContext getExecutionContext() {
+        return dnaEngine.getExecutionContext();
+    }
+
+    /**
+     * Get the RepositorySource with the supplied name.
+     * 
+     * @param repositoryName the name of the repository (or repository source)
+     * @return the named repository source, or null if there is no such repository
+     */
+    protected final RepositorySource getRepositorySource( String repositoryName ) {
+        return dnaEngine.getRepositorySource(repositoryName);
+    }
+
+    protected final RepositoryConnectionFactory getRepositoryConnectionFactory() {
+        return dnaEngine.getRepositoryConnectionFactory();
+    }
+
+    protected final RepositoryService getRepositoryService() {
+        return dnaEngine.getRepositoryService();
+    }
+
+    protected final ObservationService getObservationService() {
+        return dnaEngine.getObservationService();
+    }
+
+    protected final SequencingService getSequencingService() {
+        return dnaEngine.getSequencingService();
+    }
+
+    /**
+     * Get the {@link Repository} implementation for the named repository.
+     * 
+     * @param repositoryName the name of the repository, which corresponds to the name of a configured {@link RepositorySource}
+     * @return the named repository instance
+     * @throws IllegalArgumentException if the repository name is null, blank or invalid
+     * @throws RepositoryException if there is no repository with the specified name
+     */
+    public final Repository getRepository( String repositoryName ) throws RepositoryException {
+        CheckArg.isNotEmpty(repositoryName, "repositoryName");
+        try {
+            repositoriesLock.lock();
+            JcrRepository repository = repositories.get(repositoryName);
+            if (repository == null) {
+                if (getRepositorySource(repositoryName) == null) {
+                    // The repository name is not a valid repository ...
+                    String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
+                    throw new RepositoryException(msg);
+                }
+                repository = doCreateJcrRepository(repositoryName);
+                repositories.put(repositoryName, repository);
+            }
+            return repository;
+        } finally {
+            repositoriesLock.unlock();
+        }
+    }
+
+    protected JcrRepository doCreateJcrRepository( String repositoryName ) {
+        RepositoryConnectionFactory connectionFactory = getRepositoryConnectionFactory();
+        Map<String, String> descriptors = null;
+        Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
+        return new JcrRepository(getExecutionContext(), connectionFactory, repositoryName, descriptors, options);
+    }
+
+    /*
+     * Lifecycle methods
+     */
+
+    public void start() {
+        dnaEngine.start();
+    }
+
+    public void shutdown() {
+        dnaEngine.shutdown();
+    }
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -43,6 +43,7 @@
     public static I18n permissionDenied;
     public static I18n repositoryMustBeConfigured;
     public static I18n sourceInUse;
+    public static I18n repositoryDoesNotExist;
 
     public static I18n noNamespaceWithPrefix;
     public static I18n noNamespaceWithUri;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -27,9 +27,9 @@
 import org.jboss.dna.graph.property.basic.BasicName;
 
 /**
- * @author Randall Hauch
+ * Lexicon of names from the standard JCR "<code>http://www.jcp.org/jcr/1.0</code>" namespace.
  */
-class JcrLexicon extends org.jboss.dna.graph.JcrLexicon {
+public class JcrLexicon extends org.jboss.dna.graph.JcrLexicon {
 
     public static final Name BASE_VERSION = new BasicName(Namespace.URI, "baseVersion");
     public static final Name CONTENT = new BasicName(Namespace.URI, "content");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -1,5 +1,31 @@
+/*
+ * 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;
 
+/**
+ * Lexicon of names from the standard JCR "<code>http://www.jcp.org/jcr/mix/1.0</code>" namespace.
+ */
 public class JcrMixLexicon extends org.jboss.dna.graph.JcrMixLexicon {
 
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -40,7 +40,7 @@
 import org.jboss.dna.graph.property.Property;
 
 /**
- * @author jverhaeg
+ * 
  */
 @NotThreadSafe
 final class JcrMultiValueProperty extends AbstractJcrProperty {
@@ -222,7 +222,7 @@
         } else {
             jcrValues = EMPTY_VALUES;
         }
-        
+
         cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues, PropertyType.STRING);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -30,7 +30,7 @@
 import net.jcip.annotations.NotThreadSafe;
 
 /**
- * @author jverhaeg
+ *
  */
 @NotThreadSafe
 final class JcrNode extends AbstractJcrNode {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -26,6 +26,9 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.basic.BasicName;
 
+/**
+ * Lexicon of names from the standard JCR "<code>http://www.jcp.org/jcr/nt/1.0</code>" namespace.
+ */
 public class JcrNtLexicon extends org.jboss.dna.graph.JcrNtLexicon {
 
     public static final Name FROZEN_NODE = new BasicName(Namespace.URI, "frozenNode");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -39,7 +39,7 @@
 import org.jboss.dna.graph.property.ValueFactories;
 
 /**
- * @author jverhaeg
+ *
  */
 final class JcrSingleValueProperty extends AbstractJcrProperty {
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -27,7 +27,7 @@
 import org.jboss.dna.graph.property.basic.BasicName;
 
 /**
- *
+ * Lexicon of names from the standard JCR "<code>http://www.jcp.org/jcr/sv/1.0</code>" namespace.
  */
 public class JcrSvLexicon {
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -45,7 +45,7 @@
 import org.jboss.dna.graph.property.ValueFactories;
 
 /**
- * @author jverhaeg
+ *
  */
 @NotThreadSafe
 final class JcrValue implements Value {

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-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -68,8 +68,7 @@
 import org.xml.sax.helpers.XMLReaderFactory;
 
 /**
- * @author John Verhaeg
- * @author Randall Hauch
+ *
  */
 @NotThreadSafe
 final class JcrWorkspace implements Workspace {

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -1,201 +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.
- *
- * 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;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.ValueFormatException;
-
-/**
- * An immutable identifier for a node definition. Although instances can be serialized, the node definitions are often stored
- * within the graph as {@link #getString() string values} on a property. These string values can later be
- * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
- * use namespace prefixes, so they are long-lasting and durable.
- * <p>
- * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
- * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
- * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
- * </p>
- * <p>
- * <quote>"Similarly, a node type may have two or more child node definitions with identical name attributes as long as they are
- * distinguishable by the required primary types attribute (the value returned by
- * NodeDefinition.getRequiredPrimaryTypes)."</quote>
- * </p>
- * <p>
- * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
- * </p>
- */
- at Immutable
-public final class NodeDefinitionId implements Serializable {
-
-    /**
-     * Current version is {@value} .
-     */
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * The string-form of the name that can be used to represent a residual property definition.
-     */
-    public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
-
-    private final Name nodeTypeName;
-    private final Name childDefinitionName;
-    private final Name[] requiredPrimaryTypes;
-    /**
-     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
-     */
-    private final String stringRepresentation;
-
-    /**
-     * Create an identifier for a node definition.
-     * 
-     * @param nodeTypeName the name of the node type on which this child node definition is defined; may not be null
-     * @param childDefinitionName the name of the child node definition, which may be a {@link #ANY_NAME residual child
-     *        definition}; may not be null
-     * @param requiredPrimaryTypes the names of the required primary types for the child node definition
-     */
-    public NodeDefinitionId( Name nodeTypeName,
-                             Name childDefinitionName,
-                             Name[] requiredPrimaryTypes ) {
-        assert nodeTypeName != null;
-        assert childDefinitionName != null;
-        this.nodeTypeName = nodeTypeName;
-        this.childDefinitionName = childDefinitionName;
-        this.requiredPrimaryTypes = requiredPrimaryTypes;
-        StringBuilder sb = new StringBuilder(this.nodeTypeName.getString());
-        sb.append('/').append(this.childDefinitionName.getString());
-        for (Name requiredPrimaryType : requiredPrimaryTypes) {
-            sb.append('/');
-            sb.append(requiredPrimaryType.getString());
-        }
-        this.stringRepresentation = sb.toString();
-    }
-
-    /**
-     * Get the name of the node type on which the child node definition is defined.
-     * 
-     * @return the node type's name; never null
-     */
-    public Name getNodeTypeName() {
-        return nodeTypeName;
-    }
-
-    /**
-     * Get the name of the child definition.
-     * 
-     * @return the child definition's name; never null
-     */
-    public Name getChildDefinitionName() {
-        return childDefinitionName;
-    }
-
-    /**
-     * @return requiredPrimaryTypes
-     */
-    public Name[] getRequiredPrimaryTypes() {
-        Name[] copy = new Name[requiredPrimaryTypes.length];
-        System.arraycopy(requiredPrimaryTypes, 0, copy, 0, requiredPrimaryTypes.length);
-        return copy;
-    }
-
-    /**
-     * Determine whether this node definition defines any named child.
-     * 
-     * @return true if this node definition allows children with any name, or false if this definition requires a particular child
-     *         name
-     */
-    public boolean allowsAnyChildName() {
-        return childDefinitionName.getLocalName().equals(ANY_NAME) && childDefinitionName.getNamespaceUri().length() == 0;
-    }
-
-    /**
-     * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
-     * 
-     * @return the string form
-     */
-    public String getString() {
-        return this.stringRepresentation;
-    }
-
-    /**
-     * Parse the supplied string for of an identifer, and return the object form for that identifier.
-     * 
-     * @param definition the {@link #getString() string form of the identifier}; may not be null
-     * @param factory the factory that should be used to create Name objects; may not be null
-     * @return the object form of the identifier; never null
-     * @throws ValueFormatException if the definition is not the valid format
-     */
-    public static NodeDefinitionId fromString( String definition,
-                                               NameFactory factory ) {
-        String[] parts = definition.split("/");
-        String nodeTypeNameString = parts[0];
-        String childDefinitionNameString = parts[1];
-        Name[] requiredPrimaryTypes = new Name[parts.length - 2];
-        for (int i = 2, j = 0; i != parts.length; ++i, ++j) {
-            requiredPrimaryTypes[j] = factory.create(parts[i]);
-        }
-        Name nodeTypeName = factory.create(nodeTypeNameString);
-        Name childDefinitionName = factory.create(childDefinitionNameString);
-        return new NodeDefinitionId(nodeTypeName, childDefinitionName, requiredPrimaryTypes);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return stringRepresentation.hashCode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals( Object obj ) {
-        if (obj == this) return true;
-        if (obj instanceof NodeDefinitionId) {
-            NodeDefinitionId that = (NodeDefinitionId)obj;
-            return this.stringRepresentation.equals(that.stringRepresentation);
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return this.stringRepresentation;
-    }
-
-}

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ *
+ * 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;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+
+/**
+ * An immutable identifier for a node definition. Although instances can be serialized, the node definitions are often stored
+ * within the graph as {@link #getString() string values} on a property. These string values can later be
+ * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
+ * use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"Similarly, a node type may have two or more child node definitions with identical name attributes as long as they are
+ * distinguishable by the required primary types attribute (the value returned by
+ * NodeDefinition.getRequiredPrimaryTypes)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
+ */
+ at Immutable
+public final class NodeDefinitionId implements Serializable {
+
+    /**
+     * Current version is {@value} .
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The string-form of the name that can be used to represent a residual property definition.
+     */
+    public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+    private final Name nodeTypeName;
+    private final Name childDefinitionName;
+    private final Name[] requiredPrimaryTypes;
+    /**
+     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+     */
+    private final String stringRepresentation;
+
+    /**
+     * Create an identifier for a node definition.
+     * 
+     * @param nodeTypeName the name of the node type on which this child node definition is defined; may not be null
+     * @param childDefinitionName the name of the child node definition, which may be a {@link #ANY_NAME residual child
+     *        definition}; may not be null
+     * @param requiredPrimaryTypes the names of the required primary types for the child node definition
+     */
+    public NodeDefinitionId( Name nodeTypeName,
+                             Name childDefinitionName,
+                             Name[] requiredPrimaryTypes ) {
+        assert nodeTypeName != null;
+        assert childDefinitionName != null;
+        this.nodeTypeName = nodeTypeName;
+        this.childDefinitionName = childDefinitionName;
+        this.requiredPrimaryTypes = requiredPrimaryTypes;
+        StringBuilder sb = new StringBuilder(this.nodeTypeName.getString());
+        sb.append('/').append(this.childDefinitionName.getString());
+        for (Name requiredPrimaryType : requiredPrimaryTypes) {
+            sb.append('/');
+            sb.append(requiredPrimaryType.getString());
+        }
+        this.stringRepresentation = sb.toString();
+    }
+
+    /**
+     * Get the name of the node type on which the child node definition is defined.
+     * 
+     * @return the node type's name; never null
+     */
+    public Name getNodeTypeName() {
+        return nodeTypeName;
+    }
+
+    /**
+     * Get the name of the child definition.
+     * 
+     * @return the child definition's name; never null
+     */
+    public Name getChildDefinitionName() {
+        return childDefinitionName;
+    }
+
+    /**
+     * @return requiredPrimaryTypes
+     */
+    public Name[] getRequiredPrimaryTypes() {
+        Name[] copy = new Name[requiredPrimaryTypes.length];
+        System.arraycopy(requiredPrimaryTypes, 0, copy, 0, requiredPrimaryTypes.length);
+        return copy;
+    }
+
+    /**
+     * Determine whether this node definition defines any named child.
+     * 
+     * @return true if this node definition allows children with any name, or false if this definition requires a particular child
+     *         name
+     */
+    public boolean allowsAnyChildName() {
+        return childDefinitionName.getLocalName().equals(ANY_NAME) && childDefinitionName.getNamespaceUri().length() == 0;
+    }
+
+    /**
+     * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
+     * 
+     * @return the string form
+     */
+    public String getString() {
+        return this.stringRepresentation;
+    }
+
+    /**
+     * Parse the supplied string for of an identifer, and return the object form for that identifier.
+     * 
+     * @param definition the {@link #getString() string form of the identifier}; may not be null
+     * @param factory the factory that should be used to create Name objects; may not be null
+     * @return the object form of the identifier; never null
+     * @throws ValueFormatException if the definition is not the valid format
+     */
+    public static NodeDefinitionId fromString( String definition,
+                                               NameFactory factory ) {
+        String[] parts = definition.split("/");
+        String nodeTypeNameString = parts[0];
+        String childDefinitionNameString = parts[1];
+        Name[] requiredPrimaryTypes = new Name[parts.length - 2];
+        for (int i = 2, j = 0; i != parts.length; ++i, ++j) {
+            requiredPrimaryTypes[j] = factory.create(parts[i]);
+        }
+        Name nodeTypeName = factory.create(nodeTypeNameString);
+        Name childDefinitionName = factory.create(childDefinitionNameString);
+        return new NodeDefinitionId(nodeTypeName, childDefinitionName, requiredPrimaryTypes);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return stringRepresentation.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof NodeDefinitionId) {
+            NodeDefinitionId that = (NodeDefinitionId)obj;
+            return this.stringRepresentation.equals(that.stringRepresentation);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return this.stringRepresentation;
+    }
+
+}

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -28,7 +28,7 @@
 /**
  * Enumeration of possible behaviors for on-parent-version setting of properties and child nodes in JCR specification.
  */
-public enum OnParentVersionBehavior {
+enum OnParentVersionBehavior {
     /** @see OnParentVersionAction#ABORT */
     ABORT(OnParentVersionAction.ABORT, OnParentVersionAction.ACTIONNAME_ABORT),
     /** @see OnParentVersionAction#COMPUTE */

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -1,216 +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.
- *
- * 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;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import javax.jcr.PropertyType;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.ValueFormatException;
-
-/**
- * An immutable identifier for a property definition. Although instances can be serialized, the property definitions are often
- * stored within the graph as {@link #getString() string values} on a property. These string values can later be
- * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
- * use namespace prefixes, so they are long-lasting and durable.
- * <p>
- * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
- * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
- * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
- * </p>
- * <p>
- * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
- * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
- * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
- * PropertyDefinition.isMultiple)."</quote>
- * </p>
- * <p>
- * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
- * </p>
- */
- at Immutable
-public final class PropertyDefinitionId implements Serializable {
-
-    /**
-     * Current version is {@value} .
-     */
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * The string-form of the name that can be used to represent a residual property definition.
-     */
-    public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
-
-    private final Name nodeTypeName;
-    private final Name propertyDefinitionName;
-    private final int propertyType;
-    private final boolean allowsMultiple;
-    /**
-     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
-     */
-    private final String stringRepresentation;
-
-    /**
-     * Create a new identifier for a property definition.
-     * 
-     * @param nodeTypeName the name of the node type; may not be null
-     * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may
-     *        not be null
-     * @param propertyType the required property type for the definition; must be a valid {@link PropertyType} value
-     * @param allowsMultiple true if the property definition should allow multiple values, or false if it is a single-value
-     *        property definition
-     */
-    public PropertyDefinitionId( Name nodeTypeName,
-                                 Name propertyDefinitionName,
-                                 int propertyType,
-                                 boolean allowsMultiple ) {
-        this.nodeTypeName = nodeTypeName;
-        this.propertyDefinitionName = propertyDefinitionName;
-        this.propertyType = propertyType;
-        this.allowsMultiple = allowsMultiple;
-        this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
-                                    + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
-    }
-
-    /**
-     * Get the name of the node type on which the property definition is defined
-     * 
-     * @return the node type's name; may not be null
-     */
-    public Name getNodeTypeName() {
-        return nodeTypeName;
-    }
-
-    /**
-     * Get the name of the property definition.
-     * 
-     * @return the property definition's name; never null
-     */
-    public Name getPropertyDefinitionName() {
-        return propertyDefinitionName;
-    }
-
-    /**
-     * Get the required property type
-     * 
-     * @return the property type; always a valid {@link PropertyType} value
-     */
-    public int getPropertyType() {
-        return propertyType;
-    }
-
-    /**
-     * Return whether the property definition allows multiple values.
-     * 
-     * @return true if the property definition allows multiple values, or false if it is a single-value property definition
-     */
-    public boolean allowsMultiple() {
-        return allowsMultiple;
-    }
-
-    /**
-     * Determine whether this property definition allows properties with any name.
-     * 
-     * @return true if this node definition allows properties with any name, or false if this definition requires a particular
-     *         property name
-     */
-    public boolean allowsAnyChildName() {
-        return propertyDefinitionName.getLocalName().equals(ANY_NAME) && propertyDefinitionName.getNamespaceUri().length() == 0;
-    }
-
-    /**
-     * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
-     * 
-     * @return the string form
-     */
-    public String getString() {
-        return this.stringRepresentation;
-    }
-
-    /**
-     * Parse the supplied string for of an identifer, and return the object form for that identifier.
-     * 
-     * @param definition the {@link #getString() string form of the identifier}; may not be null
-     * @param factory the factory that should be used to create Name objects; may not be null
-     * @return the object form of the identifier; never null
-     * @throws ValueFormatException if the definition is not the valid format
-     */
-    public static PropertyDefinitionId fromString( String definition,
-                                                   NameFactory factory ) {
-        String[] parts = definition.split("/");
-        String nodeTypeNameString = parts[0];
-        String propertyDefinitionNameString = parts[1];
-        Name nodeTypeName = factory.create(nodeTypeNameString);
-        Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
-        int propertyType = PropertyType.valueFromName(parts[2]);
-        boolean allowsMultiple = parts[3].charAt(0) == '*';
-        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, allowsMultiple);
-    }
-
-    public PropertyDefinitionId asSingleValued() {
-        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, false);
-    }
-
-    public PropertyDefinitionId asMultiValued() {
-        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, true);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return this.stringRepresentation.hashCode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals( Object obj ) {
-        if (obj == this) return true;
-        if (obj instanceof PropertyDefinitionId) {
-            PropertyDefinitionId that = (PropertyDefinitionId)obj;
-            return this.stringRepresentation.equals(that.stringRepresentation);
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return this.stringRepresentation;
-    }
-
-}

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -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.
+ *
+ * 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;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import javax.jcr.PropertyType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+
+/**
+ * An immutable identifier for a property definition. Although instances can be serialized, the property definitions are often
+ * stored within the graph as {@link #getString() string values} on a property. These string values can later be
+ * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
+ * use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
+ * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
+ * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
+ * PropertyDefinition.isMultiple)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
+ */
+ at Immutable
+public final class PropertyDefinitionId implements Serializable {
+
+    /**
+     * Current version is {@value} .
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The string-form of the name that can be used to represent a residual property definition.
+     */
+    public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+    private final Name nodeTypeName;
+    private final Name propertyDefinitionName;
+    private final int propertyType;
+    private final boolean allowsMultiple;
+    /**
+     * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+     */
+    private final String stringRepresentation;
+
+    /**
+     * Create a new identifier for a property definition.
+     * 
+     * @param nodeTypeName the name of the node type; may not be null
+     * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may
+     *        not be null
+     * @param propertyType the required property type for the definition; must be a valid {@link PropertyType} value
+     * @param allowsMultiple true if the property definition should allow multiple values, or false if it is a single-value
+     *        property definition
+     */
+    public PropertyDefinitionId( Name nodeTypeName,
+                                 Name propertyDefinitionName,
+                                 int propertyType,
+                                 boolean allowsMultiple ) {
+        this.nodeTypeName = nodeTypeName;
+        this.propertyDefinitionName = propertyDefinitionName;
+        this.propertyType = propertyType;
+        this.allowsMultiple = allowsMultiple;
+        this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
+                                    + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
+    }
+
+    /**
+     * Get the name of the node type on which the property definition is defined
+     * 
+     * @return the node type's name; may not be null
+     */
+    public Name getNodeTypeName() {
+        return nodeTypeName;
+    }
+
+    /**
+     * Get the name of the property definition.
+     * 
+     * @return the property definition's name; never null
+     */
+    public Name getPropertyDefinitionName() {
+        return propertyDefinitionName;
+    }
+
+    /**
+     * Get the required property type
+     * 
+     * @return the property type; always a valid {@link PropertyType} value
+     */
+    public int getPropertyType() {
+        return propertyType;
+    }
+
+    /**
+     * Return whether the property definition allows multiple values.
+     * 
+     * @return true if the property definition allows multiple values, or false if it is a single-value property definition
+     */
+    public boolean allowsMultiple() {
+        return allowsMultiple;
+    }
+
+    /**
+     * Determine whether this property definition allows properties with any name.
+     * 
+     * @return true if this node definition allows properties with any name, or false if this definition requires a particular
+     *         property name
+     */
+    public boolean allowsAnyChildName() {
+        return propertyDefinitionName.getLocalName().equals(ANY_NAME) && propertyDefinitionName.getNamespaceUri().length() == 0;
+    }
+
+    /**
+     * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
+     * 
+     * @return the string form
+     */
+    public String getString() {
+        return this.stringRepresentation;
+    }
+
+    /**
+     * Parse the supplied string for of an identifer, and return the object form for that identifier.
+     * 
+     * @param definition the {@link #getString() string form of the identifier}; may not be null
+     * @param factory the factory that should be used to create Name objects; may not be null
+     * @return the object form of the identifier; never null
+     * @throws ValueFormatException if the definition is not the valid format
+     */
+    public static PropertyDefinitionId fromString( String definition,
+                                                   NameFactory factory ) {
+        String[] parts = definition.split("/");
+        String nodeTypeNameString = parts[0];
+        String propertyDefinitionNameString = parts[1];
+        Name nodeTypeName = factory.create(nodeTypeNameString);
+        Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
+        int propertyType = PropertyType.valueFromName(parts[2]);
+        boolean allowsMultiple = parts[3].charAt(0) == '*';
+        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, allowsMultiple);
+    }
+
+    public PropertyDefinitionId asSingleValued() {
+        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, false);
+    }
+
+    public PropertyDefinitionId asMultiValued() {
+        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return this.stringRepresentation.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof PropertyDefinitionId) {
+            PropertyDefinitionId that = (PropertyDefinitionId)obj;
+            return this.stringRepresentation.equals(that.stringRepresentation);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return this.stringRepresentation;
+    }
+
+}

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -1,111 +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.
- *
- * 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;
-
-import java.util.UUID;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.util.HashCode;
-import org.jboss.dna.graph.property.Name;
-
-/**
- * An immutable identifier for a property, often used to reference information a property held within the {@link SessionCache}.
- */
- at Immutable
-public final class PropertyId {
-
-    private final UUID nodeId;
-    private final Name propertyName;
-    private final int hc;
-
-    /**
-     * Create a new property identifier.
-     * 
-     * @param nodeId the UUID of the node that owns the property being reference; may not be null
-     * @param propertyName the name of the property being referenced; may not be null
-     */
-    public PropertyId( UUID nodeId,
-                       Name propertyName ) {
-        assert nodeId != null;
-        assert propertyName != null;
-        this.nodeId = nodeId;
-        this.propertyName = propertyName;
-        this.hc = HashCode.compute(this.nodeId, this.propertyName);
-    }
-
-    /**
-     * Get the UUID of the node that owns the property.
-     * 
-     * @return the node's UUID; never null
-     */
-    public UUID getNodeId() {
-        return nodeId;
-    }
-
-    /**
-     * Get the name of the property.
-     * 
-     * @return the property name; never null
-     */
-    public Name getPropertyName() {
-        return propertyName;
-    }
-
-    /**
-     * {@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 PropertyId) {
-            PropertyId that = (PropertyId)obj;
-            if (this.hc != that.hc) return false;
-            if (!this.nodeId.equals(that.nodeId)) return false;
-            return this.propertyName.equals(that.propertyName);
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return this.nodeId.toString() + '@' + this.propertyName.toString();
-    }
-
-}

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ *
+ * 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;
+
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * An immutable identifier for a property, often used to reference information a property held within the {@link SessionCache}.
+ */
+ at Immutable
+public final class PropertyId {
+
+    private final UUID nodeId;
+    private final Name propertyName;
+    private final int hc;
+
+    /**
+     * Create a new property identifier.
+     * 
+     * @param nodeId the UUID of the node that owns the property being reference; may not be null
+     * @param propertyName the name of the property being referenced; may not be null
+     */
+    public PropertyId( UUID nodeId,
+                       Name propertyName ) {
+        assert nodeId != null;
+        assert propertyName != null;
+        this.nodeId = nodeId;
+        this.propertyName = propertyName;
+        this.hc = HashCode.compute(this.nodeId, this.propertyName);
+    }
+
+    /**
+     * Get the UUID of the node that owns the property.
+     * 
+     * @return the node's UUID; never null
+     */
+    public UUID getNodeId() {
+        return nodeId;
+    }
+
+    /**
+     * Get the name of the property.
+     * 
+     * @return the property name; never null
+     */
+    public Name getPropertyName() {
+        return propertyName;
+    }
+
+    /**
+     * {@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 PropertyId) {
+            PropertyId that = (PropertyId)obj;
+            if (this.hc != that.hc) return false;
+            if (!this.nodeId.equals(that.nodeId)) return false;
+            return this.propertyName.equals(that.propertyName);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return this.nodeId.toString() + '@' + this.propertyName.toString();
+    }
+
+}

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -40,7 +40,7 @@
  * </p>
  */
 @NotThreadSafe
-public class SelfClosingInputStream extends InputStream {
+class SelfClosingInputStream extends InputStream {
 
     private final Binary binary;
     private final InputStream stream;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -121,7 +121,7 @@
  * </p>
  */
 @ThreadSafe
-public class SessionCache {
+class SessionCache {
 
     /**
      * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-04-30 15:31:52 UTC (rev 867)
@@ -32,7 +32,8 @@
 pathNotFoundRelativeTo = No item exists at path {0} relative to {1} in workspace "{2}"
 permissionDenied = Permission denied to perform actions "{1}" on path {0}.
 repositoryMustBeConfigured = DNA repositories must be configured with either a repository source factory or a repository source.
-sourceInUse = All sessions must end before a new repository source can be set.
+sourceInUse = All sessions must end before a new repository source can be set
+repositoryDoesNotExist = There is no repository named "{0}"
 
 noNamespaceWithPrefix = There is no namespace with prefix "{0}"
 noNamespaceWithUri = There is no namespace with URI "{0}"

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,1051 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.component.ClassLoaderFactory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.Inflector;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Reflection;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathExpression;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.RootPath;
+import org.jboss.dna.repository.sequencer.Sequencer;
+
+/**
+ * @param <BuilderType>
+ */
+public abstract class Configurator<BuilderType> {
+
+    /**
+     * Interface used to configure a sequencer.
+     * 
+     * @param <ReturnType> the type of interface to return after the sequencer's configuration is completed
+     */
+    public interface SequencerConfigurator<ReturnType> {
+
+        /**
+         * Add a new {@link Sequencer sequencer} to this configuration. The new sequencer will have the supplied name, and if the
+         * name of an existing sequencer is used, this will replace the existing sequencer configuration.
+         * 
+         * @param id the identifier of the new sequencer
+         * @return the interface for choosing the class, which returns the interface used to configure the sequencer; never null
+         * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
+         */
+        public ChooseClass<Sequencer, SequencerDetails<ReturnType>> addSequencer( final String id );
+    }
+
+    /**
+     * Interface used to initialize the configurator to use a specific repository containing configuration information.
+     * 
+     * @param <ReturnType> the configurator type returned after the configuration repository is defined
+     */
+    public interface Initializer<ReturnType> {
+        /**
+         * Specify that this configuration should use a particular {@link RepositorySource} for its configuration repository. By
+         * default each configuration uses an internal transient repository for its configuration, but using this method will make
+         * the configuration use a different repository (that is perhaps shared with other processes).
+         * 
+         * @return the interface for choosing the class, which returns the interface used to configure the repository source that
+         *         will be used for the configuration repository; never null
+         */
+        public ChooseClass<RepositorySource, ConfigRepositoryDetails<ReturnType>> withConfigurationRepository();
+    }
+
+    /**
+     * Interface used to configure a repository source.
+     * 
+     * @param <ReturnType> the type of interface to return after the repository source's configuration is completed
+     */
+    public interface RepositoryConfigurator<ReturnType> {
+        /**
+         * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name,
+         * and if the name of an existing repository is used, this will replace the existing repository configuration.
+         * 
+         * @param id the id of the new repository that is to be added
+         * @return the interface for choosing the class, which returns the interface used to configure the repository source;
+         *         never null
+         * @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
+         * @see #addRepository(RepositorySource)
+         */
+        public ChooseClass<RepositorySource, RepositoryDetails<ReturnType>> addRepository( final String id );
+
+        /**
+         * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name,
+         * and if the name of an existing repository is used, this will replace the existing repository configuration.
+         * 
+         * @param source the {@link RepositorySource} instance that should be used
+         * @return this configuration object, for method-chaining purposes
+         * @throws IllegalArgumentException if the repository source reference is null
+         * @see #addRepository(String)
+         */
+        public ReturnType addRepository( RepositorySource source );
+    }
+
+    /**
+     * Interface used to configure a MIME type detector.
+     * 
+     * @param <ReturnType> the type of interface to return after the detector's configuration is completed
+     */
+    public interface MimeDetectorConfigurator<ReturnType> {
+        /**
+         * Add a new {@link MimeTypeDetector MIME type detector} to this configuration. The new detector will have the supplied
+         * name, and if the name of an existing detector is used, this will replace the existing detector configuration.
+         * 
+         * @param id the id of the new detector
+         * @return the interface for choosing the class, which returns the interface used to configure the detector; never null
+         * @throws IllegalArgumentException if the detector name is null, empty, or otherwise invalid
+         */
+        public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails<ReturnType>> addMimeTypeDetector( final String id );
+    }
+
+    /**
+     * Interface used to build the configured component.
+     * 
+     * @param <ReturnType> the type of component that this configuration builds
+     */
+    public interface Builder<ReturnType> {
+        /**
+         * Complete this configuration and create the corresponding engine.
+         * 
+         * @return the new engine configured by this instance
+         * @throws DnaConfigurationException if the engine cannot be created from this configuration.
+         */
+        public ReturnType build() throws DnaConfigurationException;
+    }
+
+    /**
+     * Interface used to configure a {@link RepositorySource repository}.
+     * 
+     * @param <ReturnType>
+     */
+    public interface RepositoryDetails<ReturnType>
+        extends SetName<RepositoryDetails<ReturnType>>, SetDescription<RepositoryDetails<ReturnType>>,
+        SetProperties<RepositoryDetails<ReturnType>>, And<ReturnType> {
+    }
+
+    /**
+     * Interface used to define the configuration repository.
+     * 
+     * @param <ReturnType>
+     */
+    public interface ConfigRepositoryDetails<ReturnType>
+        extends SetDescription<ConfigRepositoryDetails<ReturnType>>, SetProperties<ConfigRepositoryDetails<ReturnType>>,
+        And<ReturnType> {
+        /**
+         * Specify the path under which the configuration content is to be found. This path is assumed to be "/" by default.
+         * 
+         * @param path the path to the configuration content in the configuration source; may not be null
+         * @return this instance for method chaining purposes; never null
+         */
+        public ConfigRepositoryDetails<ReturnType> under( String path );
+    }
+
+    /**
+     * Interface used to configure a {@link Sequencer sequencer}.
+     * 
+     * @param <ReturnType>
+     */
+    public interface SequencerDetails<ReturnType>
+        extends SetName<SequencerDetails<ReturnType>>, SetDescription<SequencerDetails<ReturnType>>, And<ReturnType> {
+
+        /**
+         * Specify the input {@link PathExpression path expression} represented as a string, which determines when this sequencer
+         * will be executed.
+         * 
+         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
+         *        sequencer
+         * @return the interface used to specify the output path expression; never null
+         */
+        PathExpressionOutput<ReturnType> sequencingFrom( String inputPathExpression );
+
+        /**
+         * Specify the input {@link PathExpression path expression}, which determines when this sequencer will be executed.
+         * 
+         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
+         *        sequencer
+         * @return the interface used to continue specifying the configuration of the sequencer
+         */
+        SequencerDetails<ReturnType> sequencingFrom( PathExpression inputPathExpression );
+    }
+
+    /**
+     * Interface used to specify the output path expression for a
+     * {@link Configurator.SequencerDetails#sequencingFrom(PathExpression) sequencer configuration}.
+     * 
+     * @param <ReturnType>
+     */
+    public interface PathExpressionOutput<ReturnType> {
+        /**
+         * Specify the output {@link PathExpression path expression}, which determines where this sequencer's output will be
+         * placed.
+         * 
+         * @param outputExpression the path expression for the location(s) where output generated by the sequencer is to be placed
+         * @return the interface used to continue specifying the configuration of the sequencer
+         */
+        SequencerDetails<ReturnType> andOutputtingTo( String outputExpression );
+    }
+
+    /**
+     * Interface used to configure a {@link MimeTypeDetector MIME type detector}.
+     * 
+     * @param <ReturnType>
+     */
+    public interface MimeTypeDetectorDetails<ReturnType>
+        extends SetName<MimeTypeDetectorDetails<ReturnType>>, SetDescription<MimeTypeDetectorDetails<ReturnType>>,
+        SetProperties<MimeTypeDetectorDetails<ReturnType>>, And<ReturnType> {
+    }
+
+    /**
+     * Interface for configuring the JavaBean-style properties of an object.
+     * 
+     * @param <ReturnType> the interface returned after the property has been set.
+     * @author Randall Hauch
+     */
+    public interface SetProperties<ReturnType> {
+        /**
+         * Specify the name of the JavaBean-style property that is to be set. The value may be set using the interface returned by
+         * this method.
+         * 
+         * @param beanPropertyName the name of the JavaBean-style property (e.g., "retryLimit")
+         * @return the interface used to set the value for the property; never null
+         */
+        PropertySetter<ReturnType> with( String beanPropertyName );
+    }
+
+    /**
+     * The interface used to set the value for a JavaBean-style property.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     * @author Randall Hauch
+     * @see Configurator.SetProperties#with(String)
+     */
+    public interface PropertySetter<ReturnType> {
+        /**
+         * Set the property value to an integer.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( int value );
+
+        /**
+         * Set the property value to a long number.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( long value );
+
+        /**
+         * Set the property value to a short.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( short value );
+
+        /**
+         * Set the property value to a boolean.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( boolean value );
+
+        /**
+         * Set the property value to a float.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( float value );
+
+        /**
+         * Set the property value to a double.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( double value );
+
+        /**
+         * Set the property value to a string.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( String value );
+
+        /**
+         * Set the property value to an object.
+         * 
+         * @param value the new value for the property
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType setTo( Object value );
+    }
+
+    /**
+     * The interface used to configure the class used for a component.
+     * 
+     * @param <ComponentClassType> the class or interface that the component is to implement
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface ChooseClass<ComponentClassType, ReturnType> {
+
+        /**
+         * Specify the name of the class that should be instantiated for the instance. The classpath information will need to be
+         * defined using the returned interface.
+         * 
+         * @param classname the name of the class that should be instantiated
+         * @return the interface used to define the classpath information; never null
+         * @throws IllegalArgumentException if the class name is null, empty, blank, or not a valid class name
+         */
+        LoadedFrom<ReturnType> usingClass( String classname );
+
+        /**
+         * Specify the class that should be instantiated for the instance. Because the class is already available to this class
+         * loader, there is no need to specify the classloader information.
+         * 
+         * @param clazz the class that should be instantiated
+         * @return the next component to continue configuration; never null
+         * @throws DnaConfigurationException if the class could not be accessed and instantiated (if needed)
+         * @throws IllegalArgumentException if the class reference is null
+         */
+        ReturnType usingClass( Class<? extends ComponentClassType> clazz );
+    }
+
+    /**
+     * The interface used to set a description on a component.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface SetDescription<ReturnType> {
+        /**
+         * Specify the description of this component.
+         * 
+         * @param description the description; may be null or empty
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType describedAs( String description );
+    }
+
+    /**
+     * The interface used to set a human readable name on a component.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface SetName<ReturnType> {
+        /**
+         * Specify the human-readable name for this component.
+         * 
+         * @param description the description; may be null or empty
+         * @return the next component to continue configuration; never null
+         */
+        ReturnType named( String description );
+    }
+
+    /**
+     * Interface for specifying from where the component's class is to be loaded.
+     * 
+     * @param <ReturnType> the interface returned from these methods
+     */
+    public interface LoadedFrom<ReturnType> {
+        /**
+         * Specify the names of the classloaders that form the classpath for the component, from which the component's class (and
+         * its dependencies) can be loaded. The names correspond to the names supplied to the
+         * {@link ExecutionContext#getClassLoader(String...)} methods.
+         * 
+         * @param classPathNames the names for the classloaders, as passed to the {@link ClassLoaderFactory} implementation (e.g.,
+         *        the {@link ExecutionContext}).
+         * @return the next component to continue configuration; never null
+         * @see #loadedFromClasspath()
+         * @see ExecutionContext#getClassLoader(String...)
+         */
+        ReturnType loadedFrom( String... classPathNames );
+
+        /**
+         * Specify that the component (and its dependencies) will be found on the current (or
+         * {@link Thread#getContextClassLoader() current context}) classloader.
+         * 
+         * @return the next component to continue configuration; never null
+         * @see #loadedFrom(String...)
+         * @see ExecutionContext#getClassLoader(String...)
+         */
+        ReturnType loadedFromClasspath();
+    }
+
+    /**
+     * Continue with another aspect of configuration.
+     * 
+     * @param <ReturnType>
+     */
+    public interface And<ReturnType> {
+
+        /**
+         * Return a reference to the next configuration interface for additional operations.
+         * 
+         * @return a reference to the next configuration interface
+         */
+        ReturnType and();
+    }
+
+    protected final BuilderType builder;
+    protected final ExecutionContext context;
+    protected ConfigurationRepository configurationSource;
+    private Graph graph;
+    private Graph.Batch batch;
+
+    /**
+     * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+     * 
+     * @param context the new context, or null if a default-constructed execution context should be used
+     * @param builder the builder
+     * @throws IllegalArgumentException if the supplied context reference is null
+     */
+    protected Configurator( ExecutionContext context,
+                            BuilderType builder ) {
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(builder, "builder");
+        this.context = context;
+        this.builder = builder;
+
+        // Set up the default configuration repository ...
+        this.configurationSource = createDefaultConfigurationSource();
+    }
+
+    /**
+     * Method that is used to set up the default configuration repository source. By default, this method sets up the
+     * {@link InMemoryRepositorySource} loaded from the classpath.
+     * 
+     * @return the default repository source
+     */
+    protected ConfigurationRepository createDefaultConfigurationSource() {
+        InMemoryRepositorySource defaultSource = new InMemoryRepositorySource();
+        defaultSource.setName("Configuration");
+        ConfigurationRepository result = new ConfigurationRepository(defaultSource, "Configuration Repository", null);
+        return result;
+    }
+
+    /**
+     * Get the execution context used by this configurator.
+     * 
+     * @return the execution context; never null
+     */
+    public final ExecutionContext getExecutionContext() {
+        return this.context;
+    }
+
+    protected final PathFactory pathFactory() {
+        return getExecutionContext().getValueFactories().getPathFactory();
+    }
+
+    /**
+     * Get the graph containing the configuration information.
+     * 
+     * @return the configuration repository graph; never null
+     * @see #graph()
+     */
+    protected final Graph graph() {
+        if (this.graph == null) {
+            this.graph = Graph.create(configurationSource.getRepositorySource(), context);
+        }
+        return this.graph;
+    }
+
+    /**
+     * Get the graph batch that can be used to change the configuration, where the changes are enqueued until {@link #save()
+     * saved}.
+     * 
+     * @return the latest batch for changes to the configuration repository; never null
+     * @see #graph()
+     */
+    protected final Graph.Batch configuration() {
+        if (this.batch == null) {
+            this.batch = graph().batch();
+        }
+        return this.batch;
+    }
+
+    /**
+     * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
+     * 
+     * @return this configuration object for method chaining purposes; never null
+     */
+    public BuilderType save() {
+        if (this.batch != null) {
+            this.batch.execute();
+            this.batch = this.graph.batch();
+        }
+        return this.builder;
+    }
+
+    protected abstract Name nameFor( String name );
+
+    protected Path createOrReplaceNode( Path parentPath,
+                                        String id ) {
+        Path path = pathFactory().create(parentPath, id);
+        configuration().create(path).with(DnaLexicon.READABLE_NAME, id).and();
+        return path;
+    }
+
+    protected void recordBeanPropertiesInGraph( Path path,
+                                                Object javaBean ) {
+        Reflection reflector = new Reflection(javaBean.getClass());
+        for (String propertyName : reflector.findGetterPropertyNames()) {
+            Object value;
+            try {
+                value = reflector.invokeGetterMethodOnTarget(propertyName, javaBean);
+                if (value == null) continue;
+                propertyName = Inflector.getInstance().lowerCamelCase(propertyName);
+                configuration().set(nameFor(propertyName)).to(value).on(path);
+            } catch (ValueFormatException err) {
+                throw err;
+            } catch (Throwable err) {
+                // Unable to call getter and set property
+            }
+        }
+    }
+
+    protected class ConfigurationRepositoryClassChooser<ReturnType>
+        implements ChooseClass<RepositorySource, ConfigRepositoryDetails<ReturnType>> {
+
+        private final ReturnType returnObject;
+
+        protected ConfigurationRepositoryClassChooser( ReturnType returnObject ) {
+            assert returnObject != null;
+            this.returnObject = returnObject;
+        }
+
+        public LoadedFrom<ConfigRepositoryDetails<ReturnType>> usingClass( final String className ) {
+            return new LoadedFrom<ConfigRepositoryDetails<ReturnType>>() {
+                @SuppressWarnings( "unchecked" )
+                public ConfigRepositoryDetails loadedFrom( String... classpath ) {
+                    ClassLoader classLoader = getExecutionContext().getClassLoader(classpath);
+                    Class<? extends RepositorySource> clazz = null;
+                    try {
+                        clazz = (Class<? extends RepositorySource>)classLoader.loadClass(className);
+                    } catch (ClassNotFoundException err) {
+                        throw new DnaConfigurationException(RepositoryI18n.unableToLoadClassUsingClasspath.text(className,
+                                                                                                                classpath));
+                    }
+                    return usingClass(clazz);
+                }
+
+                @SuppressWarnings( "unchecked" )
+                public ConfigRepositoryDetails loadedFromClasspath() {
+                    Class<? extends RepositorySource> clazz = null;
+                    try {
+                        clazz = (Class<? extends RepositorySource>)Class.forName(className);
+                    } catch (ClassNotFoundException err) {
+                        throw new DnaConfigurationException(RepositoryI18n.unableToLoadClass.text(className));
+                    }
+                    return usingClass(clazz);
+                }
+            };
+        }
+
+        public ConfigRepositoryDetails<ReturnType> usingClass( Class<? extends RepositorySource> repositorySource ) {
+            try {
+                Configurator.this.configurationSource = new ConfigurationRepository(repositorySource.newInstance());
+            } catch (InstantiationException err) {
+                I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
+                throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
+            } catch (IllegalAccessException err) {
+                I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
+                throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
+            }
+            return new ConfigurationSourceDetails<ReturnType>(returnObject);
+        }
+    }
+
+    protected class ConfigurationSourceDetails<ReturnType> implements ConfigRepositoryDetails<ReturnType> {
+        private final ReturnType returnObject;
+
+        protected ConfigurationSourceDetails( ReturnType returnObject ) {
+            assert returnObject != null;
+            this.returnObject = returnObject;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
+         */
+        public ConfigRepositoryDetails<ReturnType> describedAs( String description ) {
+            Configurator.this.configurationSource = Configurator.this.configurationSource.with(description);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<ConfigRepositoryDetails<ReturnType>> with( String propertyName ) {
+            return new BeanPropertySetter<ConfigRepositoryDetails<ReturnType>>(
+                                                                               Configurator.this.configurationSource.getRepositorySource(),
+                                                                               propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.ConfigRepositoryDetails#under(java.lang.String)
+         */
+        public ConfigRepositoryDetails<ReturnType> under( String path ) {
+            CheckArg.isNotNull(path, "path");
+            Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(path);
+            Configurator.this.configurationSource = Configurator.this.configurationSource.with(newPath);
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.And#and()
+         */
+        public ReturnType and() {
+            return returnObject;
+        }
+    }
+
+    /**
+     * Reusable implementation of {@link Configurator.ChooseClass} that can be used to obtain the name of a class and how its
+     * class loader is defined.
+     * 
+     * @param <ComponentClass> the type of the component that is being chosen
+     * @param <ReturnType> the interface that should be returned when the class name and classpath have been chosen.
+     */
+    protected class ClassChooser<ComponentClass, ReturnType> implements Configurator.ChooseClass<ComponentClass, ReturnType> {
+        protected final Path pathOfComponentNode;
+        protected final ReturnType returnObject;
+
+        protected ClassChooser( Path pathOfComponentNode,
+                                ReturnType returnObject ) {
+            assert pathOfComponentNode != null;
+            assert returnObject != null;
+            this.pathOfComponentNode = pathOfComponentNode;
+            this.returnObject = returnObject;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see Configurator.ChooseClass#usingClass(java.lang.String)
+         */
+        public Configurator.LoadedFrom<ReturnType> usingClass( final String classname ) {
+            CheckArg.isNotEmpty(classname, "classname");
+            configuration().set(DnaLexicon.CLASSNAME).to(classname).on(pathOfComponentNode);
+            return new Configurator.LoadedFrom<ReturnType>() {
+                public ReturnType loadedFromClasspath() {
+                    return returnObject;
+                }
+
+                public ReturnType loadedFrom( String... classpath ) {
+                    CheckArg.isNotEmpty(classpath, "classpath");
+                    if (classpath.length == 1 && classpath[0] != null) {
+                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0]).on(pathOfComponentNode);
+                    } else {
+                        Object[] remaining = new String[classpath.length - 1];
+                        System.arraycopy(classpath, 1, remaining, 0, remaining.length);
+                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0], remaining).on(pathOfComponentNode);
+                    }
+                    return returnObject;
+                }
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see Configurator.ChooseClass#usingClass(java.lang.Class)
+         */
+        public ReturnType usingClass( Class<? extends ComponentClass> clazz ) {
+            CheckArg.isNotNull(clazz, "clazz");
+            return usingClass(clazz.getName()).loadedFromClasspath();
+        }
+    }
+
+    /**
+     * Reusable implementation of {@link Configurator.PropertySetter} that sets the JavaBean-style property using reflection.
+     * 
+     * @param <ReturnType>
+     */
+    protected class BeanPropertySetter<ReturnType> implements Configurator.PropertySetter<ReturnType> {
+        private final Object javaBean;
+        private final String beanPropertyName;
+        private final ReturnType returnObject;
+
+        protected BeanPropertySetter( Object javaBean,
+                                      String beanPropertyName,
+                                      ReturnType returnObject ) {
+            assert javaBean != null;
+            assert beanPropertyName != null;
+            assert returnObject != null;
+            this.javaBean = javaBean;
+            this.beanPropertyName = beanPropertyName;
+            this.returnObject = returnObject;
+        }
+
+        public ReturnType setTo( boolean value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( int value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( long value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( short value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( float value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( double value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( String value ) {
+            return setTo((Object)value);
+        }
+
+        public ReturnType setTo( Object value ) {
+            // Set the JavaBean-style property on the RepositorySource instance ...
+            Reflection reflection = new Reflection(javaBean.getClass());
+            try {
+                reflection.invokeSetterMethodOnTarget(beanPropertyName, javaBean, value);
+            } catch (Throwable err) {
+                I18n msg = RepositoryI18n.errorSettingJavaBeanPropertyOnInstanceOfClass;
+                throw new DnaConfigurationException(msg.text(beanPropertyName, javaBean.getClass(), err.getMessage()), err);
+            }
+            return returnObject;
+        }
+    }
+
+    /**
+     * Reusable implementation of {@link Configurator.PropertySetter} that sets the property on the specified node in the
+     * configuration graph.
+     * 
+     * @param <ReturnType>
+     */
+    protected class GraphPropertySetter<ReturnType> implements Configurator.PropertySetter<ReturnType> {
+        private final Path path;
+        private final String beanPropertyName;
+        private final ReturnType returnObject;
+
+        protected GraphPropertySetter( Path path,
+                                       String beanPropertyName,
+                                       ReturnType returnObject ) {
+            assert path != null;
+            assert beanPropertyName != null;
+            assert returnObject != null;
+            this.path = path;
+            this.beanPropertyName = Inflector.getInstance().lowerCamelCase(beanPropertyName);
+            this.returnObject = returnObject;
+        }
+
+        public ReturnType setTo( boolean value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( int value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( long value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( short value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( float value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( double value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( String value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+
+        public ReturnType setTo( Object value ) {
+            configuration().set(nameFor(beanPropertyName)).to(value).on(path);
+            return returnObject;
+        }
+    }
+
+    protected class GraphRepositoryDetails<ReturnType> implements RepositoryDetails<ReturnType> {
+        private final Path path;
+        private final ReturnType returnObject;
+
+        protected GraphRepositoryDetails( Path path,
+                                          ReturnType returnObject ) {
+            assert path != null;
+            assert returnObject != null;
+            this.path = path;
+            this.returnObject = returnObject;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
+         */
+        public RepositoryDetails<ReturnType> named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
+         */
+        public RepositoryDetails<ReturnType> describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<RepositoryDetails<ReturnType>> with( String propertyName ) {
+            return new GraphPropertySetter<RepositoryDetails<ReturnType>>(path, propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.And#and()
+         */
+        public ReturnType and() {
+            return returnObject;
+        }
+    }
+
+    protected class GraphSequencerDetails<ReturnType> implements SequencerDetails<ReturnType> {
+        private final Path path;
+        private final List<String> compiledExpressions = new ArrayList<String>();
+        private final ReturnType returnObject;
+
+        protected GraphSequencerDetails( Path path,
+                                         ReturnType returnObject ) {
+            assert path != null;
+            assert returnObject != null;
+            this.path = path;
+            this.returnObject = returnObject;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SequencerDetails#sequencingFrom(java.lang.String)
+         */
+        public PathExpressionOutput<ReturnType> sequencingFrom( final String from ) {
+            CheckArg.isNotEmpty(from, "from");
+            return new PathExpressionOutput<ReturnType>() {
+                /**
+                 * {@inheritDoc}
+                 * 
+                 * @see org.jboss.dna.repository.Configurator.PathExpressionOutput#andOutputtingTo(java.lang.String)
+                 */
+                public SequencerDetails<ReturnType> andOutputtingTo( String into ) {
+                    CheckArg.isNotEmpty(into, "into");
+                    return sequencingFrom(PathExpression.compile(from + " => " + into));
+                }
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
+         */
+        public SequencerDetails<ReturnType> named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
+         */
+        public SequencerDetails<ReturnType> sequencingFrom( PathExpression expression ) {
+            CheckArg.isNotNull(expression, "expression");
+            String compiledExpression = expression.getExpression();
+            if (!compiledExpressions.contains(compiledExpression)) compiledExpressions.add(compiledExpression);
+            configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(compiledExpressions);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
+         */
+        public SequencerDetails<ReturnType> describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.And#and()
+         */
+        public ReturnType and() {
+            return returnObject;
+        }
+    }
+
+    protected class GraphMimeTypeDetectorDetails<ReturnType> implements MimeTypeDetectorDetails<ReturnType> {
+        private final Path path;
+        private final ReturnType returnObject;
+
+        protected GraphMimeTypeDetectorDetails( Path path,
+                                                ReturnType returnObject ) {
+            assert path != null;
+            assert returnObject != null;
+            this.path = path;
+            this.returnObject = returnObject;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
+         */
+        public MimeTypeDetectorDetails<ReturnType> named( String name ) {
+            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
+         */
+        public PropertySetter<MimeTypeDetectorDetails<ReturnType>> with( String propertyName ) {
+            return new GraphPropertySetter<MimeTypeDetectorDetails<ReturnType>>(path, propertyName, this);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
+         */
+        public MimeTypeDetectorDetails<ReturnType> describedAs( String description ) {
+            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.And#and()
+         */
+        public ReturnType and() {
+            return returnObject;
+        }
+    }
+
+    @Immutable
+    public static class ConfigurationRepository {
+        private final RepositorySource source;
+        private final String description;
+        private final Path path;
+
+        protected ConfigurationRepository( RepositorySource source ) {
+            this(source, null, null);
+        }
+
+        protected ConfigurationRepository( RepositorySource source,
+                                           String description,
+                                           Path path ) {
+            this.source = source;
+            this.description = description != null ? description : "";
+            this.path = path != null ? path : RootPath.INSTANCE;
+        }
+
+        /**
+         * @return source
+         */
+        public RepositorySource getRepositorySource() {
+            return source;
+        }
+
+        /**
+         * @return description
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * @return path
+         */
+        public Path getPath() {
+            return path;
+        }
+
+        public ConfigurationRepository with( String description ) {
+            return new ConfigurationRepository(source, description, path);
+        }
+
+        public ConfigurationRepository with( Path path ) {
+            return new ConfigurationRepository(source, description, path);
+        }
+    }
+}


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

Deleted: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -1,1074 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors. 
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.repository;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.component.ClassLoaderFactory;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.text.Inflector;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.common.util.Reflection;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
-import org.jboss.dna.graph.mimetype.MimeTypeDetector;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathExpression;
-import org.jboss.dna.graph.property.PathFactory;
-import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.RootPath;
-import org.jboss.dna.repository.sequencer.Sequencer;
-
-/**
- */
- at Immutable
-public class DnaConfiguration {
-
-    protected static final Map<String, Name> NAMES_TO_MAP;
-    static {
-        Map<String, Name> names = new HashMap<String, Name>();
-        names.put(DnaLexicon.READABLE_NAME.getLocalName(), DnaLexicon.READABLE_NAME);
-        names.put(DnaLexicon.DESCRIPTION.getLocalName(), DnaLexicon.DESCRIPTION);
-        names.put(DnaLexicon.DEFAULT_CACHE_POLICY.getLocalName(), DnaLexicon.DEFAULT_CACHE_POLICY);
-        names.put(DnaLexicon.RETRY_LIMIT.getLocalName(), DnaLexicon.RETRY_LIMIT);
-        names.put(DnaLexicon.PATH_EXPRESSIONS.getLocalName(), DnaLexicon.PATH_EXPRESSIONS);
-        names.put(DnaLexicon.CLASSNAME.getLocalName(), DnaLexicon.CLASSNAME);
-        names.put(DnaLexicon.CLASSPATH.getLocalName(), DnaLexicon.CLASSPATH);
-        NAMES_TO_MAP = Collections.unmodifiableMap(names);
-    }
-
-    @Immutable
-    protected class ConfigurationRepository {
-        private final RepositorySource source;
-        private final String description;
-        private final Path path;
-
-        protected ConfigurationRepository( RepositorySource source ) {
-            this(source, null, null);
-        }
-
-        protected ConfigurationRepository( RepositorySource source,
-                                           String description,
-                                           Path path ) {
-            this.source = source;
-            this.description = description != null ? description : "";
-            this.path = path != null ? path : RootPath.INSTANCE;
-        }
-
-        /**
-         * @return source
-         */
-        public RepositorySource getRepositorySource() {
-            return source;
-        }
-
-        /**
-         * @return description
-         */
-        public String getDescription() {
-            return description;
-        }
-
-        /**
-         * @return path
-         */
-        public Path getPath() {
-            return path;
-        }
-
-        public ConfigurationRepository with( String description ) {
-            return new ConfigurationRepository(source, description, path);
-        }
-
-        public ConfigurationRepository with( Path path ) {
-            return new ConfigurationRepository(source, description, path);
-        }
-    }
-
-    private final ExecutionContext context;
-    protected ConfigurationRepository configurationSource;
-    private Path sourcesPath;
-    private Path sequencersPath;
-    private Path detectorsPath;
-    private Graph graph;
-    private Graph.Batch batch;
-
-    /**
-     * Create a new configuration for DNA.
-     */
-    public DnaConfiguration() {
-        this(new ExecutionContext());
-    }
-
-    /**
-     * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
-     * 
-     * @param context the new context, or null if a default-constructed execution context should be used
-     * @throws IllegalArgumentException if the supplied context reference is null
-     */
-    public DnaConfiguration( ExecutionContext context ) {
-        CheckArg.isNotNull(context, "context");
-        this.context = context;
-
-        // Set up the default configuration repository ...
-        this.configurationSource = createDefaultConfigurationSource();
-    }
-
-    /**
-     * Method that is used to set up the default configuration repository source. By default, this method sets up the
-     * {@link InMemoryRepositorySource} loaded from the classpath.
-     * 
-     * @return the default repository source
-     */
-    protected ConfigurationRepository createDefaultConfigurationSource() {
-        InMemoryRepositorySource defaultSource = new InMemoryRepositorySource();
-        defaultSource.setName("Configuration");
-        ConfigurationRepository result = new ConfigurationRepository(defaultSource, "Configuration Repository", null);
-        return result;
-    }
-
-    protected final ExecutionContext context() {
-        return this.context;
-    }
-
-    protected final PathFactory pathFactory() {
-        return context().getValueFactories().getPathFactory();
-    }
-
-    /**
-     * Get the graph containing the configuration information. This should be called only after the
-     * {@link #withConfigurationRepository() configuration repository} is set up.
-     * 
-     * @return the configuration repository graph; never null
-     * @see #graph()
-     */
-    protected final Graph graph() {
-        if (this.graph == null) {
-            this.graph = Graph.create(configurationSource.getRepositorySource(), context);
-        }
-        return this.graph;
-    }
-
-    /**
-     * Get the graph batch that can be used to change the configuration, where the changes are enqueued until {@link #save()
-     * saved}. This should be called only after the {@link #withConfigurationRepository() configuration repository} is set up.
-     * 
-     * @return the latest batch for changes to the configuration repository; never null
-     * @see #graph()
-     */
-    protected final Graph.Batch configuration() {
-        if (this.batch == null) {
-            this.batch = graph().batch();
-        }
-        return this.batch;
-    }
-
-    /**
-     * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
-     * 
-     * @return this configuration object for method chaining purposes; never null
-     */
-    public DnaConfiguration save() {
-        if (this.batch != null) {
-            this.batch.execute();
-            this.batch = this.graph.batch();
-        }
-        return this;
-    }
-
-    /**
-     * Specify that this configuration should use a particular {@link RepositorySource} for its configuration repository. By
-     * default each configuration uses an internal transient repository for its configuration, but using this method will make the
-     * configuration use a different repository (that is perhaps shared with other processes).
-     * 
-     * @return the interface for choosing the class, which returns the interface used to configure the repository source that will
-     *         be used for the configuration repository; never null
-     */
-    public ChooseClass<RepositorySource, ConfigRepositoryDetails> withConfigurationRepository() {
-        // The config repository is different, since it has to load immediately ...
-        return new ChooseClass<RepositorySource, ConfigRepositoryDetails>() {
-            public LoadedFrom<ConfigRepositoryDetails> usingClass( final String className ) {
-                return new LoadedFrom<ConfigRepositoryDetails>() {
-                    @SuppressWarnings( "unchecked" )
-                    public ConfigRepositoryDetails loadedFrom( String... classpath ) {
-                        ClassLoader classLoader = context().getClassLoader(classpath);
-                        Class<? extends RepositorySource> clazz = null;
-                        try {
-                            clazz = (Class<? extends RepositorySource>)classLoader.loadClass(className);
-                        } catch (ClassNotFoundException err) {
-                            throw new DnaConfigurationException(RepositoryI18n.unableToLoadClassUsingClasspath.text(className,
-                                                                                                                    classpath));
-                        }
-                        return usingClass(clazz);
-                    }
-
-                    @SuppressWarnings( "unchecked" )
-                    public ConfigRepositoryDetails loadedFromClasspath() {
-                        Class<? extends RepositorySource> clazz = null;
-                        try {
-                            clazz = (Class<? extends RepositorySource>)Class.forName(className);
-                        } catch (ClassNotFoundException err) {
-                            throw new DnaConfigurationException(RepositoryI18n.unableToLoadClass.text(className));
-                        }
-                        return usingClass(clazz);
-                    }
-                };
-            }
-
-            public ConfigRepositoryDetails usingClass( Class<? extends RepositorySource> repositorySource ) {
-                try {
-                    DnaConfiguration.this.configurationSource = new ConfigurationRepository(repositorySource.newInstance());
-                } catch (InstantiationException err) {
-                    I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
-                    throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
-                } catch (IllegalAccessException err) {
-                    I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
-                    throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
-                }
-                return new ConfigurationSourceDetails();
-            }
-        };
-    }
-
-    /**
-     * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name, and
-     * if the name of an existing repository is used, this will replace the existing repository configuration.
-     * 
-     * @param id the id of the new repository that is to be added
-     * @return the interface for choosing the class, which returns the interface used to configure the repository source; never
-     *         null
-     * @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
-     * @see #addRepository(RepositorySource)
-     */
-    public ChooseClass<RepositorySource, RepositoryDetails> addRepository( final String id ) {
-        CheckArg.isNotEmpty(id, "id");
-        // Now create the "dna:source" node with the supplied id ...
-        Path sourcePath = pathFactory().create(sourcesPath(), id);
-        configuration().create(sourcePath).with(DnaLexicon.READABLE_NAME, id).and();
-        return new ClassChooser<RepositorySource, RepositoryDetails>(sourcePath, new GraphRepositoryDetails(sourcePath));
-    }
-
-    /**
-     * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name, and
-     * if the name of an existing repository is used, this will replace the existing repository configuration.
-     * 
-     * @param source the {@link RepositorySource} instance that should be used
-     * @return this configuration object, for method-chaining purposes
-     * @throws IllegalArgumentException if the repository source reference is null
-     * @see #addRepository(String)
-     */
-    public DnaConfiguration addRepository( RepositorySource source ) {
-        CheckArg.isNotNull(source, "source");
-        CheckArg.isNotEmpty(source.getName(), "source.getName()");
-        String name = source.getName();
-        RepositoryDetails details = addRepository(source.getName()).usingClass(source.getClass().getName()).loadedFromClasspath();
-        // Record all of the bean properties ...
-        Path sourcePath = pathFactory().create(sourcesPath(), name);
-        Reflection reflector = new Reflection(source.getClass());
-        for (String propertyName : reflector.findGetterPropertyNames()) {
-            Object value;
-            try {
-                value = reflector.invokeGetterMethodOnTarget(propertyName, source);
-                if (value == null) continue;
-                propertyName = Inflector.getInstance().lowerCamelCase(propertyName);
-                if (NAMES_TO_MAP.containsKey(propertyName)) {
-                    configuration().set(NAMES_TO_MAP.get(propertyName)).to(value).on(sourcePath);
-                } else {
-                    configuration().set(propertyName).to(value).on(sourcePath);
-                }
-            } catch (ValueFormatException err) {
-                throw err;
-            } catch (Throwable err) {
-                // Unable to call getter and set property
-            }
-        }
-        return details.and();
-    }
-
-    /**
-     * Add a new {@link Sequencer sequencer} to this configuration. The new sequencer will have the supplied name, and if the name
-     * of an existing sequencer is used, this will replace the existing sequencer configuration.
-     * 
-     * @param id the identifier of the new sequencer
-     * @return the interface for choosing the class, which returns the interface used to configure the sequencer; never null
-     * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
-     */
-    public ChooseClass<Sequencer, SequencerDetails> addSequencer( final String id ) {
-        CheckArg.isNotEmpty(id, "id");
-        // Now create the "dna:sequencer" node with the supplied id ...
-        Path sequencerPath = pathFactory().create(sequencersPath(), id);
-        configuration().create(sequencerPath).with(DnaLexicon.READABLE_NAME, id).and();
-        return new ClassChooser<Sequencer, SequencerDetails>(sequencerPath, new GraphSequencerDetails(sequencerPath));
-    }
-
-    /**
-     * Add a new {@link MimeTypeDetector MIME type detector} to this configuration. The new detector will have the supplied name,
-     * and if the name of an existing detector is used, this will replace the existing detector configuration.
-     * 
-     * @param id the id of the new detector
-     * @return the interface for choosing the class, which returns the interface used to configure the detector; never null
-     * @throws IllegalArgumentException if the detector name is null, empty, or otherwise invalid
-     */
-    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails> addMimeTypeDetector( final String id ) {
-        CheckArg.isNotEmpty(id, "id");
-        // Now create the "dna:sequencer" node with the supplied id ...
-        Path detectorPath = pathFactory().create(detectorsPath(), id);
-        configuration().create(detectorPath).with(DnaLexicon.READABLE_NAME, id).and();
-        return new ClassChooser<MimeTypeDetector, MimeTypeDetectorDetails>(detectorPath,
-                                                                           new GraphMimeTypeDetectorDetails(detectorPath));
-    }
-
-    /**
-     * Complete this configuration and create the corresponding engine.
-     * 
-     * @return the new engine configured by this instance
-     * @throws DnaConfigurationException if the engine cannot be created from this configuration.
-     */
-    public DnaEngine build() throws DnaConfigurationException {
-        save();
-        return new DnaEngine(this);
-    }
-
-    protected Path sourcesPath() {
-        // Make sure the "dna:sources" node is there
-        if (sourcesPath == null) {
-            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SOURCES);
-            Node node = graph().createIfMissing(path).andReturn();
-            this.sourcesPath = node.getLocation().getPath();
-        }
-        return this.sourcesPath;
-    }
-
-    protected Path sequencersPath() {
-        // Make sure the "dna:sequencers" node is there
-        if (sequencersPath == null) {
-            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SEQUENCERS);
-            Node node = graph().createIfMissing(path).andReturn();
-            this.sequencersPath = node.getLocation().getPath();
-        }
-        return this.sequencersPath;
-    }
-
-    protected Path detectorsPath() {
-        // Make sure the "dna:mimeTypeDetectors" node is there
-        if (detectorsPath == null) {
-            Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.MIME_TYPE_DETECTORS);
-            Node node = graph().createIfMissing(path).andReturn();
-            this.detectorsPath = node.getLocation().getPath();
-        }
-        return this.detectorsPath;
-    }
-
-    /**
-     * Interface used to configure a {@link RepositorySource repository}.
-     */
-    public interface RepositoryDetails
-        extends SetName<RepositoryDetails>, SetDescription<RepositoryDetails>, SetProperties<RepositoryDetails>,
-        ConfigurationBuilder {
-    }
-
-    public interface ConfigRepositoryDetails
-        extends SetDescription<ConfigRepositoryDetails>, SetProperties<ConfigRepositoryDetails>, ConfigurationBuilder {
-        /**
-         * Specify the path under which the configuration content is to be found. This path is assumed to be "/" by default.
-         * 
-         * @param path the path to the configuration content in the configuration source; may not be null
-         * @return this instance for method chaining purposes; never null
-         */
-        public ConfigRepositoryDetails under( String path );
-    }
-
-    /**
-     * Interface used to configure a {@link Sequencer sequencer}.
-     */
-    public interface SequencerDetails extends SetName<SequencerDetails>, SetDescription<SequencerDetails>, ConfigurationBuilder {
-
-        /**
-         * Specify the input {@link PathExpression path expression} represented as a string, which determines when this sequencer
-         * will be executed.
-         * 
-         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
-         *        sequencer
-         * @return the interface used to specify the output path expression; never null
-         */
-        PathExpressionOutput sequencingFrom( String inputPathExpression );
-
-        /**
-         * Specify the input {@link PathExpression path expression}, which determines when this sequencer will be executed.
-         * 
-         * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
-         *        sequencer
-         * @return the interface used to continue specifying the configuration of the sequencer
-         */
-        SequencerDetails sequencingFrom( PathExpression inputPathExpression );
-    }
-
-    /**
-     * Interface used to specify the output path expression for a {@link SequencerDetails#sequencingFrom(PathExpression) sequencer
-     * configuration}.
-     */
-    public interface PathExpressionOutput {
-        /**
-         * Specify the output {@link PathExpression path expression}, which determines where this sequencer's output will be
-         * placed.
-         * 
-         * @param outputExpression the path expression for the location(s) where output generated by the sequencer is to be placed
-         * @return the interface used to continue specifying the configuration of the sequencer
-         */
-        SequencerDetails andOutputtingTo( String outputExpression );
-    }
-
-    /**
-     * Interface used to configure a {@link MimeTypeDetector MIME type detector}.
-     */
-    public interface MimeTypeDetectorDetails
-        extends SetName<MimeTypeDetectorDetails>, SetDescription<MimeTypeDetectorDetails>,
-        SetProperties<MimeTypeDetectorDetails>, ConfigurationBuilder {
-    }
-
-    /**
-     * Interface for configuring the JavaBean-style properties of an object.
-     * 
-     * @param <ReturnType> the interface returned after the property has been set.
-     * @author Randall Hauch
-     */
-    public interface SetProperties<ReturnType> {
-        /**
-         * Specify the name of the JavaBean-style property that is to be set. The value may be set using the interface returned by
-         * this method.
-         * 
-         * @param beanPropertyName the name of the JavaBean-style property (e.g., "retryLimit")
-         * @return the interface used to set the value for the property; never null
-         */
-        PropertySetter<ReturnType> with( String beanPropertyName );
-    }
-
-    /**
-     * The interface used to set the value for a JavaBean-style property.
-     * 
-     * @param <ReturnType> the interface returned from these methods
-     * @author Randall Hauch
-     * @see SetProperties#with(String)
-     */
-    public interface PropertySetter<ReturnType> {
-        /**
-         * Set the property value to an integer.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( int value );
-
-        /**
-         * Set the property value to a long number.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( long value );
-
-        /**
-         * Set the property value to a short.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( short value );
-
-        /**
-         * Set the property value to a boolean.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( boolean value );
-
-        /**
-         * Set the property value to a float.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( float value );
-
-        /**
-         * Set the property value to a double.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( double value );
-
-        /**
-         * Set the property value to a string.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( String value );
-
-        /**
-         * Set the property value to an object.
-         * 
-         * @param value the new value for the property
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType setTo( Object value );
-    }
-
-    /**
-     * The interface used to configure the class used for a component.
-     * 
-     * @param <ComponentClassType> the class or interface that the component is to implement
-     * @param <ReturnType> the interface returned from these methods
-     */
-    public interface ChooseClass<ComponentClassType, ReturnType> {
-
-        /**
-         * Specify the name of the class that should be instantiated for the instance. The classpath information will need to be
-         * defined using the returned interface.
-         * 
-         * @param classname the name of the class that should be instantiated
-         * @return the interface used to define the classpath information; never null
-         * @throws IllegalArgumentException if the class name is null, empty, blank, or not a valid class name
-         */
-        LoadedFrom<ReturnType> usingClass( String classname );
-
-        /**
-         * Specify the class that should be instantiated for the instance. Because the class is already available to this class
-         * loader, there is no need to specify the classloader information.
-         * 
-         * @param clazz the class that should be instantiated
-         * @return the next component to continue configuration; never null
-         * @throws DnaConfigurationException if the class could not be accessed and instantiated (if needed)
-         * @throws IllegalArgumentException if the class reference is null
-         */
-        ReturnType usingClass( Class<? extends ComponentClassType> clazz );
-    }
-
-    /**
-     * The interface used to set a description on a component.
-     * 
-     * @param <ReturnType> the interface returned from these methods
-     */
-    public interface SetDescription<ReturnType> {
-        /**
-         * Specify the description of this component.
-         * 
-         * @param description the description; may be null or empty
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType describedAs( String description );
-    }
-
-    /**
-     * The interface used to set a human readable name on a component.
-     * 
-     * @param <ReturnType> the interface returned from these methods
-     */
-    public interface SetName<ReturnType> {
-        /**
-         * Specify the human-readable name for this component.
-         * 
-         * @param description the description; may be null or empty
-         * @return the next component to continue configuration; never null
-         */
-        ReturnType named( String description );
-    }
-
-    /**
-     * Interface for specifying from where the component's class is to be loaded.
-     * 
-     * @param <ReturnType> the interface returned from these methods
-     */
-    public interface LoadedFrom<ReturnType> {
-        /**
-         * Specify the names of the classloaders that form the classpath for the component, from which the component's class (and
-         * its dependencies) can be loaded. The names correspond to the names supplied to the
-         * {@link ExecutionContext#getClassLoader(String...)} methods.
-         * 
-         * @param classPathNames the names for the classloaders, as passed to the {@link ClassLoaderFactory} implementation (e.g.,
-         *        the {@link ExecutionContext}).
-         * @return the next component to continue configuration; never null
-         * @see #loadedFromClasspath()
-         * @see ExecutionContext#getClassLoader(String...)
-         */
-        ReturnType loadedFrom( String... classPathNames );
-
-        /**
-         * Specify that the component (and its dependencies) will be found on the current (or
-         * {@link Thread#getContextClassLoader() current context}) classloader.
-         * 
-         * @return the next component to continue configuration; never null
-         * @see #loadedFrom(String...)
-         * @see ExecutionContext#getClassLoader(String...)
-         */
-        ReturnType loadedFromClasspath();
-    }
-
-    /**
-     * Interface for classes that can return a reference to their DNA configuration.
-     */
-    public interface ConfigurationBuilder {
-
-        /**
-         * Return a reference to the enclosing configuration so that it can be built or further configured.
-         * 
-         * @return a reference to the enclosing configuration
-         */
-        DnaConfiguration and();
-    }
-
-    protected class ConfigurationSourceDetails implements ConfigRepositoryDetails {
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public ConfigRepositoryDetails describedAs( String description ) {
-            DnaConfiguration.this.configurationSource = DnaConfiguration.this.configurationSource.with(description);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
-         */
-        public PropertySetter<ConfigRepositoryDetails> with( String propertyName ) {
-            return new BeanPropertySetter<ConfigRepositoryDetails>(
-                                                                   DnaConfiguration.this.configurationSource.getRepositorySource(),
-                                                                   propertyName, this);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigRepositoryDetails#under(java.lang.String)
-         */
-        public ConfigRepositoryDetails under( String path ) {
-            CheckArg.isNotNull(path, "path");
-            Path newPath = context().getValueFactories().getPathFactory().create(path);
-            DnaConfiguration.this.configurationSource = DnaConfiguration.this.configurationSource.with(newPath);
-            return null;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-    }
-
-    protected class GraphRepositoryDetails implements RepositoryDetails {
-        private final Path path;
-
-        protected GraphRepositoryDetails( Path path ) {
-            assert path != null;
-            this.path = path;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
-         */
-        public RepositoryDetails named( String name ) {
-            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public RepositoryDetails describedAs( String description ) {
-            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
-         */
-        public PropertySetter<RepositoryDetails> with( String propertyName ) {
-            return new GraphPropertySetter<RepositoryDetails>(path, propertyName, this);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-    }
-
-    protected class GraphSequencerDetails implements SequencerDetails {
-        private final Path path;
-        private final List<String> compiledExpressions = new ArrayList<String>();
-
-        protected GraphSequencerDetails( Path path ) {
-            assert path != null;
-            this.path = path;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(java.lang.String)
-         */
-        public PathExpressionOutput sequencingFrom( final String from ) {
-            CheckArg.isNotEmpty(from, "from");
-            return new PathExpressionOutput() {
-                /**
-                 * {@inheritDoc}
-                 * 
-                 * @see org.jboss.dna.repository.DnaConfiguration.PathExpressionOutput#andOutputtingTo(java.lang.String)
-                 */
-                public SequencerDetails andOutputtingTo( String into ) {
-                    CheckArg.isNotEmpty(into, "into");
-                    return sequencingFrom(PathExpression.compile(from + " => " + into));
-                }
-            };
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
-         */
-        public SequencerDetails named( String name ) {
-            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
-         */
-        public SequencerDetails sequencingFrom( PathExpression expression ) {
-            CheckArg.isNotNull(expression, "expression");
-            String compiledExpression = expression.getExpression();
-            if (!compiledExpressions.contains(compiledExpression)) compiledExpressions.add(compiledExpression);
-            configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(compiledExpressions);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public SequencerDetails describedAs( String description ) {
-            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-    }
-
-    protected class GraphMimeTypeDetectorDetails implements MimeTypeDetectorDetails {
-        private final Path path;
-
-        protected GraphMimeTypeDetectorDetails( Path path ) {
-            assert path != null;
-            this.path = path;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetName#named(java.lang.String)
-         */
-        public MimeTypeDetectorDetails named( String name ) {
-            configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetProperties#with(java.lang.String)
-         */
-        public PropertySetter<MimeTypeDetectorDetails> with( String propertyName ) {
-            return new GraphPropertySetter<MimeTypeDetectorDetails>(path, propertyName, this);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.SetDescription#describedAs(java.lang.String)
-         */
-        public MimeTypeDetectorDetails describedAs( String description ) {
-            configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
-            return this;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ConfigurationBuilder#and()
-         */
-        public DnaConfiguration and() {
-            return DnaConfiguration.this;
-        }
-    }
-
-    /**
-     * Abstract implementation of {@link ChooseClass} that has a single abstract method to obtain the interface that should be
-     * returned when the class name and classpath have been selected.
-     * 
-     * @param <ComponentClass> the type of the component that is being chosen
-     * @param <ReturnType> the interface that should be returned when the class name and classpath have been chosen.
-     * @author Randall Hauch
-     */
-    protected class ClassChooser<ComponentClass, ReturnType> implements ChooseClass<ComponentClass, ReturnType> {
-        protected final Path pathOfComponentNode;
-        protected final ReturnType returnObject;
-
-        protected ClassChooser( Path pathOfComponentNode,
-                                ReturnType returnObject ) {
-            assert pathOfComponentNode != null;
-            assert returnObject != null;
-            this.pathOfComponentNode = pathOfComponentNode;
-            this.returnObject = returnObject;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ChooseClass#usingClass(java.lang.String)
-         */
-        public LoadedFrom<ReturnType> usingClass( final String classname ) {
-            CheckArg.isNotEmpty(classname, "classname");
-            configuration().set(DnaLexicon.CLASSNAME).to(classname).on(pathOfComponentNode);
-            return new LoadedFrom<ReturnType>() {
-                public ReturnType loadedFromClasspath() {
-                    return returnObject;
-                }
-
-                public ReturnType loadedFrom( String... classpath ) {
-                    CheckArg.isNotEmpty(classpath, "classpath");
-                    if (classpath.length == 1 && classpath[0] != null) {
-                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0]).on(pathOfComponentNode);
-                    } else {
-                        Object[] remaining = new String[classpath.length - 1];
-                        System.arraycopy(classpath, 1, remaining, 0, remaining.length);
-                        configuration().set(DnaLexicon.CLASSPATH).to(classpath[0], remaining).on(pathOfComponentNode);
-                    }
-                    return returnObject;
-                }
-            };
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.repository.DnaConfiguration.ChooseClass#usingClass(java.lang.Class)
-         */
-        public ReturnType usingClass( Class<? extends ComponentClass> clazz ) {
-            CheckArg.isNotNull(clazz, "clazz");
-            return usingClass(clazz.getName()).loadedFromClasspath();
-        }
-    }
-
-    /**
-     * Reusable implementation of {@link PropertySetter} that sets the JavaBean-style property using reflection.
-     * 
-     * @param <ReturnType>
-     * @author Randall Hauch
-     */
-    protected class BeanPropertySetter<ReturnType> implements PropertySetter<ReturnType> {
-        private final Object javaBean;
-        private final String beanPropertyName;
-        private final ReturnType returnObject;
-
-        protected BeanPropertySetter( Object javaBean,
-                                      String beanPropertyName,
-                                      ReturnType returnObject ) {
-            assert javaBean != null;
-            assert beanPropertyName != null;
-            assert returnObject != null;
-            this.javaBean = javaBean;
-            this.beanPropertyName = beanPropertyName;
-            this.returnObject = returnObject;
-        }
-
-        public ReturnType setTo( boolean value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( int value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( long value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( short value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( float value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( double value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( String value ) {
-            return setTo((Object)value);
-        }
-
-        public ReturnType setTo( Object value ) {
-            // Set the JavaBean-style property on the RepositorySource instance ...
-            Reflection reflection = new Reflection(javaBean.getClass());
-            try {
-                reflection.invokeSetterMethodOnTarget(beanPropertyName, javaBean, value);
-            } catch (Throwable err) {
-                I18n msg = RepositoryI18n.errorSettingJavaBeanPropertyOnInstanceOfClass;
-                throw new DnaConfigurationException(msg.text(beanPropertyName, javaBean.getClass(), err.getMessage()), err);
-            }
-            return returnObject;
-        }
-    }
-
-    /**
-     * Reusable implementation of {@link PropertySetter} that sets the property on the specified node in the configuration graph.
-     * 
-     * @param <ReturnType>
-     * @author Randall Hauch
-     */
-    protected class GraphPropertySetter<ReturnType> implements PropertySetter<ReturnType> {
-        private final Path path;
-        private final String beanPropertyName;
-        private final ReturnType returnObject;
-
-        protected GraphPropertySetter( Path path,
-                                       String beanPropertyName,
-                                       ReturnType returnObject ) {
-            assert path != null;
-            assert beanPropertyName != null;
-            assert returnObject != null;
-            this.path = path;
-            this.beanPropertyName = Inflector.getInstance().lowerCamelCase(beanPropertyName);
-            this.returnObject = returnObject;
-        }
-
-        public ReturnType setTo( boolean value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( int value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( long value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( short value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( float value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( double value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( String value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-
-        public ReturnType setTo( Object value ) {
-            if (NAMES_TO_MAP.containsKey(beanPropertyName)) {
-                configuration().set(NAMES_TO_MAP.get(beanPropertyName)).to(value).on(path);
-            } else {
-                configuration().set(beanPropertyName).to(value).on(path);
-            }
-            return returnObject;
-        }
-    }
-}

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ *
+ * 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.repository;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.repository.Configurator.ChooseClass;
+import org.jboss.dna.repository.Configurator.ConfigRepositoryDetails;
+import org.jboss.dna.repository.Configurator.ConfigurationRepository;
+import org.jboss.dna.repository.Configurator.MimeTypeDetectorDetails;
+import org.jboss.dna.repository.Configurator.RepositoryDetails;
+import org.jboss.dna.repository.Configurator.SequencerDetails;
+import org.jboss.dna.repository.sequencer.Sequencer;
+
+/**
+ * 
+ */
+public class DnaConfiguration
+    implements Configurator.Initializer<DnaConfiguration>, Configurator.SequencerConfigurator<DnaConfiguration>,
+    Configurator.RepositoryConfigurator<DnaConfiguration>, Configurator.MimeDetectorConfigurator<DnaConfiguration>,
+    Configurator.Builder<DnaEngine> {
+
+    protected static final Map<String, Name> NAMES_TO_MAP;
+    static {
+        Map<String, Name> names = new HashMap<String, Name>();
+        names.put(DnaLexicon.READABLE_NAME.getLocalName(), DnaLexicon.READABLE_NAME);
+        names.put(DnaLexicon.DESCRIPTION.getLocalName(), DnaLexicon.DESCRIPTION);
+        names.put(DnaLexicon.DEFAULT_CACHE_POLICY.getLocalName(), DnaLexicon.DEFAULT_CACHE_POLICY);
+        names.put(DnaLexicon.RETRY_LIMIT.getLocalName(), DnaLexicon.RETRY_LIMIT);
+        names.put(DnaLexicon.PATH_EXPRESSIONS.getLocalName(), DnaLexicon.PATH_EXPRESSIONS);
+        names.put(DnaLexicon.CLASSNAME.getLocalName(), DnaLexicon.CLASSNAME);
+        names.put(DnaLexicon.CLASSPATH.getLocalName(), DnaLexicon.CLASSPATH);
+        NAMES_TO_MAP = Collections.unmodifiableMap(names);
+    }
+
+    private final Builder<DnaConfiguration> builder;
+
+    /**
+     * Create a new configuration for DNA.
+     */
+    public DnaConfiguration() {
+        this(new ExecutionContext());
+    }
+
+    /**
+     * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+     * 
+     * @param context the new context, or null if a default-constructed execution context should be used
+     * @throws IllegalArgumentException if the supplied context reference is null
+     */
+    public DnaConfiguration( ExecutionContext context ) {
+        this.builder = new Builder<DnaConfiguration>(context, this);
+    }
+
+    /**
+     * Get the execution context used by this configurator.
+     * 
+     * @return the execution context; never null
+     */
+    public final ExecutionContext getExecutionContext() {
+        return builder.getExecutionContext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.Initializer#withConfigurationRepository()
+     */
+    public ChooseClass<RepositorySource, ConfigRepositoryDetails<DnaConfiguration>> withConfigurationRepository() {
+        return builder.withConfigurationRepository();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
+     */
+    public ChooseClass<RepositorySource, RepositoryDetails<DnaConfiguration>> addRepository( String id ) {
+        return builder.addRepository(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(org.jboss.dna.graph.connector.RepositorySource)
+     */
+    public DnaConfiguration addRepository( RepositorySource source ) {
+        return builder.addRepository(source);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.SequencerConfigurator#addSequencer(java.lang.String)
+     */
+    public ChooseClass<Sequencer, SequencerDetails<DnaConfiguration>> addSequencer( String id ) {
+        return builder.addSequencer(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.MimeDetectorConfigurator#addMimeTypeDetector(java.lang.String)
+     */
+    public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails<DnaConfiguration>> addMimeTypeDetector( String id ) {
+        return builder.addMimeTypeDetector(id);
+    }
+
+    /**
+     * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
+     * 
+     * @return this configuration object for method chaining purposes; never null
+     */
+    public DnaConfiguration save() {
+        return builder.save();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.repository.Configurator.Builder#build()
+     */
+    public DnaEngine build() throws DnaConfigurationException {
+        save();
+        return new DnaEngine(builder.getExecutionContext(), builder.configurationSource);
+    }
+
+    protected Graph graph() {
+        return builder.graph();
+    }
+
+    protected ConfigurationRepository configurationRepository() {
+        return builder.configurationSource;
+    }
+
+    public static class Builder<ReturnType> extends Configurator<ReturnType>
+        implements Configurator.Initializer<ReturnType>, Configurator.SequencerConfigurator<ReturnType>,
+        Configurator.RepositoryConfigurator<ReturnType>, Configurator.MimeDetectorConfigurator<ReturnType> {
+
+        private Path sourcesPath;
+        private Path sequencersPath;
+        private Path detectorsPath;
+
+        /**
+         * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+         * 
+         * @param context the new context, or null if a default-constructed execution context should be used
+         * @param builder the builder object returned from all the methods
+         * @throws IllegalArgumentException if the supplied context reference is null
+         */
+        public Builder( ExecutionContext context,
+                        ReturnType builder ) {
+            super(context, builder);
+        }
+
+        public DnaEngine buildDnaEngine() {
+            return new DnaEngine(context, configurationSource);
+        }
+
+        public ConfigurationRepository getConfigurationRepository() {
+            return configurationSource;
+        }
+
+        protected Path sourcesPath() {
+            // Make sure the "dna:sources" node is there
+            if (sourcesPath == null) {
+                Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SOURCES);
+                Node node = graph().createIfMissing(path).andReturn();
+                this.sourcesPath = node.getLocation().getPath();
+            }
+            return this.sourcesPath;
+        }
+
+        protected Path sequencersPath() {
+            // Make sure the "dna:sequencers" node is there
+            if (sequencersPath == null) {
+                Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.SEQUENCERS);
+                Node node = graph().createIfMissing(path).andReturn();
+                this.sequencersPath = node.getLocation().getPath();
+            }
+            return this.sequencersPath;
+        }
+
+        protected Path detectorsPath() {
+            // Make sure the "dna:mimeTypeDetectors" node is there
+            if (detectorsPath == null) {
+                Path path = pathFactory().create(this.configurationSource.getPath(), DnaLexicon.MIME_TYPE_DETECTORS);
+                Node node = graph().createIfMissing(path).andReturn();
+                this.detectorsPath = node.getLocation().getPath();
+            }
+            return this.detectorsPath;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.Initializer#withConfigurationRepository()
+         */
+        public ChooseClass<RepositorySource, ConfigRepositoryDetails<ReturnType>> withConfigurationRepository() {
+            return new ConfigurationRepositoryClassChooser<ReturnType>(builder);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.SequencerConfigurator#addSequencer(java.lang.String)
+         */
+        public ChooseClass<Sequencer, SequencerDetails<ReturnType>> addSequencer( String id ) {
+            CheckArg.isNotEmpty(id, "id");
+            // Now create the "dna:sequencer" node with the supplied id ...
+            Path path = createOrReplaceNode(sequencersPath(), id);
+            SequencerDetails<ReturnType> details = new GraphSequencerDetails<ReturnType>(path, builder);
+            return new ClassChooser<Sequencer, SequencerDetails<ReturnType>>(path, details);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
+         */
+        public ChooseClass<RepositorySource, RepositoryDetails<ReturnType>> addRepository( String id ) {
+            CheckArg.isNotEmpty(id, "id");
+            // Now create the "dna:source" node with the supplied id ...
+            Path path = createOrReplaceNode(sourcesPath(), id);
+            RepositoryDetails<ReturnType> details = new GraphRepositoryDetails<ReturnType>(path, builder);
+            return new ClassChooser<RepositorySource, RepositoryDetails<ReturnType>>(path, details);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(org.jboss.dna.graph.connector.RepositorySource)
+         */
+        public ReturnType addRepository( RepositorySource source ) {
+            CheckArg.isNotNull(source, "source");
+            CheckArg.isNotEmpty(source.getName(), "source.getName()");
+            String name = source.getName();
+            RepositoryDetails<ReturnType> details = addRepository(source.getName()).usingClass(source.getClass().getName())
+                                                                                   .loadedFromClasspath();
+            // Record all of the bean properties ...
+            Path sourcePath = pathFactory().create(sourcesPath(), name);
+            recordBeanPropertiesInGraph(sourcePath, source);
+            return details.and();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator.MimeDetectorConfigurator#addMimeTypeDetector(java.lang.String)
+         */
+        public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails<ReturnType>> addMimeTypeDetector( String id ) {
+            CheckArg.isNotEmpty(id, "id");
+            // Now create the "dna:sequencer" node with the supplied id ...
+            Path detectorPath = createOrReplaceNode(detectorsPath(), id);
+            MimeTypeDetectorDetails<ReturnType> details = new GraphMimeTypeDetectorDetails<ReturnType>(detectorPath, builder);
+            return new ClassChooser<MimeTypeDetector, MimeTypeDetectorDetails<ReturnType>>(detectorPath, details);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.Configurator#nameFor(java.lang.String)
+         */
+        @Override
+        protected Name nameFor( String name ) {
+            Name result = NAMES_TO_MAP.get(name);
+            if (result == null) result = context.getValueFactories().getNameFactory().create(name);
+            return result;
+        }
+    }
+
+}

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaEngine.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -70,7 +70,7 @@
 
     public static final String CONFIGURATION_REPOSITORY_NAME = "dna:configuration";
 
-    private final DnaConfiguration.ConfigurationRepository configuration;
+    private final Configurator.ConfigurationRepository configuration;
     private final ConfigurationScanner scanner;
     private final Problems problems;
     private final ExecutionContext context;
@@ -84,14 +84,15 @@
 
     private final RepositoryConnectionFactory connectionFactory;
 
-    DnaEngine( DnaConfiguration configuration ) {
+    DnaEngine( ExecutionContext context,
+               Configurator.ConfigurationRepository configuration ) {
         this.problems = new SimpleProblems();
 
         // Use the configuration's context ...
-        this.context = configuration.context();
+        this.context = context;
 
         // And set up the scanner ...
-        this.configuration = configuration.configurationSource;
+        this.configuration = configuration;
         this.scanner = new ConfigurationScanner(this.problems, this.context, this.configuration);
 
         // Add the configuration source to the repository library ...
@@ -211,11 +212,11 @@
     protected class ConfigurationScanner {
         private final Problems problems;
         private final ExecutionContext context;
-        private final DnaConfiguration.ConfigurationRepository configurationRepository;
+        private final Configurator.ConfigurationRepository configurationRepository;
 
         protected ConfigurationScanner( Problems problems,
                                         ExecutionContext context,
-                                        DnaConfiguration.ConfigurationRepository configurationRepository ) {
+                                        Configurator.ConfigurationRepository configurationRepository ) {
             this.problems = problems;
             this.context = context;
             this.configurationRepository = configurationRepository;

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-30 04:32:26 UTC (rev 866)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/DnaConfigurationTest.java	2009-04-30 15:31:52 UTC (rev 867)
@@ -85,8 +85,8 @@
                                                .and()
                                                .save();
         assertThat(config, is(notNullValue()));
-        assertThat(config.configurationSource.getRepositorySource(), is(instanceOf(InMemoryRepositorySource.class)));
-        InMemoryRepositorySource source = (InMemoryRepositorySource)config.configurationSource.getRepositorySource();
+        assertThat(config.configurationRepository().getRepositorySource(), is(instanceOf(InMemoryRepositorySource.class)));
+        InMemoryRepositorySource source = (InMemoryRepositorySource)config.configurationRepository().getRepositorySource();
         assertThat(source.getName(), is("repository name"));
         assertThat(source.getRetryLimit(), is(5));
     }
@@ -287,7 +287,8 @@
                      .usingClass(ExtensionBasedMimeTypeDetector.class)
                      .describedAs("default detector");
         configuration.addSequencer("sequencerA")
-                     .usingClass(MockSequencerA.class)
+                     .usingClass(MockSequencerA.class.getName())
+                     .loadedFromClasspath()
                      .named("The (Main) Sequencer")
                      .describedAs("Mock Sequencer A")
                      .sequencingFrom("/foo/source")



From dna-commits at lists.jboss.org  Thu Apr 30 18:11:02 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 30 Apr 2009 18:11:02 -0400
Subject: [dna-commits] DNA SVN: r870 - in trunk:
	docs/gettingstarted/src/main/docbook/en-US and 3 other directories.
Message-ID: <E1LzeT8-00084U-I4@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-30 18:11:02 -0400 (Thu, 30 Apr 2009)
New Revision: 870

Modified:
   trunk/docs/gettingstarted/src/main/docbook/en-US/content/custom_sequencers.xml
   trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml
   trunk/docs/gettingstarted/src/main/docbook/en-US/master.xml
   trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
   trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
   trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml
   trunk/docs/reference/src/main/docbook/en-US/content/testing.xml
   trunk/docs/reference/src/main/docbook/en-US/custom.dtd
   trunk/docs/reference/src/main/docbook/en-US/master.xml
   trunk/pom.xml
Log:
DNA-385 Documentation Updates for 0.4

Reviewed and edited both documents to bring everything up to the current state of the code.  Most parts were still accurate, but there were a number of places there were minor changes and quite a few sections that needed more work.

Pending errors or other changes/suggestions, the docs are complete and accurate for this release.

Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/content/custom_sequencers.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/content/custom_sequencers.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/content/custom_sequencers.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -73,12 +73,12 @@
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-common</artifactId>
-  <version>0.1</version>
+  <version>0.4</version>
 </dependency>
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-graph</artifactId>
-  <version>0.1</version>
+  <version>0.4</version>
 </dependency>
 <dependency>
   <groupId>org.slf4j</groupId>
@@ -122,7 +122,7 @@
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-repository</artifactId>
-  <version>0.1</version>
+  <version>0.4</version>
   <scope>test</scope>
 </dependency>
 <!-- Java Content Repository API -->
@@ -136,7 +136,7 @@
 <dependency>
   <groupId>org.apache.jackrabbit</groupId>
   <artifactId>jackrabbit-api</artifactId>
-  <version>1.3.3</version>
+  <version>1.4</version>
   <scope>test</scope>
   <!-- Exclude these since they are included in JDK 1.5 -->
   <exclusions>
@@ -153,7 +153,7 @@
 <dependency>
   <groupId>org.apache.jackrabbit</groupId>
   <artifactId>jackrabbit-core</artifactId>
-  <version>1.3.3</version>
+  <version>1.4.5</version>
   <scope>test</scope>
   <!-- Exclude these since they are included in JDK 1.5 -->
   <exclusions>

Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/content/understanding_dna.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -45,9 +45,10 @@
   </sect1>
   <sect1 id="architecture">
     <title>Architecture</title>
-    <para>The architecture for JBoss DNA consists of several major components that will be built on top of standard APIs,
+    <para>The planned architecture for JBoss DNA consists of several major components that will be built on top of standard APIs,
       including JCR, JDBC, JNDI and HTTP. The goal is to allow these components to be assembled as needed and add value on top
-      of other DNA components or third-party systems that support these standard APIs.</para>
+      of other DNA components or third-party systems that support these standard APIs.  Not all of these components exist
+      yet - at the moment we're focusing on completing our JCR-compliant implementation using our connector framework.</para>
     <mediaobject>
       <imageobject role="fo">
         <imagedata align="center" fileref="dna-architecture.png" />
@@ -57,18 +58,87 @@
       </imageobject>
     </mediaobject>
     <para>
-      As shown in the diagram above, the major components are (starting at the top):
+      Let's go over each of these components, starting from the bottom of the diagram:
       <itemizedlist>
         <listitem>
           <para>
+            <emphasis role="strong">DNA Connectors</emphasis>
+            are used to communicate with these external sources of information, whether there's one source (like a database) in
+ 						which all of the content is stored, or information is being federated from multiple sources.  The connector's
+ 						job is to interact with the external source and map the source's information into the lower-level graph language used by
+						JBoss DNA.  Connectors also may optionally participate in distributed transactions by exposing an XAResource.
+						In summary, then, the connector API isolates all of the other components from how the graph are persisted.
+						We've built a number of connectors already, but we're always interested in adding more.  Or, you could write your own,
+						since we've designed the connector API to be as straightforward as possible.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">DNA Federation</emphasis>
+            is actually special repository connector that accesses information from multiple other sources via connectors,
+ 						making all this information look like it is part of a single, unified graph.  Because it is a connector,
+ 						it can be used wherever connectors can be used.  And because it uses connectors, the federation connector
+						makes it possible to integrate a wide variety of external systems, like other JCR
+            repositories, databases, applications, and services.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">DNA JCR</emphasis>
+            is an implementation of the JCR API that accesses the content from a single connector.  Our implementation has come a long
+						with in the 0.4 release, and is nearly Level 1 and Level 2 compliant.  Finishing the remaining features, including search
+						and query, are major objectives of our next release.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">DNA Sequencers</emphasis>
+            are pluggable components that automatically process content (typically files) that are uploaded into the repository,
+						looking for useful structure and information in that content.  When a file is uploaded to the repository, JBoss DNA automatically
+						figures out which sequencer(s) should be run, and then runs them.  Each sequencer, then, extracts the meaningful information
+						from the file's content and places that structure in the repository.  Once this information is in
+            the repository, it can be viewed, edited, analyzed, searched, and related to other content. DNA defines a Java
+            interface that sequencers must implement. DNA sequencers operate upon any JCR-compliant repository.
+						We have a number of sequencers, but plan on adding more over time.  Like connectors, the Sequencer API makes it very
+						simple to write sequencers for your own file types.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">DNA Analyses</emphasis>
+            are similar to sequencers, except they process the graph structure rather than uploaded files.  You can think of them
+ 						as report generators, where they process some area of the repository and generate output.  The output doesn't take the
+						form of a document or file, but rather more graph content that is then stored in the repository, where it can be accessed
+						and searched just like any other content.  We're still figuring out how to best make analyzers easy to write and use,
+						and have been focused on other aspects of the architecture.  But we're planning on using analyzers for dependency,
+						similarity, and statistical analyses.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">DNA Maven</emphasis>
+            is a classloader library compatible with Maven 2 project dependencies. This allows the creation of Java ClassLoader
+            instances using Maven 2 style paths, and all dependencies are transitively managed and included.  This exists but is
+						still immature.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+		<para>
+			The remaining parts of the architecture haven't yet been started, but they service important purposes for a complete metadata
+			repository system.
+			<itemizedlist>
+        <listitem>
+          <para>
             <emphasis role="strong">DNA Eclipse Plugins</emphasis>
-            enable Eclipse users to access the contents of a JBoss DNA repository.
+            enable Eclipse users to access the contents of a JBoss DNA repository.  This is a planned component that may be started soon.
           </para>
         </listitem>
         <listitem>
           <para>
             <emphasis role="strong">DNA JDBC Driver</emphasis>
             provides a driver implementation, allowing JDBC-aware applications to connect to and use a JBoss DNA repository.
+            This is a planned component on our roadmap.
           </para>
         </listitem>
         <listitem>
@@ -106,74 +176,19 @@
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">DNA Sequencers</emphasis>
-            are pluggable components that make it possible for content to be uploaded to the repository and automatically
-            processed to extract meaningful structure and place that structure in the repository. Once this information is in
-            the repository, it can be viewed, edited, analyzed, searched, and related to other content. DNA defines a Java
-            interface that sequencers must implement. DNA sequencers operate upon any JCR-compliant repository.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            <emphasis role="strong">DNA Analyses</emphasis>
-            are pluggable components that analyze content and the relationships between content to generate reports or to answer
-            queries. DNA will include some standard analyzers, like dependency analysis and similarity analysis, that are
-            commonly needed by many different solutions. DNA analyzers operate upon any JCR-compliant repository.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">DNA Views</emphasis>
             are definitions of how types of information are to be presented in a user interface to allow for creation, reading,
             editing, and deletion of information. DNA view definitions consist of data stored in a JCR repository, and as such
             views can be easily added, changed or removed entirely by using the DNA Web Application, requiring no programming.
           </para>
         </listitem>
-        <listitem>
-          <para>
-            <emphasis role="strong">DNA Repositories</emphasis>
-            is an implementation of the JCR API that builds the content within the repository by accessing and integrating
-            information from one or more sources. 
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            <emphasis role="strong">DNA Federation</emphasis>
-            is a special repository connector that accesses information from multiple sources and makes it accessible 
-						as if it were a single repository. DNA Federation allows the integration of external systems, like other JCR
-            repositories, databases, applications, and services.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            <emphasis role="strong">DNA Connectors</emphasis>
-            are used to communicate with these external sources of information. In the federation engine, each source is able to
-            contribute node structure and node properties to any part of the federated graph, although typically many connectors
-            will contribute most of their information to isolated subgraphs. The result is that integration from a wide range of
-            systems can be integrated and accessed through the DNA Web Application, DNA Publishing Server, and DNA WebDAV
-            Server. Connectors also may optionally participate in distributed transactions by exposing an XAResource.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            <emphasis role="strong">DNA Maven</emphasis>
-            is a classloader library compatible with Maven 2 project dependencies. This allows the creation of Java ClassLoader
-            instances using Maven 2 style paths, and all dependencies are transitively managed and included.
-          </para>
-        </listitem>
       </itemizedlist>
     </para>
     <para>
       Continue reading the rest of this chapter for more detail about the
-      <link linkend="sequencers">sequencing framework</link>
-      available in this release, or the
-      <link linkend="federation">federation engine</link>
-      and
-      <link linkend="federation_connectors">connectors</link>
-      that will be the focus of the next release. Or, skip to the
-      <link linkend="downloading_and_running">examples</link>
-      to see how to start using JBoss DNA &versionNumber;
-      today. 
+      <link linkend="sequencers">sequencing framework</link> available in this release, or the <link linkend="federation">federation engine</link>
+      and <link linkend="federation_connectors">connectors</link>. Or, skip to the <link linkend="downloading_and_running">examples</link>
+      to see how to start using JBoss DNA &versionNumber; today. 
     </para>
   </sect1>
   <sect1 id="sequencers">
@@ -190,7 +205,7 @@
       policies, UML, MetaMatrix models, etc.</para>
     <para>
       JBoss DNA sequencers sit on top of existing JCR repositories (including federated repositories) - they basically extract
-      more useful information from what's already stored in the repository. And they use the existing JCR versioning system. Each
+      more useful information from what's already stored in the repository. And they use the existing JCR event and versioning system. Each
       sequencer typically processes a single kind of file format or a single kind of content. </para>
     <para>The following sequencers are included in JBoss DNA:
       <itemizedlist>
@@ -201,9 +216,7 @@
             writes that image metadata to the repository. It gets the file format, image resolution, number of bits per pixel
             (and optionally number of images), comments and physical resolution from JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM,
             PGM, PPM, and PSD files. (This sequencer may be improved in the future to also extract EXIF metadata from JPEG
-            files; see
-            <ulink url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ulink>
-            .)
+            files; see <ulink url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ulink>.)
           </para>
         </listitem>
         <listitem>
@@ -212,10 +225,41 @@
             - A sequencer that processes the contents of an MP3 audio file, extracts the metadata for the file, and then
             writes that image metadata to the repository. It gets the title, author, album, year, and comment.  
             (This sequencer may be improved in the future to also extract other ID3 metadata from other audio file formats; see
-            <ulink url="http://jira.jboss.org/jira/browse/DNA-66">DNA-26</ulink>
-            .)
+            <ulink url="http://jira.jboss.org/jira/browse/DNA-66">DNA-66</ulink>.)
           </para>
         </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">ZIP Archive Sequencer</emphasis>
+            - Process ZIP archive files to extract (explode) the contents into the repository.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">Java Source File Sequencer</emphasis>
+            - Process Java source files to extract the class structure (including annotations) into the repository.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">XML File Sequencer</emphasis>
+            - Process XML files to extract the structure into the repository.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">Microsoft Office File Sequencer</emphasis>
+            - Process Microsoft Office documents, spreadsheets, and presentation files to extract their basic structure.
+						For example, the sequencer extracts the outline of a document, or the title and outline of a presentation. 
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis role="strong">JCR Compact Node Definition (CND) File Sequencer</emphasis>
+            - Process the CND files defined by JCR, extracting the various node types with their property definitions and child
+						node definitions.
+          </para>
+        </listitem>
       </itemizedlist>
     </para>
     <para>
@@ -224,6 +268,16 @@
       <itemizedlist>
         <listitem>
           <para>
+            <emphasis role="strong">Data Definition Language (DDL) Sequencer</emphasis>
+            - Process various dialects of DDL, including that from Oracle, SQL Server, MySQL, PostgreSQL, and others. May need
+            to be split up into a different sequencer for each dialect. This sequencer is being developed as part of a
+						Google Summer of Code 2009 project. (See
+            <ulink url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ulink>
+            )
+          </para>
+        </listitem>
+        <listitem>
+          <para>
             <emphasis role="strong">XML Schema Document (XSD) Sequencer</emphasis>
             - Process XSD files and extract the various elements, attributes, complex types, simple types, groups, and other
             information. (See
@@ -259,14 +313,6 @@
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">ZIP Archive Sequencer</emphasis>
-            - Process ZIP archive files to extract (explode) the contents into the repository. (See
-            <ulink url="http://jira.jboss.org/jira/browse/DNA-63">DNA-63</ulink>
-            )
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">Java Archive (JAR) Sequencer</emphasis>
             - Process JAR files to extract (explode) the contents into the classes and file resources. (See
             <ulink url="http://jira.jboss.org/jira/browse/DNA-64">DNA-64</ulink>
@@ -284,14 +330,6 @@
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">Java Source File Sequencer</emphasis>
-            - Process Java source files to extract the class structure (including annotations) into the repository. (See
-            <ulink url="http://jira.jboss.org/jira/browse/DNA-51">DNA-51</ulink>
-            )
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">PDF Sequencer</emphasis>
             - Process PDF files to extract the document metadata, including table of contents. (See
             <ulink url="http://jira.jboss.org/jira/browse/DNA-50">DNA-50</ulink>
@@ -309,15 +347,6 @@
         </listitem>
         <listitem>
           <para>
-            <emphasis role="strong">Data Definition Language (DDL) Sequencer</emphasis>
-            - Process various dialects of DDL, including that from Oracle, SQL Server, MySQL, PostgreSQL, and others. May need
-            to be split up into a different sequencer for each dialect. (See
-            <ulink url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ulink>
-            )
-          </para>
-        </listitem>
-        <listitem>
-          <para>
             <emphasis role="strong">MP3 and MP4 Sequencer</emphasis>
             - Process MP3 and MP4 audio files to extract the name of the song, artist, album, track number, and other metadata.
             (See
@@ -342,7 +371,7 @@
       to access that information as if it were all stored in a single JCR repository, but to really leave the information where
       it is.</para>
     <para>Why not just copy or move the information into a JCR repository?  Moving it is probably pretty difficult, since most 
-			likely there are existing applications that rely upon that information being where it is.  All of those applications
+			likely existing applications rely upon that information being where it is.  All of those applications
 			would break or have to change.  And copying the information means that we'd have to continually synchronize the changes.
 			This not only is a lot of work, but it often creates issues with knowing which information is accurate.
 		</para>
@@ -500,11 +529,6 @@
         (and possibly rewritten) such that it can be pushed down to all the appropriate sources. Also, the cached results must
         be consulted prior to returning the query results, as the results from one source might have contributions from another
         source.</para>
-      <note>
-        <para> It is hoped that the MetaMatrix query engine can be used for this purpose after it is open-sourced. This engine
-          implements sophisticated query planning and optimization techniques for working efficiently with multiple sources.
-        </para>
-      </note>
       <para>Searching the whole federated repository is also important. This allows users to simply supply a handful of
         search terms, and to get results that are ranked based upon how close each result is to the search terms. (Searching is
         very different from querying, which involves specifying the exact semantics of what is to be searched and how the

Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/master.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/master.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -25,9 +25,9 @@
   ~ Boston, MA  02110-1301  USA
   -->
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY versionNumber "0.3">
-<!ENTITY copyrightYear "2008">
-<!ENTITY copyrightHolder "Red Hat Middleware, LLC.">
+<!ENTITY versionNumber "0.4">
+<!ENTITY copyrightYear "2008-2009">
+<!ENTITY copyrightHolder "Red Hat, Inc.">
 ]>
 <book lang="en">
 	<bookinfo>
@@ -35,7 +35,7 @@
 		<subtitle>Getting Started Guide</subtitle>
 		<releaseinfo>&versionNumber;</releaseinfo>
 		<productnumber>&versionNumber;</productnumber>
-		<issuenum>1</issuenum>
+		<issuenum>4</issuenum>
 		<mediaobject>
 			<imageobject role="fo">
 				<imagedata fileref="dna-logo.png" align="center"/>

Modified: trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/content/introduction.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -170,7 +170,7 @@
 	  </para>
 	  <para>
 	    The <emphasis>JBoss DNA project</emphasis>
-	    is building unified metadata repository system that is compliant with JCR.  Nearly all of these capabilities are to be hidden
+	    is building unified metadata repository system that will be compliant with JCR.  Nearly all of these capabilities are to be hidden
 	    below the JCR API and involve automated processing of the information in the repository. Thus, JBoss DNA can add value to
 	    existing repository implementations. For example, JCR repositories offer the ability to upload files into the repository and
 	    have the file content indexed for search purposes. JBoss DNA also defines a library for "sequencing" content - to extract

Modified: trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -101,6 +101,15 @@
 </programlisting>
   </sect1>
 	<sect1>
+		<title>JCR Support</title>
+		<para>
+			JBoss DNA currently supports most of the Level 1 and Level 2 feature set defined by the <ulink url="&JSR170;">JSR-170</ulink> specification.
+			Queries, which are part of Level 1, are not implemented, nor are all of the update methods that make up Level 2.
+			So while the current implementation provides many of the features that may be needed by an application, we really hope
+			that this release will allow you to give us some feedback on what we have so far.
+		</para>
+	</sect1>
+	<sect1>
 		<title>Summary</title>
 		<para>
 			In this chapter, we covered how to use JCR with JBoss DNA.  Now that you know how JBoss DNA repositories work,

Modified: trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -167,6 +167,24 @@
 			<link linkend="connector-library">existing connectors</link> and show how to <link linkend="custom-connectors">create your own connectors</link>.
 		</para>
   </sect1>
+  <sect1 id="repository-workspaces">
+    <title>Workspaces</title>
+		<para>The previous section talked about how connector expose their information through the graph language of JBoss DNA.
+			This is true, except that we didn't dive into too much of the detail.  JBoss DNA graphs have the notion of <emphasis>workspaces</emphasis>
+			in which the content appears, and its very easy for clients using the graph to switch between workspaces.  In fact,
+			workspaces differ from each other in that they provide different views of the same information.
+		</para>
+		<para>Consider a source control system, like SVN or CVS.  These systems provide different views of the source code:
+			a mainline development branch as well as other branches (or tags) commonly used for releases.  So, just like one source
+			file might appear in the mainline branch as well as the previous two release branches, a node in a repository source
+			might appear in multiple workspaces.
+		</para>
+		<para>
+			However, each connector can kind of decide how (or whether) it uses workspaces.  For example, there may be no overlap
+			in the content between workspaces.  Or a connector might only expose a single workspace (in other words, there's only one
+			"default" workspace).
+		</para>
+  </sect1>
   <sect1 id="repository-service">
     <title>Repository Service</title>
 		<para>The JBoss DNA &RepositoryService; is the component that manages the <emphasis>repository sources</emphasis>
@@ -309,6 +327,11 @@
 								cached by other, higher-level components.  The default value of null implies that this source does not define a specific
 								duration for caching information provided by this repository source.</entry>
 						</row>
+						<row>
+							<entry>defaultWorkspaceName</entry>
+							<entry>Optional property that is initialized to an empty string and which defines the name for the workspace that will be used by default
+								if none is specified.</entry>
+						</row>
 					</tbody>
 				</tgroup>
 			</table>
@@ -373,6 +396,19 @@
 								cached by other, higher-level components.  The default value of null implies that this source does not define a specific
 								duration for caching information provided by this repository source.</entry>
 						</row>
+						<row>
+							<entry>nameOfDefaultWorkspace</entry>
+							<entry>Optional property that is initialized to an empty string and which defines the name for the workspace that will be used by default
+								if none is specified.</entry>
+						</row>
+						<row>
+							<entry>predefinedWorkspaceNames</entry>
+							<entry>Optional property that defines the names of the workspaces that exist and that are available for use without having to create them.</entry>
+						</row>
+						<row>
+							<entry>creatingWorkspacesAllowed</entry>
+							<entry>Optional property that is by default 'true' that defines whether clients can create new workspaces.</entry>
+						</row>
 					</tbody>
 				</tgroup>
 			</table>
@@ -422,22 +458,25 @@
 				cache and federated sources.  This graph structure that is expected at this location is as follows:
 			</para>
 			<programlisting><![CDATA[<!-- Define the federation configuration. -->
-<dna:federation xmlns:dna="http://www.jboss.org/dna" 
+<dna:federatedRepository xmlns:dna="http://www.jboss.org/dna" 
 	                   xmlns:jcr="http://www.jcp.org/jcr/1.0"
                      dna:timeToCache="100000" >
-    <!-- Define how the content in the 'Cache' source is to map to the federated cache -->
-    <dna:cache>
-        <dna:projection jcr:name="Cache" dna:projectionRules="/ => /" />
-    </dna:cache>
-    <!-- Define how the content in the two sources maps to the federated/unified repository.
-         This example puts the 'Cars' and 'Aircraft' content underneath '/vehicles', but the
-         'Configuration' content (which is defined by this file) will appear under '/'. -->
-    <dna:projections>
+  <dna:workspaces>
+    <dna:workspace jcr:name="default">
+	    <!-- Define how the content in the 'Cache' source is to map to the federated cache -->
+      <dna:cache dna:sourceName="Cache" dna:workspaceName="default" dna:projectionRules="/a => /" />
+
+      <!-- Define how the content in the two sources maps to the federated/unified repository.
+           This example puts the 'Cars' and 'Aircraft' content underneath '/vehicles', but the
+           'Configuration' content (which is defined by this file) will appear under '/'. -->
+      <dna:projections>
         <dna:projection jcr:name="Cars" dna:projectionRules="/Vehicles => /" />
         <dna:projection jcr:name="Aircraft" dna:projectionRules="/Vehicles => /" />
         <dna:projection jcr:name="Configuration" dna:projectionRules="/ => /" />
-    </dna:projections>
-</dna:federation>
+      </dna:projections>
+    </dna:workspace>
+	</dna:workspaces>
+</dna:federatedRepository>
 ]]></programlisting>
 			<note>
 				<para>
@@ -488,6 +527,11 @@
 								&RepositoryLibrary;.</entry>
 						</row>
 						<row>
+							<entry>configurationWorkspaceName</entry>
+							<entry>The name of the workspace in the configuration &RepositorySource; with the content defining
+								how this federated repository is to be set up and configured.</entry>
+						</row>
+						<row>
 							<entry>configurationSourcePath</entry>
 							<entry>The path to the node in the configuration repository below which a "dna:federation" node exists with the 
 								graph structure describing how this federated repository is to be configured.</entry>
@@ -601,7 +645,7 @@
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-graph</artifactId>
-  <version>0.3</version>
+  <version>0.4</version>
 </dependency>
 	 ]]></programlisting>
 			<para>
@@ -616,14 +660,14 @@
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-graph</artifactId>
-  <version>0.3</version>
+  <version>0.4</version>
   <type>test-jar</type>
   <scope>test</scope>
 </dependency>
 <dependency>
   <groupId>org.jboss.dna</groupId>
   <artifactId>dna-common</artifactId>
-  <version>0.3</version>
+  <version>0.4</version>
   <type>test-jar</type>
   <scope>test</scope>
 </dependency>
@@ -907,7 +951,7 @@
 				information, although the connector should still perform as expected when requests have incomplete locations.
 			</para>
 			<table frame='all'>
-				<title>Types of Requests</title>
+				<title>Types of Node Operation Requests</title>
 				<tgroup cols='2' align='left' colsep='1' rowsep='1'>
 		      <colspec colname='c1' colwidth="1*"/>
 		      <colspec colname='c2' colwidth="1*"/>
@@ -921,62 +965,76 @@
 						<row>
 							<entry>ReadNodeRequest</entry>
 							<entry>
-								A request to read from the source a node's properties and children.
+								A request to read from the named workspace in the source a node's properties and children.
 								The node may be specified by path and/or by identification properties.
 								The connector returns all properties and the locations for all children,
-								or sets a &PathNotFoundException; error on the request if the node did not exist.
+								or sets a &PathNotFoundException; error on the request if the node did not exist in the workspace.
 								If the node is found, the connector sets on the request the actual location of the node (including the path and identification properties).
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
+							<entry>VerifyNodeExistsRequest</entry>
+							<entry>
+								A request to verify the existance of a node at the specified location in the named workspace of the source.
+								The connector returns all the actual location for the node if it exists, or
+								sets a &PathNotFoundException; error on the request if the node does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
+							</entry>
+						</row>
+						<row>
 							<entry>ReadAllPropertiesRequest</entry>
 							<entry>
-								A request to read from the source all of the properties of a node.
+								A request to read from the named workspace in the source all of the properties of a node.
 								The node may be specified by path and/or by identification properties.
 								The connector returns all properties that were found on the node,
-								or sets a &PathNotFoundException; error on the request if the node did not exist.
+								or sets a &PathNotFoundException; error on the request if the node did not exist in the workspace.
 								If the node is found, the connector sets on the request the actual location of the node (including the path and identification properties).
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
 							<entry>ReadPropertyRequest</entry>
 							<entry>
-								A request to read from the source a single property of a node.
+								A request to read from the named workspace in the source a single property of a node.
 								The node may be specified by path and/or by identification properties,
 								and the property is specified by name.
 								The connector returns the property if found on the node,
-								or sets a &PathNotFoundException; error on the request if the node or property did not exist.
+								or sets a &PathNotFoundException; error on the request if the node or property did not exist in the workspace.
 								If the node is found, the connector sets on the request the actual location of the node (including the path and identification properties).
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
 							<entry>ReadAllChildrenRequest</entry>
 							<entry>
-								A request to read from the source all of the children of a node.
+								A request to read from the named workspace in the source all of the children of a node.
 								The node may be specified by path and/or by identification properties.
 								The connector returns an ordered list of locations for each child found on the node,
 								an empty list if the node had no children,
-								or sets a &PathNotFoundException; error on the request if the node did not exist.
+								or sets a &PathNotFoundException; error on the request if the node did not exist in the workspace.
 								If the node is found, the connector sets on the request the actual location of the parent node (including the path and identification properties).
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
 							<entry>ReadBlockOfChildrenRequest</entry>
 							<entry>
-								A request to read from the source a block of children of a node, starting with the n<superscript>th</superscript> children.
+								A request to read from the named workspace in the source a block of children of a node, starting with the n<superscript>th</superscript> children.
 								This is designed to allow paging through the children, which is much more efficient for large numbers of children.
 								The node may be specified by path and/or by identification properties, and the block
 								is defined by a starting index and a count (i.e., the block size).
 								The connector returns an ordered list of locations for each of the node's children found in the block,
 								or an empty list if there are no children in that range.
 								The connector also sets on the request the actual location of the parent node (including the path and identification properties)
-								or sets a &PathNotFoundException; error on the request if the parent node did not exist.
+								or sets a &PathNotFoundException; error on the request if the parent node did not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
 							<entry>ReadNextBlockOfChildrenRequest</entry>
 							<entry>
-								A request to read from the source a block of children of a node, starting with the children that immediately follow
+								A request to read from the named workspace in the source a block of children of a node, starting with the children that immediately follow
 								a previously-returned child.
 								This is designed to allow paging through the children, which is much more efficient for large numbers of children.
 								The node may be specified by path and/or by identification properties, and the block
@@ -984,7 +1042,8 @@
 								The connector returns an ordered list of locations for each of the node's children found in the block,
 								or an empty list if there are no children in that range.
 								The connector also sets on the request the actual location of the parent node (including the path and identification properties)
-								or sets a &PathNotFoundException; error on the request if the parent node did not exist.
+								or sets a &PathNotFoundException; error on the request if the parent node did not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -997,7 +1056,8 @@
 								nodes found in the branch.  
 								The connector also sets on the request the actual location of the branch's root node (including the path and identification properties).
 								The connector sets a &PathNotFoundException; error on the request if the node at 
-								the top of the branch does not exist.
+								the top of the branch does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1009,7 +1069,8 @@
 								will have their SNS indexes adjusted.  However, if the requested location does not include a SNS index, the new
 								node is added after all existing children, and it's SNS index is set accordingly.)
 								The connector also sets on the request the actual location of the new node (including the path and identification properties)..
-								The connector sets a &PathNotFoundException; error on the request if the parent node does not exist.
+								The connector sets a &PathNotFoundException; error on the request if the parent node does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1018,7 +1079,8 @@
 								A request to remove a set of properties on an existing node.  The request contains the location of the node as well as the
 								names of the properties to be removed.  The connector performs these changes and sets on the request the
 								actual location (including the path and identification properties) of the node.
-								The connector sets a &PathNotFoundException; error on the request if the node does not exist.
+								The connector sets a &PathNotFoundException; error on the request if the node does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1027,7 +1089,8 @@
 								A request to set or update properties on an existing node.  The request contains the location of the node as well as the
 								properties to be set and those to be deleted.  The connector performs these changes and sets on the request the
 								actual location (including the path and identification properties) of the node.
-								The connector sets a &PathNotFoundException; error on the request if the node does not exist.
+								The connector sets a &PathNotFoundException; error on the request if the node does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1036,19 +1099,23 @@
 								A request to change the name of a node.  The connector changes the node's name, adjusts all SNS indexes
 								accordingly, and returns the actual locations (including the path and identification properties) of both the original
 								location and the new location.
-								The connector sets a &PathNotFoundException; error on the request if the node does not exist.
+								The connector sets a &PathNotFoundException; error on the request if the node does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
 							<entry>CopyBranchRequest</entry>
 							<entry>
 								A request to copy a portion of a subgraph that has as its root a particular node, up to a maximum depth.
+								The request includes the name of the workspace where the original node is located as well as the name of the
+								workspace where the copy is to be placed (these may be the same, but may be different).
 								The connector copies the branch from the original location, up to the specified maximum depth, and places a copy
 								of the node as a child of the new location.
 								The connector also sets on the request the actual location (including the path and identification properties)
 								of the original location as well as the location of the new copy.
 								The connector sets a &PathNotFoundException; error on the request if the node at 
-								the top of the branch does not exist.
+								the top of the branch does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if one of the named workspaces does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1059,7 +1126,8 @@
 								The connector also sets on the request the actual location (including the path and identification properties)
 								of the original and new locations.  The connector will adjust SNS indexes accordingly.
 								The connector sets a &PathNotFoundException; error on the request if the node that is to be moved or the
-								new location do not exist.
+								new location do not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1068,7 +1136,8 @@
 								A request to delete an entire branch specified by a single node's location.
 								The connector deletes the specified node and all nodes below it, and sets the actual location,
 								including the path and identification properties, of the node that was deleted.
-								The connector sets a &PathNotFoundException; error on the request if the node being deleted does not exist.
+								The connector sets a &PathNotFoundException; error on the request if the node being deleted does not exist in the workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
 							</entry>
 						</row>
 						<row>
@@ -1082,6 +1151,60 @@
 					</tbody>
 				</tgroup>
 			</table>
+			<para>There are also requests that deal with workspaces:</para>
+			<table frame='all'>
+				<title>Types of Workspace Requests</title>
+				<tgroup cols='2' align='left' colsep='1' rowsep='1'>
+		      <colspec colname='c1' colwidth="1*"/>
+		      <colspec colname='c2' colwidth="1*"/>
+					<thead>
+						<row>
+				  		<entry>Name</entry>
+				  		<entry>Description</entry>
+						</row>
+					</thead>
+					<tbody>
+						<row>
+							<entry>GetWorkspacesRequest</entry>
+							<entry>
+								A request to obtain the names of the existing workspaces that are accessible to the caller.
+							</entry>
+						</row>
+						<row>
+							<entry>VerifyWorkspaceRequest</entry>
+							<entry>
+								A request to verify that a workspace with a particular name exists.
+								The connector returns the actual location for the root node if the workspace exists, as well as the actual name of the workspace
+								(e.g., the default workspace name if a null name is supplied).
+							</entry>
+						</row>
+						<row>
+							<entry>CreateWorkspaceRequest</entry>
+							<entry>
+								A request to create a workspace with a particular name.
+								The connector returns the actual location for the root node if the workspace exists, as well as the actual name of the workspace
+								(e.g., the default workspace name if a null name is supplied).
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace already exists.
+							</entry>
+						</row>
+						<row>
+							<entry>DestroyWorkspaceRequest</entry>
+							<entry>
+								A request to destroy a workspace with a particular name.
+								The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
+							</entry>
+						</row>
+						<row>
+							<entry>CloneWorkspaceRequest</entry>
+							<entry>
+								A request to clone one named workspace as another new named workspace.
+								The connector sets a &InvalidWorkspaceException; error on the request if the original workspace does not exist, 
+								or if the new workspace already exists.
+							</entry>
+						</row>
+					</tbody>
+				</tgroup>
+			</table>
 	    <para>
 				Although there are over a dozen different kinds of requests, we do anticipate adding more in future releases.
 				For example, DNA will likely support searching repository content in sources through an additional subclass of &Request;.
@@ -1101,10 +1224,10 @@
 			</para>
 			<note>
 				<para>
-					In many cases, the default implementations of the <code>process(...)</code> methods are <emphasis>sufficient</emphasis>
-					but probably not efficient or optimum.  If that is the case, simply provide your own methods that perform the request
-					in a manner that is efficient for your source.  However, if performance is not a big issue, all of the concrete methods
-					will provide the correct behavior.  And remember, you can always provide better implementations later.
+					The &RequestProcessor; abstract class contains default implementations for quite a few of the <code>process(...)</code> methods,
+					and these will be <emphasis>sufficient</emphasis>	but probably not efficient or optimum.  If you can provide a more efficient
+					implementation given your source, feel free to do so.  However, if performance is not a big issue, all of the concrete methods
+					will provide the correct behavior. Keep things simple to start out - you can always provide better implementations later.
 				</para>
 			</note>
 			<para>
@@ -1116,7 +1239,11 @@
 public void execute( final &ExecutionContext; context,
                      final &Request; request ) throws RepositorySourceException {
     RequestProcessor processor = new RequestProcessor(context);
-    processor.process(request);
+    try {
+        processor.process(request);
+    } finally {
+        processor.close();
+    }
 }</programlisting>
 			<para>
 				If you do this, the bulk of your connector implementation may be in the &RequestProcessor; implementation methods.
@@ -1191,8 +1318,67 @@
 			Just remember that under the covers, a &Graph; is just building &Request; objects, submitting them to the connector,
 			and then exposing the results.
 		</para>
+		<sect2 id="dna_graph_api_workspaces">
+			<title>Using workspaces</title>
 		<para>
-			Let's look at some examples of how the Graph API works.  This first example returns a map of properties (keyed by property name) 
+			Let's look at some examples of how the Graph API works.  This first example shows how to obtain the names of the available workspaces:
+		</para>
+		<programlisting>
+&Set;&lt;&String;> workspaceNames = graph.getWorkspaces();
+</programlisting>
+		<para>Once you know the name of the workspace, you can specify that the graph should use it:
+		</para>
+		<programlisting>
+graph.useWorkspace("myWorkspace");
+</programlisting>
+		<para>
+			From this point forward, all requests will apply to the workspace named "myWorkspace".  At any time, you can use a different workspace,
+			which will affect all subsequent requests made using the graph.  Of course, creating a new workspace is just as easy:
+		</para>
+		<programlisting>
+graph.createWorkspace().named("newWorkspace");
+</programlisting>
+	  <para>This will attempt to create a workspace named "newWorkspace", which will fail if that workspace already exists.  You may
+		want to create a new workspace with a name that should be altered if the name you supply is already used.  The following code shows
+		how you can do this:
+		</para>
+		<programlisting>
+graph.createWorkspace().namedSomethingLike("newWorkspace");
+</programlisting>
+	  <para>If there is no existing workspace named "newWorkspace", a new one will be created with this name.  However, if "newWorkspace" already
+		exists, this call will create a workspace with a name that is some alteration of the supplied name.
+		</para>
+	  <para>
+		You can also clone workspaces, too:
+		</para>
+		<programlisting>
+graph.createWorkspace().clonedFrom("original").named("something");
+</programlisting>
+		<para>
+			or
+		</para>
+		<programlisting>
+graph.createWorkspace().clonedFrom("original").namedSomethingLike("something");
+</programlisting>
+		<para>
+			As you can see, it's very easy to specify which workspace you want to use or to create new workspaces.  You can also find out which workspace
+			the graph is currently using:
+		</para>
+		<programlisting>
+&String; current = graph.getCurrentWorkspaceName();
+</programlisting>
+		<para>or, if you want, you can get more information about the workspace:
+		</para>
+		<programlisting>
+&Workspace; current = graph.getCurrentWorkspace();
+&String; name = current.getName();
+&Location; rootLocation = current.getRoot();
+</programlisting>
+    </sect2>
+		<sect2 id="dna_graph_api_nodes">
+			<title>Working with nodes</title>
+		<para>
+			Now let's switch to working with nodes.  This first example returns a map of properties (keyed by property name) 
 			for a node at a specific &Path;:
 		</para>
 		<programlisting>
@@ -1268,6 +1454,7 @@
 			The &Graph; interface is actually quite complete and offers a full-featured approach for reading and updating a graph.
 			For more information, see the &Graph; JavaDocs.
 		</para>
+	</sect2>
 	</sect1>
 	<sect1>
 		<title>Summary</title>

Modified: trunk/docs/reference/src/main/docbook/en-US/content/testing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/testing.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/content/testing.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -129,4 +129,24 @@
 			to do it quickly and in a way that makes it easy to understand what the code is supposed to do (or not do).
 		</para>
 	</sect1>
+  <sect1 id="tck-tests">
+    <title>Technology Compatibility Kit (TCK) tests</title>
+    <para>
+    	Many Java specifications provide TCK test suites that can be used to check or verify that an implementation
+      correctly implements the API or SPI defined by the specification.  These TCK tests vary by technology, but
+			<ulink url="&JSR170;">JSR-170</ulink> does provide TCK tests that ensure that a JCR repository implementation exhibits the correct and expected
+			behavior.
+	  </para>
+	  <para>
+		  JBoss DNA has not yet passed enough of the TCK tests to publish the results.  We still have to implement
+		  queries, which is a required feature of Level 1 repositories.  However, suffice to say that JBoss DNA has passed
+		  many of the individual tests that make up the Level 1 and Level 2 tests, and it is a major objective of the next
+		  release to pass the remaining Level 1 and Level 2 tests (along with some other optional features).
+    </para>
+		<para>
+			JBoss DNA also frequently runs the JCR unit tests from the Apache Jackrabbit project.  (Those these tests are not
+			the official TCK, they apparently are used within the official TCK.)  These unit tests are set up in the
+			<code>dna-jcr-tck</code> project.
+		</para>
+  </sect1>
 </chapter>

Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-04-30 22:11:02 UTC (rev 870)
@@ -1,4 +1,4 @@
-<!ENTITY versionNumber "0.3">
+<!ENTITY versionNumber "0.4">
 <!ENTITY copyrightYears "2008-2009">
 <!ENTITY copyrightHolder "Red Hat, Inc.">
 
@@ -50,7 +50,7 @@
 
 <!ENTITY Logger							       		"<ulink url='&API;common/util/Logger.html'><interface>Logger</interface></ulink>">
 <!ENTITY ClassLoaderFactory       		"<ulink url='&API;common/component/ClassLoaderFactory.html'><interface>ClassLoaderFactory</interface></ulink>">
-<!ENTITY StandardClassLoaderFactory		"<ulink url='&API;common/component/ClassLoaderFactory.html'><classname>StandardClassLoaderFactory</classname></ulink>">
+<!ENTITY StandardClassLoaderFactory		"<ulink url='&API;common/component/StandardClassLoaderFactory.html'><classname>StandardClassLoaderFactory</classname></ulink>">
 
 <!-- Types in dna-graph -->
 
@@ -58,6 +58,7 @@
 <!ENTITY GraphBatch				       			"<ulink url='&API;graph/Graph.Batch.html'><classname>Graph.Batch</classname></ulink>">
 <!ENTITY Subgraph							       	"<ulink url='&API;graph/Subgraph.html'><interface>Subgraph</interface></ulink>">
 <!ENTITY Node								       		"<ulink url='&API;graph/Node.html'><interface>Node</interface></ulink>">
+<!ENTITY Workspace								    "<ulink url='&API;graph/Workspace.html'><interface>Workspace</interface></ulink>">
 <!ENTITY Results							       	"<ulink url='&API;graph/Results.html'><interface>Results</interface></ulink>">
 <!ENTITY Location							       	"<ulink url='&API;graph/Location.html'><classname>Location</classname></ulink>">
 <!ENTITY ExecutionContext		       		"<ulink url='&API;graph/ExecutionContext.html'><classname>ExecutionContext</classname></ulink>">
@@ -74,13 +75,16 @@
 <!ENTITY RepositoryConnectionFactory	"<ulink url='&API;graph/connector/RepositoryConnectionFactory.html'><interface>RepositoryConnectionFactory</interface></ulink>">
 <!ENTITY RepositorySourceListener			"<ulink url='&API;graph/connector/RepositorySourceListener.html'><interface>RepositorySourceListener</interface></ulink>">
 <!ENTITY RepositorySourceCapabilities	"<ulink url='&API;graph/connector/RepositorySourceCapabilities.html'><classname>RepositorySourceCapabilities</classname></ulink>">
-<!ENTITY InMemoryRepository			  		"<ulink url='&API;graph.connector/inmemory/InMemoryRepository.html'><classname>InMemoryRepository</classname></ulink>">
-<!ENTITY InMemoryRepositorySource  		"<ulink url='&API;graph.connector/inmemory/InMemoryRepositorySource.html'><classname>InMemoryRepositorySource</classname></ulink>">
+<!ENTITY InMemoryRepository			  		"<ulink url='&API;graph/connector/inmemory/InMemoryRepository.html'><classname>InMemoryRepository</classname></ulink>">
+<!ENTITY InMemoryRepositorySource  		"<ulink url='&API;graph/connector/inmemory/InMemoryRepositorySource.html'><classname>InMemoryRepositorySource</classname></ulink>">
 <!ENTITY CachePolicy							    "<ulink url='&API;graph/cache/CachePolicy.html'><interface>CachePolicy</interface></ulink>">
-<!ENTITY Request					       			"<ulink url='&API;graph/request/Requests.html'><classname>Request</classname></ulink>">
+<!ENTITY Request					       			"<ulink url='&API;graph/request/Request.html'><classname>Request</classname></ulink>">
 <!ENTITY CompositeRequest					    "<ulink url='&API;graph/request/CompositeRequest.html'><classname>CompositeRequest</classname></ulink>">
 <!ENTITY ReadNodeRequest					    "<ulink url='&API;graph/request/ReadNodeRequest.html'><classname>ReadNodeRequest</classname></ulink>">
 <!ENTITY CopyBranchRequest					  "<ulink url='&API;graph/request/CopyBranchRequest.html'><classname>CopyBranchRequest</classname></ulink>">
+<!ENTITY InvalidRequestException	   	"<ulink url='&API;graph/request/InvalidRequestException.html'><classname>InvalidRequestException</classname></ulink>">
+<!ENTITY InvalidWorkspaceException		"<ulink url='&API;graph/request/InvalidWorkspaceException.html'><classname>InvalidWorkspaceException</classname></ulink>">
+<!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 SequencerOutput		    			"<ulink url='&API;graph/sequencer/SequencerOutput.html'><interface>SequencerOutput</interface></ulink>">

Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -34,7 +34,7 @@
 		<subtitle>Reference Guide</subtitle>
 		<releaseinfo>&versionNumber;</releaseinfo>
 		<productnumber>&versionNumber;</productnumber>
-		<issuenum>1</issuenum>
+		<issuenum>4</issuenum>
 		<mediaobject>
 			<imageobject role="fo">
 				<imagedata fileref="dna-logo.png" align="center"/>

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-04-30 18:18:49 UTC (rev 869)
+++ trunk/pom.xml	2009-04-30 22:11:02 UTC (rev 870)
@@ -403,6 +403,11 @@
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-jcr-tck</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-connector-federation</artifactId>
 				<version>${pom.version}</version>
 			</dependency>
@@ -413,9 +418,59 @@
 			</dependency>
 			<dependency>
 				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-connector-store-jpa</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-connector-filesystem</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-connector-svn</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
 				<artifactId>dna-mimetype-detector-aperture</artifactId>
 				<version>${pom.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-cnd</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-images</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-java</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-mp3</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-msoffice</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-xml</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jboss.dna</groupId>
+				<artifactId>dna-sequencer-zip</artifactId>
+				<version>${pom.version}</version>
+			</dependency>
 			<!-- 
 		    Time and Date
 		    -->



From dna-commits at lists.jboss.org  Thu Apr 30 12:16:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 30 Apr 2009 12:16:00 -0400
Subject: [dna-commits] DNA SVN: r868 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/io and 6 other
	directories.
Message-ID: <E1LzYvY-0002Jw-Eb@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-04-30 12:15:59 -0400 (Thu, 30 Apr 2009)
New Revision: 868

Added:
   trunk/dna-graph/src/test/resources/xmlHandler/docWithNestedProperties.xml
Modified:
   trunk/dna-graph/pom.xml
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
   trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java
Log:
DNA-378 XML Graph Importer should support importing multi-valued properties

Applied the patch, which adds support for multi-valued properties as child elements (intermixed with child elements for nodes).

Modified: trunk/dna-graph/pom.xml
===================================================================
--- trunk/dna-graph/pom.xml	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/pom.xml	2009-04-30 16:15:59 UTC (rev 868)
@@ -32,6 +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>
     <!-- 
     Testing (note the scope)
     -->

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -66,6 +66,15 @@
                         Property... additionalProperties );
 
     /**
+     * Sets the given properties on the node at the supplied path. The path will be absolute.
+     * 
+     * @param path the absolute path of the node
+     * @param properties the remaining properties for the node
+     */
+    public void setProperties( Path path,
+                               Property... properties );
+
+    /**
      * Signal to this destination that any enqueued create requests should be submitted. Usually this happens at the end of the
      * document parsing, but an implementer must allow for it to be called multiple times and anytime during parsing.
      */

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -117,6 +117,18 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @see org.jboss.dna.graph.io.Destination#setProperties(org.jboss.dna.graph.property.Path, org.jboss.dna.graph.property.Property[])
+     */
+    public void setProperties( Path path, 
+                               Property... properties ) {
+        if (properties == null) return;
+        
+        batch.set(properties).on(path);
+    }
+
+    /**
+     * {@inheritDoc}
      * 
      * @see org.jboss.dna.graph.io.Destination#submit()
      */

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-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -94,7 +94,7 @@
                 return newRequest;
             }
             // We have at least one other request, so add the pending request ...
-            add(newRequest);
+            addPending();
             ++number;
         } else {
             // There is no pending request ...

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-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -24,6 +24,9 @@
 package org.jboss.dna.graph.xml;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -45,6 +48,8 @@
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.xml.sax.Attributes;
 import org.xml.sax.ext.DefaultHandler2;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
 
 /**
  * A {@link DefaultHandler2} specialization that responds to XML content events by creating the corresponding content in the
@@ -162,6 +167,20 @@
     protected final Object[] propertyValues = new Object[1];
 
     /**
+     * Character buffer to aggregate nested character data
+     * 
+     * @see ElementEntry
+     */
+    private StringBuilder characterDataBuffer = new StringBuilder();
+
+    /**
+     * Stack of pending {@link ElementEntry element entries} from the root of the imported content to the current node.
+     * 
+     * @see ElementEntry
+     */
+    private final LinkedList<ElementEntry> elementStack = new LinkedList<ElementEntry>();
+
+    /**
      * Create a handler that creates content in the supplied graph
      * 
      * @param destination the destination where the content should be sent.graph in which the content should be placed
@@ -310,6 +329,16 @@
         assert localName != null;
         Name nodeName = null;
 
+        ElementEntry element;
+        if (!elementStack.isEmpty()) {
+            // Add the parent
+            elementStack.peek().addAsNode();
+            element = new ElementEntry(elementStack.peek(), currentPath, null);
+        } else {
+            element = new ElementEntry(null, currentPath, null);
+        }
+        elementStack.addFirst(element);
+
         properties.clear();
         Object typePropertyValue = null;
         // Convert each of the attributes to a property ...
@@ -333,6 +362,7 @@
             // Check to see if this is an attribute that represents the node name (which may be null) ...
             if (nodeName == null && attributeName.equals(nameAttribute)) {
                 nodeName = nameFactory.create(attributes.getValue(i)); // don't use a decoder
+                element.setName(nodeName);
                 continue;
             }
             if (typePropertyValue == null && attributeName.equals(typeAttribute)) {
@@ -340,13 +370,13 @@
                 continue;
             }
             // Create a property for this attribute ...
-            Property property = createProperty(attributeName, attributes.getValue(i));
-            properties.add(property);
+            element.addProperty(attributeName, attributes.getValue(i));
         }
         // Create the node name if required ...
         if (nodeName == null) {
             // No attribute defines the node name ...
             nodeName = nameFactory.create(uri, localName, decoder);
+            element.setName(nodeName);
         } else {
             if (typePropertyValue == null) typePropertyValue = nameFactory.create(uri, localName, decoder);
         }
@@ -354,15 +384,12 @@
             // A attribute defines the node name. Set the type property, if required
             if (typePropertyValue == null) typePropertyValue = typeAttributeValue;
             if (typePropertyValue != null) {
-                propertyValues[0] = typePropertyValue;
-                Property property = propertyFactory.create(typeAttribute, propertyValues);
-                properties.add(property);
+                element.addProperty(typeAttribute, typePropertyValue);
             }
         }
+
         // Update the current path ...
-        currentPath = pathFactory.create(currentPath, nodeName);
-        // Create the node, and note that we don't care about same-name siblings (as the graph will correct them) ...
-        destination.create(currentPath, properties);
+        currentPath = element.path();
     }
 
     /**
@@ -374,6 +401,15 @@
     public void endElement( String uri,
                             String localName,
                             String name ) {
+
+        String s = characterDataBuffer.toString().trim();
+        if (s.length() > 0) {
+            elementStack.removeFirst().addAsPropertySetTo(s);
+        } else if (!elementStack.isEmpty()) {
+            elementStack.removeFirst().submit();
+        }
+        characterDataBuffer = new StringBuilder();
+
         // Nothing to do but to change the current path to be the parent ...
         currentPath = currentPath.getParent();
     }
@@ -381,6 +417,19 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+     */
+    @Override
+    public void characters( char[] ch,
+                            int start,
+                            int length ) {
+        // Have to add this to a buffer as one logical set of character data can cause this method to fire multiple times
+        characterDataBuffer.append(ch, start, length);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.xml.sax.helpers.DefaultHandler#endDocument()
      */
     @Override
@@ -402,8 +451,118 @@
     protected Property createProperty( Name propertyName,
                                        Object value ) {
         propertyValues[0] = value;
-        Property result = propertyFactory.create(propertyName, propertyValues);
-        return result;
+        return propertyFactory.create(propertyName, propertyValues);
     }
 
+    /**
+     * Create a property with the given name and values, obtained from an attribute name and value in the XML content.
+     * <p>
+     * By default, this method creates a property by directly using the values as the values of the property.
+     * </p>
+     * 
+     * @param propertyName the name of the property; never null
+     * @param values the attribute values
+     * @return the property; may not be null
+     */
+    protected Property createProperty( Name propertyName,
+                                       Collection<Object> values ) {
+        return propertyFactory.create(propertyName, values);
+    }
+
+    /**
+     * Possible states for an {@link ElementEntry} instance. All element entries start in state {@code TBD} and then transition to
+     * one of the terminating states, {@code NODE} or {@code PROPERTY} when {@link ElementEntry#addAsNode()} or
+     * {@link ElementEntry#addAsPropertySetTo(Object)} is invoked.
+     */
+    protected enum ElementEntryState {
+        NODE,
+        PROPERTY,
+        TBD
+    }
+
+    /**
+     * Element entries hold references to the data of "pending" elements. "Pending" elements are elements which have been
+     * encountered through a {@link XmlHandler#startElement(String, String, String, Attributes)} event but have not yet been fully
+     * committed to the {@link XmlHandler#destination}.
+     * <p>
+     * As the current import semantics allow elements with nested character data to be imported as properties, it is not always
+     * possible to determine whether the element represents a node or a property from within the {@code startElement} method.
+     * Therefore, {@code ElementEntries} are initially created in an {@link ElementEntryState#TBD unknown state} and submitted to
+     * the {@code destination} when it can be positively determined that the entry represents a property (if nested character data
+     * is encountered) or a node (if a child node is detected or the {@link XmlHandler#endElement(String, String, String)} method
+     * is invoked prior to encountering nested character data).
+     * </p>
+     * <p>
+     * As DNA does not currently support a way to add a value to an existing property through the Graph API, {@code
+     * ElementEntries} also contain a {@link Multimap} of property names to values. The node's properties are aggregated and only
+     * submitted to the {@code destination} when the {@link XmlHandler#endElement(String, String, String)} event fires.
+     * </p>
+     */
+    private class ElementEntry {
+
+        private ElementEntry parent;
+        // Stored separately since the root node has no parent ElementEntry but does have a path
+        private Path pathToParent;
+        private Path pathToThisNode;
+        private Name name;
+        private Multimap<Name, Object> properties;
+        private ElementEntryState state;
+
+        protected ElementEntry( ElementEntry parent,
+                                Path pathToParent,
+                                Name name ) {
+            this.parent = parent;
+            this.pathToParent = pathToParent;
+            this.name = name;
+            this.state = ElementEntryState.TBD;
+            properties = new LinkedHashMultimap<Name, Object>();
+        }
+
+        protected void setName( Name name ) {
+            this.name = name;
+            pathToThisNode = pathFactory.create(pathToParent, name);
+        }
+
+        protected void addProperty( Name propertyName,
+                                    Object propertyValue ) {
+            assert state != ElementEntryState.PROPERTY;
+            properties.put(propertyName, propertyValue);
+        }
+
+        protected void addAsNode() {
+            assert state != ElementEntryState.PROPERTY;
+            if (state == ElementEntryState.NODE) return;
+
+            state = ElementEntryState.NODE;
+            destination.create(pathFactory.create(pathToParent, name), Collections.<Property>emptyList());
+        }
+
+        protected void addAsPropertySetTo( Object value ) {
+            assert state != ElementEntryState.NODE;
+            state = ElementEntryState.PROPERTY;
+            parent.addProperty(name, value);
+        }
+
+        protected final Path path() {
+            return pathToThisNode;
+        }
+
+        protected void submit() {
+            if (state == ElementEntryState.PROPERTY) return;
+
+            if (state == ElementEntryState.NODE && properties.size() == 0) return;
+            Property[] propertiesToAdd = new Property[properties.size()];
+            int i = 0;
+            for (Name name : properties.keySet()) {
+                propertiesToAdd[i++] = createProperty(name, properties.get(name));
+            }
+
+            if (state == ElementEntryState.TBD) {
+                // Merge the add and the create
+                destination.create(pathToThisNode, Arrays.asList(propertiesToAdd));
+            } else {
+                destination.setProperties(pathToThisNode, propertiesToAdd);
+            }
+        }
+    }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -26,6 +26,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.stub;
 import java.io.File;
 import java.net.URI;
@@ -51,6 +52,8 @@
 import org.jboss.dna.graph.request.CompositeRequest;
 import org.jboss.dna.graph.request.CreateNodeRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.junit.Before;
 import org.junit.Test;
@@ -98,8 +101,8 @@
         assertThat(lastExecutedRequest, is(instanceOf(CompositeRequest.class)));
         Iterator<Request> iter = ((CompositeRequest)lastExecutedRequest).iterator();
         // assertCreateNode(iter, "/a/b/", "jcr:primaryType={http://www.jboss.org/dna/xml/1.0}document");
-        assertCreateNode(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
-        assertCreateNode(iter, "/a/b/dna:system[1]/dna:sources[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
+        assertCreateNode(iter, "/a/b/dna:system[1]");
+        assertCreateNode(iter, "/a/b/dna:system[1]/dna:sources[1]");
         assertCreateNode(iter,
                          "/a/b/dna:system[1]/dna:sources[1]/sourceA[1]",
                          "repositoryName=repositoryA",
@@ -111,6 +114,8 @@
                          "repositoryName=repositoryB",
                          "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured",
                          "dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
+        assertCreateProperties(iter, "/a/b/dna:system[1]/dna:sources[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
+        assertCreateProperties(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
         assertThat(iter.hasNext(), is(false));
     }
 
@@ -124,8 +129,60 @@
         Path parentPath = createNode.under().getPath();
         assertThat(parentPath, is(expectedPath.getParent()));
         assertThat(createNode.named(), is(expectedPath.getLastSegment().getName()));
+        
+        if (properties.length > 0) {
+            Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
+            for (Property prop : createNode.properties()) {
+                propertiesByName.put(prop.getName(), prop);
+            }
+            for (String propertyStr : properties) {
+                if (propertyStr == "any properties") {
+                    propertiesByName.clear();
+                    break;
+                }
+                Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
+                if (!matcher.matches()) continue;
+                System.out.println("Property: " + propertyStr + " ==> " + matcher);
+                Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
+                System.out.println("Property name: " + matcher.group(1));
+                String value = matcher.group(2); // doesn't handle multiple values!!
+                if (value.trim().length() == 0) value = null;
+                Property actual = propertiesByName.remove(propertyName);
+                Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
+                assertThat("missing property " + propertyName, actual, is(expectedProperty));
+            }
+            if (!propertiesByName.isEmpty()) {
+                System.out.println("Properties for " + path + "\n" + propertiesByName);
+            }
+            assertThat(propertiesByName.isEmpty(), is(true));
+        }
+    }
+
+    public void assertCreateProperties( Iterator<Request> iterator,
+                                        String path,
+                                        String... properties ) {
+        Request nextCommand = iterator.next();
+        
+        if (nextCommand instanceof UpdatePropertiesRequest) {
+            assertUpdateProperties((UpdatePropertiesRequest) nextCommand, path, properties);
+        }
+        else if (nextCommand instanceof SetPropertyRequest) {
+            assertSetProperty((SetPropertyRequest) nextCommand, path, properties);
+        }
+        else {
+            fail("Invalid next request type: " + nextCommand.getClass().getName());
+        }
+        
+    }
+    public void assertUpdateProperties( UpdatePropertiesRequest createNode,
+                                  String path,
+                                  String... properties ) {
+        Path expectedPath = context.getValueFactories().getPathFactory().create(path);
+        Path parentPath = createNode.changedLocation().getPath().getParent();
+        assertThat(parentPath, is(expectedPath.getParent()));
+        assertThat(createNode.changedLocation().getPath().getLastSegment().getName(), is(expectedPath.getLastSegment().getName()));
         Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
-        for (Property prop : createNode.properties()) {
+        for (Property prop : createNode.properties().values()) {
             propertiesByName.put(prop.getName(), prop);
         }
         for (String propertyStr : properties) {
@@ -150,6 +207,40 @@
         assertThat(propertiesByName.isEmpty(), is(true));
     }
 
+    public void assertSetProperty( SetPropertyRequest createNode,
+                                        String path,
+                                        String... properties ) {
+              Path expectedPath = context.getValueFactories().getPathFactory().create(path);
+              Path parentPath = createNode.changedLocation().getPath().getParent();
+              assertThat(parentPath, is(expectedPath.getParent()));
+              assertThat(createNode.changedLocation().getPath().getLastSegment().getName(), is(expectedPath.getLastSegment().getName()));
+              Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
+              Property prop = createNode.property();
+              propertiesByName.put(prop.getName(), prop);
+
+              for (String propertyStr : properties) {
+                  if (propertyStr == "any properties") {
+                      propertiesByName.clear();
+                      break;
+                  }
+                  Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
+                  if (!matcher.matches()) continue;
+                  System.out.println("Property: " + propertyStr + " ==> " + matcher);
+                  Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
+                  System.out.println("Property name: " + matcher.group(1));
+                  String value = matcher.group(2); // doesn't handle multiple values!!
+                  if (value.trim().length() == 0) value = null;
+                  Property actual = propertiesByName.remove(propertyName);
+                  Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
+                  assertThat("missing property " + propertyName, actual, is(expectedProperty));
+              }
+              if (!propertiesByName.isEmpty()) {
+                  System.out.println("Properties for " + path + "\n" + propertiesByName);
+              }
+              assertThat(propertiesByName.isEmpty(), is(true));
+          }
+
+    
     protected class MockRepositoryConnection implements RepositoryConnection {
         public void close() {
         }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -146,7 +146,11 @@
 
     @Test
     public void shouldParseXmlDocumentWithoutNamespaces() throws IOException, SAXException {
+        System.out.println("\n");
+        System.out.flush();
         parse("xmlHandler/docWithoutNamespaces.xml");
+        System.out.println("\n");
+        System.out.flush();
         // Check the generated content; note that the attribute name doesn't match, so the nodes don't get special names
         assertNode("Cars");
         assertNode("Cars/Hybrid");
@@ -350,14 +354,18 @@
         parse("xmlHandler/docWithoutNamespaces.xml");
         // Check the generated content; note that the attribute name doesn't match, so the nodes don't get special names
         String unstructPrimaryType = "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured";
-        assertNode("Cars", unstructPrimaryType);
-        assertNode("Cars/Hybrid", unstructPrimaryType);
+        assertNode("Cars");
+        assertNode("Cars/Hybrid");
         assertNode("Cars/Hybrid/car", unstructPrimaryType, "name=Toyota Prius", "maker=Toyota", "model=Prius");
         assertNode("Cars/Hybrid/car", unstructPrimaryType, "name=Toyota Highlander", "maker=Toyota", "model=Highlander");
         assertNode("Cars/Hybrid/car", unstructPrimaryType, "name=Nissan Altima", "maker=Nissan", "model=Altima");
-        assertNode("Cars/Sports", unstructPrimaryType);
+        assertProperties("Cars/Hybrid", unstructPrimaryType);
+        assertNode("Cars/Sports");
         assertNode("Cars/Sports/car", unstructPrimaryType, "name=Aston Martin DB9", "maker=Aston Martin", "model=DB9");
         assertNode("Cars/Sports/car", unstructPrimaryType, "name=Infiniti G37", "maker=Infiniti", "model=G37");
+        assertProperties("Cars/Sports", unstructPrimaryType);
+        assertProperties("Cars", unstructPrimaryType);
+        
     }
 
     @Test
@@ -371,21 +379,94 @@
         // Check the generated content; note that the attribute name doesn't match, so the nodes don't get special names
         String unstructPrimaryType = "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured";
         String carPrimaryType = "jcr:primaryType={http://default.namespace.com}car";
-        assertNode("c:Cars", unstructPrimaryType);
-        assertNode("c:Cars/c:Hybrid", unstructPrimaryType);
+        assertNode("c:Cars");
+        assertNode("c:Cars/c:Hybrid");
         assertNode("c:Cars/c:Hybrid/c:Toyota Prius", carPrimaryType, "c:maker=Toyota", "c:model=Prius");
         assertNode("c:Cars/c:Hybrid/c:Toyota Highlander", carPrimaryType, "c:maker=Toyota", "c:model=Highlander");
         assertNode("c:Cars/c:Hybrid/c:Nissan Altima", carPrimaryType, "c:maker=Nissan", "c:model=Altima");
-        assertNode("c:Cars/c:Sports", unstructPrimaryType);
+        assertProperties("c:Cars/c:Hybrid", unstructPrimaryType);
+        assertNode("c:Cars/c:Sports");
         assertNode("c:Cars/c:Sports/c:Aston Martin DB9", carPrimaryType, "c:maker=Aston Martin", "c:model=DB9");
         assertNode("c:Cars/c:Sports/c:Infiniti G37", carPrimaryType, "c:maker=Infiniti", "c:model=G37");
+        assertProperties("c:Cars/c:Sports", unstructPrimaryType);
+        assertProperties("c:Cars", unstructPrimaryType);
     }
 
+    @Test
+    public void shouldParseXmlDocumentWithNestedPropertiesShouldPlaceContentUnderRootNode() throws IOException, SAXException {
+        context.getNamespaceRegistry().register("jcr", "http://www.jcp.org/jcr/1.0");
+        handler = new XmlHandler(destination, skipRootElement, parentPath, decoder, nameAttribute, typeAttribute,
+                                 typeAttributeValue, scoping);
+        parse("xmlHandler/docWithNestedProperties.xml");
+        // Check the generated content; note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
+        assertNode("Cars");
+        assertNode("Cars/Hybrid");
+        assertNode("Cars/Hybrid/car", "name=Toyota Prius", "maker=Toyota", "model=Prius");
+        assertNode("Cars/Hybrid/car", "name=Toyota Highlander", "maker=Toyota", "model=Highlander");
+        assertNode("Cars/Hybrid/car");
+        assertProperties("Cars/Hybrid/car", "name=Nissan Altima", "maker=Nissan", "model=Altima");
+        assertNode("Cars/Sports");
+        assertNode("Cars/Sports/car", "name=Aston Martin DB9", "maker=Aston Martin", "model=DB9");
+        assertNode("Cars/Sports/car");
+        assertNode("Cars/Sports/car/driver", "name=Tony Stewart");
+        assertProperties("Cars/Sports/car", "name=Infiniti G37", "maker=Infiniti", "model=G37", "category=Turbocharged=My Sedan");
+        assertNode("Cars/Sports/car");
+        assertNode("Cars/Sports/car/jcr:xmltext", "jcr:xmlcharacters=This is my text ");        
+        assertNode("Cars/Sports/car/jcr:xmltext", "jcr:xmlcharacters=that should be merged");
+        assertProperties("Cars/Sports/car", "name=Infiniti G37", "maker=Infiniti", "model=G37");
+    }
+
     protected void assertNode( String path,
                                String... properties ) {
         // Create the expected path ...
         PathFactory factory = context.getValueFactories().getPathFactory();
         Path expectedPath = parentPath != null ? factory.create(parentPath, path) : factory.create("/" + path);
+        // Now get the next request and compare the expected and actual ...
+        CreateNodeRequest request = requests.remove();
+        Path parentPath = request.under().getPath();
+        assertThat(parentPath, is(expectedPath.getParent()));
+        assertThat(request.named(), is(expectedPath.getLastSegment().getName()));
+
+        if (properties.length != 0) {
+            // Create the list of properties ...
+            Map<Name, Property> expectedProperties = new HashMap<Name, Property>();
+            for (String propertyString : properties) {
+                String[] strings = propertyString.split("=");
+                if (strings.length < 2) continue;
+                Name name = context.getValueFactories().getNameFactory().create(strings[0]);
+                Object[] values = new Object[strings.length - 1];
+                for (int i = 1; i != strings.length; ++i) {
+                    values[i - 1] = strings[i];
+                }
+                Property property = context.getPropertyFactory().create(name, values);
+                expectedProperties.put(name, property);
+            }
+
+            for (Property actual : request.properties()) {
+                Property expected = expectedProperties.remove(actual.getName());
+                assertThat("unexpected property: " + actual, expected, is(notNullValue()));
+                assertThat(actual, is(expected));
+            }
+            if (!expectedProperties.isEmpty()) {
+                StringBuilder msg = new StringBuilder("missing actual properties: ");
+                boolean isFirst = true;
+                for (Property expected : expectedProperties.values()) {
+                    if (!isFirst) msg.append(", ");
+                    else isFirst = false;
+                    msg.append(expected.getName());
+                }
+                msg.append(" on node ").append(request.under());
+                System.out.println("Found properties: " + request.properties());
+                assertThat(msg.toString(), expectedProperties.isEmpty(), is(true));
+            }
+        }
+    }
+
+    protected void assertProperties( String path,
+                               String... properties ) {
+        // Create the expected path ...
+        PathFactory factory = context.getValueFactories().getPathFactory();
+        Path expectedPath = parentPath != null ? factory.create(parentPath, path) : factory.create("/" + path);
         // Create the list of properties ...
         Map<Name, Property> expectedProperties = new HashMap<Name, Property>();
         for (String propertyString : properties) {
@@ -399,12 +480,13 @@
             Property property = context.getPropertyFactory().create(name, values);
             expectedProperties.put(name, property);
         }
-        // Now get the next request and compare the expected and actual ...
-        CreateNodeRequest request = requests.remove();
-        Path parentPath = request.under().getPath();
+
+        CreateNodeRequest propertyRequest = requests.remove();
+        Path parentPath = propertyRequest.under().getPath();
         assertThat(parentPath, is(expectedPath.getParent()));
-        assertThat(request.named(), is(expectedPath.getLastSegment().getName()));
-        for (Property actual : request.properties()) {
+        assertThat(propertyRequest.named(), is(expectedPath.getLastSegment().getName()));
+
+        for (Property actual : propertyRequest.properties()) {
             Property expected = expectedProperties.remove(actual.getName());
             assertThat("unexpected property: " + actual, expected, is(notNullValue()));
             assertThat(actual, is(expected));
@@ -417,6 +499,8 @@
                 else isFirst = false;
                 msg.append(expected.getName());
             }
+            msg.append(" on node ").append(propertyRequest.under());
+            System.out.println("Found properties: " + propertyRequest.properties());
             assertThat(msg.toString(), expectedProperties.isEmpty(), is(true));
         }
     }
@@ -481,6 +565,21 @@
             }
         }
 
+        public void setProperties( Path path, 
+                                   Property... properties ) {
+            if (properties.length == 0) {
+                return;
+            }
+            else if (properties.length == 1) {
+                create(path, properties[0]);
+            }
+            else {
+               Property[] additionalProperties = new Property[properties.length - 1];
+               System.arraycopy(properties, 1, additionalProperties, 0, properties.length - 1);
+               create(path, properties[0], additionalProperties);
+            }
+        }        
+        
         @SuppressWarnings( "synthetic-access" )
         public ExecutionContext getExecutionContext() {
             return XmlHandlerTest.this.context;

Added: trunk/dna-graph/src/test/resources/xmlHandler/docWithNestedProperties.xml
===================================================================
--- trunk/dna-graph/src/test/resources/xmlHandler/docWithNestedProperties.xml	                        (rev 0)
+++ trunk/dna-graph/src/test/resources/xmlHandler/docWithNestedProperties.xml	2009-04-30 16:15:59 UTC (rev 868)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Cars xmlns:jcr="http://www.jcp.org/jcr/1.0">
+    <Hybrid>
+        <car name="Toyota Prius" maker="Toyota" model="Prius"/>
+        <car name="Toyota Highlander" maker="Toyota" model="Highlander"/>
+        <car name="Nissan Altima">
+            <property jcr:name="maker">Nissan</property>
+            <model>Altima</model>
+        </car>
+    </Hybrid>
+    <Sports>
+        <car name="Aston Martin DB9" maker="Aston Martin" model="DB9"/>
+        <car name="Infiniti G37">
+            <maker>Infiniti</maker>
+            <category>Turbocharged</category>
+            <driver name="Tony Stewart" />
+            <property jcr:name="model">G37</property>
+            <category>My <![CDATA[Sedan]]></category>
+        </car>
+        <car name="Infiniti G37" maker="Infiniti" model="G37">
+	       <unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my text " />
+	       <unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="that should be merged" />        
+        </car>
+    </Sports>
+</Cars>
\ No newline at end of file


Property changes on: trunk/dna-graph/src/test/resources/xmlHandler/docWithNestedProperties.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java
===================================================================
--- trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java	2009-04-30 15:31:52 UTC (rev 867)
+++ trunk/extensions/dna-sequencer-cnd/src/main/java/org/jboss/dna/sequencer/cnd/CndSequencer.java	2009-04-30 16:15:59 UTC (rev 868)
@@ -111,6 +111,19 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.io.Destination#setProperties(org.jboss.dna.graph.property.Path,
+         *      org.jboss.dna.graph.property.Property[])
+         */
+        public void setProperties( Path path,
+                                   Property... properties ) {
+            for (Property property : properties) {
+                output.setProperty(path, property.getName(), property.getValues());
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.io.Destination#submit()
          */
         public void submit() {



