From dna-commits at lists.jboss.org  Sun Mar  1 19:23:52 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sun, 01 Mar 2009 19:23:52 -0500
Subject: [dna-commits] DNA SVN: r739 -
	trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn.
Message-ID: <E1Ldvwm-0002fD-DT@committer01.frg.pub.inap.atl.jboss.com>

Author: spagop
Date: 2009-03-01 19:23:52 -0500 (Sun, 01 Mar 2009)
New Revision: 739

Modified:
   trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
Log:
CreateNodeRequest - add folder and file in a svn repos.

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-02-27 22:21:50 UTC (rev 738)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java	2009-03-02 00:23:52 UTC (rev 739)
@@ -23,11 +23,14 @@
  */
 package org.jboss.dna.connector.svn;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.connector.scm.ScmAction;
@@ -73,6 +76,7 @@
 import org.tmatesoft.svn.core.SVNProperty;
 import org.tmatesoft.svn.core.io.ISVNEditor;
 import org.tmatesoft.svn.core.io.SVNRepository;
+import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
 
 /**
  * The {@link RequestProcessor} implementation for the file subversion repository connector. This is the class that does the bulk
@@ -128,6 +132,7 @@
     public void process( CreateNodeRequest request ) {
         logger.trace(request.toString());
         verifyUpdatesAllowed();
+        // get the parent location of the new node
         Location myLocation = request.under();
         Path parent = getPathFor(myLocation, request);
         try {
@@ -152,18 +157,29 @@
                 SVNException ex = new SVNException(err);
                 request.setError(ex);
             } else if (rootKind == SVNNodeKind.DIR) {
-
-                // TODO if node is a file
-
-                // if the node is a directory
-                String childName = request.named().getString(getExecutionContext().getNamespaceRegistry());
-                if (root.length() == 1 && root.charAt(0) == '/') {
-                    // test if so a directory does not exist.
-                    mkdir("", childName, request.toString());
-                } else {
-                    if (root.length() > 1 && root.charAt(0) == '/') {
-                        // test if so a directory does not exist.
-                        mkdir(root.substring(1), childName, request.toString());
+                Collection<Property> childNodeProperties = request.properties();
+                Object[] objs = organizeThings(childNodeProperties);
+                for (Object object : objs) {
+                    if (object instanceof Name && ((Name)object).compareTo(JcrNtLexicon.FOLDER) == 0) {
+                        // process folder creation
+                        // if the node is a directory
+                        String folderName = request.named().getString(getExecutionContext().getNamespaceRegistry());
+                        if (root.length() == 1 && root.charAt(0) == '/') {
+                            // test if so a directory does not exist.
+                            mkdir("", folderName, request.toString());
+                        } else {
+                            if (root.length() > 1 && root.charAt(0) == '/') {
+                                // test if so a directory does not exist.
+                                mkdir(root.substring(1), folderName, request.toString());
+                            }
+                        }
+                    } else if (object instanceof Name && ((Name)object).compareTo(JcrNtLexicon.FILE) == 0) {
+                        String fileName = request.named().getString(getExecutionContext().getNamespaceRegistry());
+                        byte[] content = getContent(objs);
+                        // TODO: what is with the created on
+                        // Date createdOn = getCreatedOn(objs);
+                        // commit in to the repository
+                        newFile(root, fileName, content, request.toString());
                     }
                 }
             }
@@ -710,6 +726,28 @@
     }
 
     /**
+     * Create a file.
+     * @param path
+     * @param file
+     * @param content
+     * @param message
+     * @throws SVNException
+     */
+    private void newFile( String path,
+                          String file,
+                          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);
+        } else {
+            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Item with name '{0}' can't be created (already exist)", file);
+            throw new SVNException(err);
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.connector.scm.ScmActionFactory#addDirectory(java.lang.String, java.lang.String)
@@ -726,8 +764,8 @@
      */
     public ScmAction addFile( String path,
                               String file,
-                              byte[] content ) {
-        return null;
+                              byte[] content) {
+        return new AddFile(path, file, content);
     }
 
     /**
@@ -790,4 +828,63 @@
             closeDirectories(editor, this.root);
         }
     }
+
+    public static class AddFile implements ScmAction {
+        private String path;
+        private String file;
+        private byte[] content;
+
+        public AddFile( String path,
+                        String file,
+                        byte[] content ) {
+            this.path = path;
+            this.file = file;
+            this.content = content;
+        }
+
+        public void applyAction( Object context ) throws Exception {
+            ISVNEditor editor = (ISVNEditor)context;
+            openDirectories(editor, path);
+
+            editor.addFile(path + "/" + file, null, -1);
+            editor.applyTextDelta(path + "/" + file, null);
+            SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
+            String checksum = deltaGenerator.sendDelta(path + "/" + file, new ByteArrayInputStream(this.content), editor, true);
+            editor.closeFile(path + "/" + file, checksum);
+
+            closeDirectories(editor, path);
+
+        }
+
+    }
+
+//    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;
+        for (Object object : objs) {
+            if (object != null && object instanceof Binary) {
+                Binary buf = (Binary)object;
+                content = buf.getBytes();
+            }
+        }
+        return content;
+    }
+
+    private Object[] organizeThings( Collection<Property> childNodeProperties ) {
+        Set<Object> result = new HashSet<Object>();
+        for (Property property : childNodeProperties) {
+            result.add(property.getFirstValue());
+        }
+        return result.toArray();
+    }
 }



From dna-commits at lists.jboss.org  Sun Mar  1 19:28:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sun, 01 Mar 2009 19:28:30 -0500
Subject: [dna-commits] DNA SVN: r740 -
	trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn.
Message-ID: <E1Ldw1G-0004D9-6o@committer01.frg.pub.inap.atl.jboss.com>

Author: spagop
Date: 2009-03-01 19:28:30 -0500 (Sun, 01 Mar 2009)
New Revision: 740

Modified:
   trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
Log:
CreateNodeRequest - add folder and file in a svn repos.

Modified: trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java	2009-03-02 00:23:52 UTC (rev 739)
+++ trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java	2009-03-02 00:28:30 UTC (rev 740)
@@ -284,6 +284,18 @@
         SVNNodeKind nodeC1Kind = repository.checkPath("nodeC/nodeC_1", -1);
         assertThat(nodeC1Kind, is(SVNNodeKind.DIR));
 
+        byte[] content1 = "My content".getBytes();
+        Property jcrDataProperty = propertyFactory().create(JcrLexicon.DATA, binaryFactory().create(content1));
+        graph.batch().create("/nodeC/nodeC_1/file1.txt").with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE)).and(propertyFactory().create(JcrLexicon.CREATED,
+                                                                                                                                                                 new Date())).and(jcrDataProperty).execute();
+
+        // Look up the file
+        Node nodeC1 = graph.getNodeAt("/nodeC/nodeC_1");
+        assertThat(nodeC1, is(notNullValue()));
+        assertThat(nodeC1.getChildren().size(), is(1));
+        SVNNodeKind fileKind = repository.checkPath("/nodeC/nodeC_1/file1.txt", -1);
+        assertThat(fileKind, is(SVNNodeKind.FILE));
+
     }
 
     protected Collection<String> containsPaths( Collection<Location> locations ) {



From dna-commits at lists.jboss.org  Sun Mar  1 19:30:52 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sun, 01 Mar 2009 19:30:52 -0500
Subject: [dna-commits] DNA SVN: r741 -
	trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn.
Message-ID: <E1Ldw3Y-0005eM-SR@committer01.frg.pub.inap.atl.jboss.com>

Author: spagop
Date: 2009-03-01 19:30:52 -0500 (Sun, 01 Mar 2009)
New Revision: 741

Modified:
   trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java
Log:
CreateNodeRequest - add folder and file in a svn repos.

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-03-02 00:28:30 UTC (rev 740)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryRequestProcessor.java	2009-03-02 00:30:52 UTC (rev 741)
@@ -158,7 +158,7 @@
                 request.setError(ex);
             } else if (rootKind == SVNNodeKind.DIR) {
                 Collection<Property> childNodeProperties = request.properties();
-                Object[] objs = organizeThings(childNodeProperties);
+                Object[] objs = values(childNodeProperties);
                 for (Object object : objs) {
                     if (object instanceof Name && ((Name)object).compareTo(JcrNtLexicon.FOLDER) == 0) {
                         // process folder creation
@@ -880,7 +880,7 @@
         return content;
     }
 
-    private Object[] organizeThings( Collection<Property> childNodeProperties ) {
+    private Object[] values( Collection<Property> childNodeProperties ) {
         Set<Object> result = new HashSet<Object>();
         for (Property property : childNodeProperties) {
             result.add(property.getFirstValue());



From dna-commits at lists.jboss.org  Mon Mar  2 12:40:38 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 02 Mar 2009 12:40:38 -0500
Subject: [dna-commits] DNA SVN: r742 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 2 other directories.
Message-ID: <E1LeC86-0005vh-6B@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-02 12:40:37 -0500 (Mon, 02 Mar 2009)
New Revision: 742

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.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/JcrNodeTypeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java
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/JcrLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
   trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml
Log:
DNA-91 Implement node type management

Applied the patch, which is a good design for node type management.

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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -26,6 +26,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -48,6 +49,7 @@
 import javax.jcr.version.VersionHistory;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path.Segment;
 
@@ -225,8 +227,20 @@
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
-    public NodeType[] getMixinNodeTypes() {
-        throw new UnsupportedOperationException();
+    public NodeType[] getMixinNodeTypes() throws RepositoryException {
+        PropertyIterator mixinProperties = getProperties(JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext().getNamespaceRegistry()));
+        List<NodeType> mixinNodeTypes = new ArrayList<NodeType>((int)mixinProperties.getSize());
+
+        while (mixinProperties.hasNext()) {
+            Property property = mixinProperties.nextProperty();
+
+            String nodeTypeName = property.getValue().getString();
+            NodeType nodeType = session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
+
+            mixinNodeTypes.add(nodeType);
+        }
+
+        return mixinNodeTypes.toArray(new NodeType[mixinNodeTypes.size()]);
     }
 
     /**
@@ -308,8 +322,16 @@
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getPrimaryNodeType()
      */
-    public NodeType getPrimaryNodeType() {
-        throw new UnsupportedOperationException();
+    public NodeType getPrimaryNodeType() throws RepositoryException {
+        String primaryTypeName = JcrLexicon.PRIMARY_TYPE.getString(session.getExecutionContext().getNamespaceRegistry());
+        Property primaryNodeTypeProperty = getProperty(primaryTypeName);
+        Value nodeValue = primaryNodeTypeProperty.getValue();
+
+        ExecutionContext context = session.getExecutionContext();
+        Name nodeValueAsName = context.getValueFactories().getNameFactory().create(nodeValue.getString());
+
+        String nodeTypeName = nodeValueAsName.getString(context.getNamespaceRegistry());
+        return session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
     }
 
     /**
@@ -324,12 +346,19 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getProperties(java.lang.String)
      */
-    public PropertyIterator getProperties( String namePattern ) {
+    public PropertyIterator getProperties( String namePattern ) throws RepositoryException {
         // TODO: Implement after changing impl to delegate to Graph API
-        throw new UnsupportedOperationException();
+
+        // Implementing exact-matching only for now to prototype types as properties
+        Set<Property> matchingProps = new HashSet<Property>();
+        for (Property prop : properties) {
+            String propName = prop.getName();
+            if (propName.equals(namePattern)) matchingProps.add(prop);
+        }
+
+        return new JcrPropertyIterator(matchingProps);
     }
 
     /**
@@ -522,7 +551,20 @@
      * @return <code>false</code>
      * @see javax.jcr.Node#isNodeType(java.lang.String)
      */
-    public boolean isNodeType( String nodeTypeName ) {
+    public boolean isNodeType( String nodeTypeName ) throws RepositoryException {
+        NodeType nodeType = getPrimaryNodeType();
+
+        if (nodeType.isNodeType(nodeTypeName)) {
+            return true;
+        }
+
+        NodeType[] mixinNodeTypes = getMixinNodeTypes();
+        for (int i = 0; i < mixinNodeTypes.length; i++) {
+            if (mixinNodeTypes[i].isNodeType(nodeTypeName)) {
+                return true;
+            }
+        }
+
         return false;
     }
 

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,638 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
+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.
+ */
+ at Immutable
+class JcrBuiltinNodeTypeSource implements JcrNodeTypeSource {
+
+    // Convenience constants to help improve readability
+    private static final Value[] NO_DEFAULT_VALUES = new Value[0];
+    private static final String[] NO_CONSTRAINTS = new String[0];
+    private static final List<NodeType> NO_SUPERTYPES = Collections.<NodeType>emptyList();
+    private static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
+    private static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
+
+    // Indicates that the node type has no primary item name - added for readability
+    private static final Name NO_PRIMARY_ITEM_NAME = null;
+    // Indicates that the definition should apply to all property definition or child node definitions - added for readability
+    private static final Name ALL_NODES = null;
+
+    // Indicates whether or not the node type is a mixin - added for readability
+    private static final boolean IS_A_MIXIN = true;
+    private static final boolean NOT_MIXIN = false;
+
+    // Indicates whether or not the node type has orderable children - added for readability
+    private static final boolean ORDERABLE_CHILD_NODES = true;
+    private static final boolean UNORDERABLE_CHILD_NODES = false;
+
+    /** The list of primary node types. */
+    private final List<JcrNodeType> primaryNodeTypes;
+    /** The list of mixin node types. */
+    private final List<JcrNodeType> mixinNodeTypes;
+
+    JcrBuiltinNodeTypeSource( JcrSession session ) {
+        primaryNodeTypes = new ArrayList<JcrNodeType>();
+
+        Value trueValue = new JcrValue<Boolean>(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN,
+                                                Boolean.TRUE);
+        Value ntBaseValue = new JcrValue<Name>(session.getExecutionContext().getValueFactories(), PropertyType.NAME,
+                                               JcrNtLexicon.BASE);
+
+        // Stubbing in child node and property definitions for now
+        JcrNodeType base = new JcrNodeType(session, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                           Arrays.asList(new JcrPropertyDefinition[] {
+                                               new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_TYPE,
+                                                                         OnParentVersionBehavior.COMPUTE.getJcrValue(), true,
+                                                                         true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                         NO_CONSTRAINTS, false),
+                                               new JcrPropertyDefinition(session, null, JcrLexicon.MIXIN_TYPES,
+                                                                         OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                         false, true, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                         NO_CONSTRAINTS, true)}), NOT_MIXIN,
+                                           UNORDERABLE_CHILD_NODES);
+
+        // This needs to be declared early, as some of the primary types reference it
+        JcrNodeType referenceable = new JcrNodeType(
+                                                    session,
+                                                    JcrMixLexicon.REFERENCEABLE,
+                                                    NO_SUPERTYPES,
+                                                    NO_PRIMARY_ITEM_NAME,
+                                                    NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         session,
+                                                                                                                         null,
+                                                                                                                         JcrLexicon.UUID,
+                                                                                                                         OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                                                                         true,
+                                                                                                                         true,
+                                                                                                                         true,
+                                                                                                                         NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.STRING,
+                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType childNodeDefinition = new JcrNodeType(
+                                                          session,
+                                                          JcrNtLexicon.CHILD_NODE_DEFINITION,
+                                                          Arrays.asList(new NodeType[] {base}),
+                                                          NO_PRIMARY_ITEM_NAME,
+                                                          NO_CHILD_NODES,
+                                                          Arrays.asList(new JcrPropertyDefinition[] {
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.AUTO_CREATED,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                        false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.DEFAULT_PRIMARY_TYPE,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.MANDATORY,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                        false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.NAME,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.ON_PARENT_VERSION,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.STRING, NO_CONSTRAINTS,
+                                                                                        false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.PROTECTED,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                        false),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.REQUIRED_PRIMARY_TYPES,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false,
+                                                                                        new Value[] {ntBaseValue},
+                                                                                        PropertyType.NAME, NO_CONSTRAINTS, true),
+                                                              new JcrPropertyDefinition(
+                                                                                        session,
+                                                                                        null,
+                                                                                        JcrLexicon.SAME_NAME_SIBLINGS,
+                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                        false, true, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                        false)}), NOT_MIXIN,
+                                                          UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType hierarchyNode = new JcrNodeType(
+                                                    session,
+                                                    JcrNtLexicon.HIERARCHY_NODE,
+                                                    Arrays.asList(new NodeType[] {base}),
+                                                    NO_PRIMARY_ITEM_NAME,
+                                                    NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         session,
+                                                                                                                         null,
+                                                                                                                         JcrLexicon.CREATED,
+                                                                                                                         OnParentVersionBehavior.INITIALIZE.getJcrValue(),
+                                                                                                                         true,
+                                                                                                                         false,
+                                                                                                                         true,
+                                                                                                                         NO_DEFAULT_VALUES,
+                                                                                                                         PropertyType.DATE,
+                                                                                                                         NO_CONSTRAINTS,
+                                                                                                                         false),}),
+                                                    NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType file = new JcrNodeType(
+                                           session,
+                                           JcrNtLexicon.FILE,
+                                           Arrays.asList(new NodeType[] {hierarchyNode}),
+                                           JcrLexicon.CONTENT,
+                                           Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                        session,
+                                                                                                        null,
+                                                                                                        JcrLexicon.CONTENT,
+                                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                        false, true, false,
+                                                                                                        false, null,
+                                                                                                        new NodeType[] {base})}),
+                                           NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType folder = new JcrNodeType(
+                                             session,
+                                             JcrNtLexicon.FOLDER,
+                                             Arrays.asList(new NodeType[] {hierarchyNode}),
+                                             NO_PRIMARY_ITEM_NAME,
+                                             Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                          session,
+                                                                                                          null,
+                                                                                                          null,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          false,
+                                                                                                          null,
+                                                                                                          new NodeType[] {hierarchyNode})}),
+                                             NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType frozenNode = new JcrNodeType(
+                                                 session,
+                                                 JcrNtLexicon.FROZEN_NODE,
+                                                 Arrays.asList(new NodeType[] {base, referenceable}),
+                                                 NO_PRIMARY_ITEM_NAME,
+                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                              session,
+                                                                                                              null,
+                                                                                                              ALL_NODES,
+                                                                                                              OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                                                              false,
+                                                                                                              false,
+                                                                                                              true,
+                                                                                                              true,
+                                                                                                              null,
+                                                                                                              new NodeType[] {base})}),
+                                                 Arrays.asList(new JcrPropertyDefinition[] {
+                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_MIXIN_TYPES,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                               false, false, true, NO_DEFAULT_VALUES,
+                                                                               PropertyType.NAME, NO_CONSTRAINTS, true),
+                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_PRIMARY_TYPE,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
+                                                                               true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                               NO_CONSTRAINTS, false),
+                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_UUID,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
+                                                                               true, true, NO_DEFAULT_VALUES,
+                                                                               PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                     new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                               false, false, true, NO_DEFAULT_VALUES,
+                                                                               PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
+                                                     new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                               false, false, true, NO_DEFAULT_VALUES,
+                                                                               PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
+                                                 NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+        JcrNodeType linkedFile = new JcrNodeType(
+                                                 session,
+                                                 JcrNtLexicon.LINKED_FILE,
+                                                 Arrays.asList(new NodeType[] {hierarchyNode}),
+                                                 JcrLexicon.CONTENT,
+                                                 NO_CHILD_NODES,
+                                                 Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                      session,
+                                                                                                                      null,
+                                                                                                                      JcrLexicon.CONTENT,
+                                                                                                                      OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                                                      false,
+                                                                                                                      true,
+                                                                                                                      false,
+                                                                                                                      NO_DEFAULT_VALUES,
+                                                                                                                      PropertyType.REFERENCE,
+                                                                                                                      NO_CONSTRAINTS,
+                                                                                                                      false),}),
+                                                 NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        // Had to be moved above nodeType due to dependency
+        JcrNodeType propertyDefinition = new JcrNodeType(
+                                                         session,
+                                                         JcrNtLexicon.PROPERTY_DEFINITION,
+                                                         Arrays.asList(new NodeType[] {base}),
+                                                         NO_PRIMARY_ITEM_NAME,
+                                                         NO_CHILD_NODES,
+                                                         Arrays.asList(new JcrPropertyDefinition[] {
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.AUTO_CREATED,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                       false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.DEFAULT_VALUES,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, false, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.UNDEFINED, NO_CONSTRAINTS,
+                                                                                       true),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.MANDATORY,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                       false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.MULTIPLE,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                       false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.NAME,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, false, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.NAME, NO_CONSTRAINTS, false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.ON_PARENT_VERSION,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.PROTECTED,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.BOOLEAN, NO_CONSTRAINTS,
+                                                                                       false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.REQUIRED_TYPE,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, true, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                             new JcrPropertyDefinition(
+                                                                                       session,
+                                                                                       null,
+                                                                                       JcrLexicon.VALUE_CONSTRAINTS,
+                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                       false, false, false, NO_DEFAULT_VALUES,
+                                                                                       PropertyType.STRING, NO_CONSTRAINTS, true)}),
+                                                         NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType nodeType = new JcrNodeType(session, JcrNtLexicon.NODE_TYPE, Arrays.asList(new NodeType[] {base}),
+                                               NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
+                                                   new JcrNodeDefinition(session, null, JcrLexicon.CHILD_NODE_DEFINITION,
+                                                                         OnParentVersionBehavior.VERSION.getJcrValue(), false,
+                                                                         false, false, true, JcrNtLexicon.CHILD_NODE_DEFINITION,
+                                                                         new NodeType[] {childNodeDefinition}),
+                                                   new JcrNodeDefinition(session, null, JcrLexicon.PROPERTY_DEFINITION,
+                                                                         OnParentVersionBehavior.VERSION.getJcrValue(), false,
+                                                                         false, false, true, JcrNtLexicon.PROPERTY_DEFINITION,
+                                                                         new NodeType[] {propertyDefinition})}),
+                                               Arrays.asList(new JcrPropertyDefinition[] {
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES,
+                                                                             PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.IS_MIXIN,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES,
+                                                                             PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.NODE_TYPE_NAME,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                             NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_ITEM_NAME,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                             NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.SUPERTYPES,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
+                                                                             NO_CONSTRAINTS, true),}), NOT_MIXIN,
+                                               UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType query = new JcrNodeType(session, JcrNtLexicon.QUERY, Arrays.asList(new NodeType[] {base}),
+                                            NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+                                                new JcrPropertyDefinition(session, null, JcrLexicon.LANGUAGE,
+                                                                          OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                          false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                          NO_CONSTRAINTS, false),
+                                                new JcrPropertyDefinition(session, null, JcrLexicon.STATEMENT,
+                                                                          OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                          false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                          NO_CONSTRAINTS, false),}), NOT_MIXIN,
+                                            UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType resource = new JcrNodeType(session, JcrNtLexicon.RESOURCE,
+                                               Arrays.asList(new NodeType[] {base, referenceable}), JcrLexicon.DATA,
+                                               NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.DATA,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES, PropertyType.BINARY,
+                                                                             NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.ENCODING,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             false, false, NO_DEFAULT_VALUES,
+                                                                             PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LAST_MODIFIED,
+                                                                             OnParentVersionBehavior.IGNORE.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES, PropertyType.DATE,
+                                                                             NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.MIME_TYPE,
+                                                                             OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                             true, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                             NO_CONSTRAINTS, false),}), NOT_MIXIN,
+                                               UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType unstructured = new JcrNodeType(
+                                                   session,
+                                                   JcrNtLexicon.UNSTRUCTURED,
+                                                   Arrays.asList(new NodeType[] {base}),
+                                                   NO_PRIMARY_ITEM_NAME,
+                                                   Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                                session,
+                                                                                                                null,
+                                                                                                                ALL_NODES,
+                                                                                                                OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                false,
+                                                                                                                false,
+                                                                                                                false,
+                                                                                                                true,
+                                                                                                                JcrNtLexicon.UNSTRUCTURED,
+                                                                                                                new NodeType[] {base}),}),
+                                                   Arrays.asList(new JcrPropertyDefinition[] {
+                                                       new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                                 OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
+                                                       new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                                 OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
+                                                   NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+        JcrNodeType version = new JcrNodeType(
+                                              session,
+                                              JcrNtLexicon.VERSION,
+                                              Arrays.asList(new NodeType[] {base, referenceable}),
+                                              NO_PRIMARY_ITEM_NAME,
+                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                           session,
+                                                                                                           null,
+                                                                                                           JcrLexicon.FROZEN_NODE,
+                                                                                                           OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                                                           false,
+                                                                                                           false,
+                                                                                                           true,
+                                                                                                           false,
+                                                                                                           null,
+                                                                                                           new NodeType[] {frozenNode}),}),
+                                              Arrays.asList(new JcrPropertyDefinition[] {
+                                                  new JcrPropertyDefinition(session, null, JcrLexicon.CREATED,
+                                                                            OnParentVersionBehavior.ABORT.getJcrValue(), true,
+                                                                            true, true, NO_DEFAULT_VALUES, PropertyType.DATE,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+                                                                            OnParentVersionBehavior.ABORT.getJcrValue(), false,
+                                                                            false, true, NO_DEFAULT_VALUES,
+                                                                            PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+                                                  new JcrPropertyDefinition(session, 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(
+                                                    session,
+                                                    JcrNtLexicon.VERSION_LABELS,
+                                                    Arrays.asList(new NodeType[] {base}),
+                                                    NO_PRIMARY_ITEM_NAME,
+                                                    NO_CHILD_NODES,
+                                                    Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                         session,
+                                                                                                                         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(
+                                                     session,
+                                                     JcrNtLexicon.VERSION_HISTORY,
+                                                     Arrays.asList(new NodeType[] {base, referenceable}),
+                                                     NO_PRIMARY_ITEM_NAME,
+                                                     Arrays.asList(new JcrNodeDefinition[] {
+                                                         new JcrNodeDefinition(session, null, JcrLexicon.ROOT_VERSION,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
+                                                                               true, true, false, JcrNtLexicon.VERSION,
+                                                                               new NodeType[] {version}),
+                                                         new JcrNodeDefinition(session, null, JcrLexicon.VERSION_LABELS,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(), true,
+                                                                               true, true, false, JcrNtLexicon.VERSION_LABELS,
+                                                                               new NodeType[] {versionLabels}),
+                                                         new JcrNodeDefinition(session, null, ALL_NODES,
+                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                               false, false, true, false, JcrNtLexicon.VERSION,
+                                                                               new NodeType[] {version}),}),
+                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                          session,
+                                                                                                                          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(
+                                                     session,
+                                                     JcrNtLexicon.VERSIONED_CHILD,
+                                                     Arrays.asList(new NodeType[] {base}),
+                                                     NO_PRIMARY_ITEM_NAME,
+                                                     NO_CHILD_NODES,
+                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                          session,
+                                                                                                                          null,
+                                                                                                                          CHILD_VERSION_HISTORY,
+                                                                                                                          OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                                                                          true,
+                                                                                                                          true,
+                                                                                                                          true,
+                                                                                                                          NO_DEFAULT_VALUES,
+                                                                                                                          PropertyType.REFERENCE,
+                                                                                                                          NO_CONSTRAINTS,
+                                                                                                                          false),}),
+                                                     NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder,
+            frozenNode, hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType, version,
+            versionHistory, versionLabels, versionedChild}));
+
+        mixinNodeTypes = new ArrayList<JcrNodeType>();
+
+        JcrNodeType lockable = new JcrNodeType(session, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME,
+                                               NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_IS_DEEP,
+                                                                             OnParentVersionBehavior.IGNORE.getJcrValue(), false,
+                                                                             false, true, NO_DEFAULT_VALUES,
+                                                                             PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_OWNER,
+                                                                             OnParentVersionBehavior.IGNORE.getJcrValue(), false,
+                                                                             false, true, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                             NO_CONSTRAINTS, false)}), IS_A_MIXIN,
+                                               UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType versionable = new JcrNodeType(
+                                                  session,
+                                                  JcrMixLexicon.VERSIONABLE,
+                                                  Arrays.asList(new NodeType[] {referenceable}),
+                                                  NO_PRIMARY_ITEM_NAME,
+                                                  NO_CHILD_NODES,
+                                                  Arrays.asList(new JcrPropertyDefinition[] {
+                                                      new JcrPropertyDefinition(session, null, JcrLexicon.BASE_VERSION,
+                                                                                OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                false, true, true, NO_DEFAULT_VALUES,
+                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, false),
+                                                      new JcrPropertyDefinition(session, null, JcrLexicon.IS_CHECKED_OUT,
+                                                                                OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                true, true, true, new Value[] {trueValue},
+                                                                                PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                      new JcrPropertyDefinition(session, null, JcrLexicon.MERGE_FAILED,
+                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
+                                                                                false, false, true, NO_DEFAULT_VALUES,
+                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+                                                      new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                false, true, true, NO_DEFAULT_VALUES,
+                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, true),
+                                                      new JcrPropertyDefinition(session, null, JcrLexicon.VERSION_HISTORY,
+                                                                                OnParentVersionBehavior.COPY.getJcrValue(),
+                                                                                false, true, true, NO_DEFAULT_VALUES,
+                                                                                PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),
+                                                  IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {lockable, referenceable, versionable}));
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     */
+    public Collection<JcrNodeType> getMixinNodeTypes() {
+        return mixinNodeTypes;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
+     */
+    public Collection<JcrNodeType> getPrimaryNodeTypes() {
+        return primaryNodeTypes;
+    }
+
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -57,7 +57,9 @@
 
     public static I18n errorWhileInitializingTheNamespaceRegistry;
     public static I18n invalidPathParameter;
-
+    
+    public static I18n typeNotFound;
+    
     public static I18n REP_NAME_DESC;
     public static I18n REP_VENDOR_DESC;
     public static I18n SPEC_NAME_DESC;

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,122 @@
+/*
+ * 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 javax.jcr.nodetype.ItemDefinition;
+import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * DNA implementation of the {@link ItemDefinition} interface.  This implementation is immutable and has all fields initialized
+ * through its constructor. 
+ */
+ at Immutable
+class JcrItemDefinition implements ItemDefinition {
+
+    protected final JcrSession session;
+
+    private final NodeType declaringNodeType;
+    protected final Name name;
+    private final int onParentVersion;
+    private final boolean autoCreated;
+    private final boolean mandatory;
+    private final boolean protectedItem;
+
+    JcrItemDefinition( JcrSession session,
+                       NodeType declaringNodeType,
+                       Name name,
+                       int onParentVersion,
+                       boolean autoCreated,
+                       boolean mandatory,
+                       boolean protectedItem ) {
+        super();
+        this.session = session;
+        this.declaringNodeType = declaringNodeType;
+        this.name = name;
+        this.onParentVersion = onParentVersion;
+        this.autoCreated = autoCreated;
+        this.mandatory = mandatory;
+        this.protectedItem = protectedItem;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#getDeclaringNodeType()
+     */
+    public NodeType getDeclaringNodeType() {
+        return declaringNodeType;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#getName()
+     */
+    public String getName() {
+        if (name == null) {
+            return "*";
+        }
+
+        return name.getString(session.getExecutionContext().getNamespaceRegistry());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#getOnParentVersion()
+     */
+    public int getOnParentVersion() {
+        return onParentVersion;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#isAutoCreated()
+     */
+    public boolean isAutoCreated() {
+        return autoCreated;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#isMandatory()
+     */
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.ItemDefinition#isProtected()
+     */
+    public boolean isProtected() {
+        return protectedItem;
+    }
+
+}


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

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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -31,6 +31,48 @@
  */
 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 MIME_TYPE = new BasicName(Namespace.URI, "mimeType");
+    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_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_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");
 
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,5 @@
+package org.jboss.dna.jcr;
+
+public class JcrMixLexicon extends org.jboss.dna.graph.JcrMixLexicon {
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,124 @@
+/*
+ * 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 javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * DNA implementation of the {@link NodeDefinition} class.
+ */
+ at Immutable
+class JcrNodeDefinition extends JcrItemDefinition implements NodeDefinition {
+
+    /** @see NodeDefinition#allowsSameNameSiblings() */
+    private final boolean allowsSameNameSiblings;
+
+    /**
+     * The name of the default primary type (if any). The name is used instead of the raw node type to allow circular references a
+     * la <code>nt:unstructured</code>.
+     */
+    private final Name defaultPrimaryTypeName;
+
+    /** @see NodeDefinition#getRequiredPrimaryTypes() */
+    private final NodeType[] requiredPrimaryTypes;
+
+    JcrNodeDefinition( JcrSession session,
+                       NodeType declaringNodeType,
+                       Name name,
+                       int onParentVersion,
+                       boolean autoCreated,
+                       boolean mandatory,
+                       boolean protectedItem,
+                       boolean allowsSameNameSiblings,
+                       Name defaultPrimaryTypeName,
+                       NodeType[] requiredPrimaryTypes ) {
+        super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        this.allowsSameNameSiblings = allowsSameNameSiblings;
+        this.defaultPrimaryTypeName = defaultPrimaryTypeName;
+        this.requiredPrimaryTypes = requiredPrimaryTypes;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings()
+     */
+    public boolean allowsSameNameSiblings() {
+        return allowsSameNameSiblings;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeDefinition#getDefaultPrimaryType()
+     */
+    public NodeType getDefaultPrimaryType() {
+        // It is valid for this field to be null.
+        if (defaultPrimaryTypeName == null) {
+            return null;
+        }
+
+        /*
+         * Translate the name to a prefixed type based on the current transient (session) and persistent (workspace) 
+         * prefix to URI mappings.
+         */
+        String mappedTypeName = defaultPrimaryTypeName.getString(session.getExecutionContext().getNamespaceRegistry());
+
+        try {
+            return session.getWorkspace().getNodeTypeManager().getNodeType(mappedTypeName);
+        } catch (RepositoryException re) {
+            /*
+             * The spec doesn't allow us to throw a checked exception at this point, but a corrupted namespace mapping
+             * would be pretty severe. 
+             */
+            throw new IllegalStateException(JcrI18n.typeNotFound.text(mappedTypeName));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes()
+     */
+    public NodeType[] getRequiredPrimaryTypes() {
+        return requiredPrimaryTypes;
+    }
+
+    /**
+     * Creates a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
+     * <code>declaringNodeType</code>. Provided to support immutable pattern for this class.
+     * 
+     * @param declaringNodeType the declaring node type for the new <code>JcrNodeDefinition</code>
+     * @return a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
+     *         <code>declaringNodeType</code>.
+     */
+    JcrNodeDefinition with( NodeType declaringNodeType ) {
+        return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), isAutoCreated(), isMandatory(),
+                                     isProtected(), allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,598 @@
+/*
+ * 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.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+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 net.jcip.annotations.Immutable;
+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.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * DNA implementation of JCR {@link NodeType}s.
+ */
+ at Immutable
+class JcrNodeType implements NodeType {
+
+    /** The name of the node type (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code>) */
+    private final Name name;
+    /** The name of the node's primary item */
+    private final Name primaryItemName;
+
+    /** The set of child node definitions for nodes of this type (possibly empty). */
+    private final Set<JcrNodeDefinition> childNodeDefinitions;
+    /** The set of property definitions for nodes of this type (possibly empty). */
+    private final Set<JcrPropertyDefinition> propertyDefinitions;
+    /** The supertypes for this node. */
+    private final List<NodeType> declaredSupertypes;
+
+    /** Indicates whether this node type is a mixin type (as opposed to a primary type). */
+    private boolean mixin;
+    /** Indicates whether the child nodes of nodes of this type can be ordered. */
+    private boolean orderableChildNodes;
+
+    /**
+     * A reference to the session in which this node type exists, used to remap the internal names to their appropriate prefixed
+     * version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>&quot;nt:base&quot;</code>.).
+     */
+    private JcrSession session;
+
+    JcrNodeType( JcrSession session,
+                 Name name,
+                 List<NodeType> declaredSupertypes,
+                 Name primaryItemName,
+                 Collection<JcrNodeDefinition> childNodeDefinitions,
+                 Collection<JcrPropertyDefinition> propertyDefinitions,
+                 boolean mixin,
+                 boolean orderableChildNodes ) {
+        this.session = session;
+        this.name = name;
+        this.primaryItemName = primaryItemName;
+        this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<NodeType>emptyList();
+        this.mixin = mixin;
+        this.orderableChildNodes = orderableChildNodes;
+        this.propertyDefinitions = new HashSet<JcrPropertyDefinition>(propertyDefinitions.size());
+        for (JcrPropertyDefinition property : propertyDefinitions) {
+            this.propertyDefinitions.add(property.with(this));
+        }
+
+        this.childNodeDefinitions = new HashSet<JcrNodeDefinition>(childNodeDefinitions.size());
+        for (JcrNodeDefinition childNode : childNodeDefinitions) {
+            this.childNodeDefinitions.add(childNode.with(this));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
+     */
+    public boolean canAddChildNode( String childNodeName ) {
+
+        CheckArg.isNotNull(childNodeName, "childNodeName");
+
+        JcrNodeDefinition residual = null;
+
+        // First, try to find a child node definition with the given name
+        for (JcrNodeDefinition childNode : childNodeDefinitions) {
+            if (childNodeName.equals(childNode.getName())) {
+                NodeType defaultType = childNode.getDefaultPrimaryType();
+                // If there's no default type, the child node can't be created
+                if (defaultType == null) {
+                    return false;
+                }
+
+                // Check if the node can be added with the named child node definition
+                return checkTypeAgainstDefinition(defaultType, childNode);
+                // If we run into a residual (*) definition, save it just in case
+            } else if (childNode.name == null) {
+                residual = childNode;
+            }
+        }
+
+        // If there's no matching child node definition for the name and no residual definition, the node cannot be added
+        if (residual == null) {
+            return false;
+        }
+
+        NodeType defaultType = residual.getDefaultPrimaryType();
+
+        // If there's no default type, the child node can't be created
+        if (defaultType == null) {
+            return false;
+        }
+        
+        // Check if the node can be added with the default type of the residual child node definition
+        return checkTypeAgainstDefinition(defaultType, residual);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String, java.lang.String)
+     */
+    public boolean canAddChildNode( String childNodeName,
+                                    String primaryNodeTypeName ) {
+
+        CheckArg.isNotNull(childNodeName, "childNodeName");
+        CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
+
+        NodeType primaryNodeType;
+        try {
+            primaryNodeType = session.getWorkspace().getNodeTypeManager().getNodeType(primaryNodeTypeName);
+        } catch (RepositoryException re) {
+            // If the node type doesn't exist, you can't add a child node with that type
+            return false;
+        }
+
+        JcrNodeDefinition residual = null;
+
+        // First, try to find a child node definition with the given name
+        for (JcrNodeDefinition childNode : childNodeDefinitions) {
+            if (childNodeName.equals(childNode.getName())) {
+                return checkTypeAgainstDefinition(primaryNodeType, childNode);
+                // If we run into a residual (*) definition, save it just in case
+            } else if (childNode.name == null) {
+                residual = childNode;
+            }
+        }
+
+        // If there's no matching child node definition for the name and no residual definition, the node cannot be added
+        if (residual == null) {
+            return false;
+        }
+
+        return checkTypeAgainstDefinition(primaryNodeType, residual);
+    }
+
+    /**
+     * Checks whether the given type is the same type or a subtype of each of the required primary types for the given node
+     * definition.
+     * 
+     * @param typeToCheck the type to check
+     * @param definition the node definition to check against
+     * @return <code>true</code> if and only if the given type is the same type or extends each of the required primary types in
+     *         the given definition
+     */
+    private boolean checkTypeAgainstDefinition( NodeType typeToCheck,
+                                                NodeDefinition definition ) {
+        NodeType[] requiredPrimaryTypes = definition.getRequiredPrimaryTypes();
+        for (int i = 0; i < requiredPrimaryTypes.length; i++) {
+            // See if the given type for the node matches all of the required primary types
+            if (!typeToCheck.isNodeType(requiredPrimaryTypes[i].getName())) {
+                return false;
+            }
+        }
+        // The node can be added with the given type based on the given child node definition
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#canRemoveItem(java.lang.String)
+     */
+    public boolean canRemoveItem( String itemName ) {
+        CheckArg.isNotNull(itemName, "itemName");
+
+        for (PropertyDefinition item : propertyDefinitions) {
+            if (itemName.equals(item.getName())) {
+                return !item.isMandatory() && !item.isProtected();
+            }
+        }
+
+        for (NodeDefinition item : childNodeDefinitions) {
+            if (itemName.equals(item.getName())) {
+                return !item.isMandatory() && !item.isProtected();
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value)
+     */
+    public boolean canSetProperty( String propertyName,
+                                   Value value ) {
+        CheckArg.isNotNull(propertyName, "propertyName");
+
+        JcrPropertyDefinition residual = null;
+
+        for (JcrPropertyDefinition property : propertyDefinitions) {
+            if (propertyName.equals(property.getName())) {
+                if (property.isMultiple()) {
+                    return false;
+                }
+
+                return canSetProperty(property, value);
+            } else if (property.name == null && !property.isMultiple()) {
+                residual = property;
+            }
+        }
+
+        return canSetProperty(residual, value);
+    }
+
+    /**
+     * Internal method to validate that a value can be set on a given property. The values are set according to the following
+     * rules:
+     * <ol>
+     * <li>If <code>value</code> is <code>null</code>, return the value of {@link JcrNodeType#canRemoveItem(String)}</li>
+     * <li>If <code>property.isProtected()</code> is <code>true</code>, return <code>false</code></li>
+     * <li>If <code>property.getRequiredType()</code> is {@link PropertyType#UNDEFINED}, return <code>true</code></li>
+     * <li>Compare the type of the given value to the required type and see if they are compatible based on the rules in the JCR
+     * 1.0 spec.</li>
+     * </ol>
+     * 
+     * @param property the property to be set
+     * @param value the value to set (may be <code>null</code>)
+     * @return whether the property can be set to the value based on the described rules
+     */
+    private boolean canSetProperty( JcrPropertyDefinition property,
+                                    Value value ) {
+        assert property != null;
+
+        if (value == null) {
+            return !property.isProtected() && !property.isMandatory();
+        }
+
+        if (property.isProtected()) {
+            return false;
+        }
+
+        int valueType = value.getType();
+
+        switch (property.getRequiredType()) {
+            case PropertyType.BINARY:
+                return true;
+            case PropertyType.BOOLEAN:
+                if (valueType == PropertyType.BOOLEAN || valueType == PropertyType.STRING) {
+                    return true;
+                }
+
+                // If the binary can be converted to a UTF-8 string, it can be set onto a boolean property
+                if (valueType == PropertyType.BINARY) {
+                    try {
+                        value.getString();
+                        return true;
+                    } catch (RepositoryException re) {
+                        return false;
+                    }
+                }
+                return false;
+
+            case PropertyType.DATE:
+                if (valueType == PropertyType.DATE || valueType == PropertyType.DOUBLE || valueType == PropertyType.LONG) {
+                    return true;
+                }
+
+                if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
+                    try {
+                        value.getDate();
+                        return true;
+                    } catch (RepositoryException re) {
+                        return false;
+                    }
+                }
+                return false;
+
+            case PropertyType.DOUBLE:
+                return value.getType() == PropertyType.DOUBLE;
+            case PropertyType.LONG:
+                return value.getType() == PropertyType.LONG;
+            case PropertyType.NAME:
+                if (value.getType() == PropertyType.NAME) {
+                    return true;
+                }
+
+                try {
+                    if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
+                        session.getExecutionContext().getValueFactories().getNameFactory().create(value.getString());
+                        return true;
+                    }
+
+                    if (valueType == PropertyType.PATH) {
+                        Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(value.getString());
+
+                        Segment[] segments = path.getSegmentsArray();
+                        return !path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex();
+                    }
+                } catch (ValueFormatException re) {
+                    return false;
+                } catch (RepositoryException re) {
+                    return false;
+                }
+
+                return false;
+
+            case PropertyType.PATH:
+                return value.getType() == PropertyType.PATH || value.getType() == PropertyType.STRING;
+            case PropertyType.REFERENCE:
+                return value.getType() == PropertyType.REFERENCE;
+
+                // Anything can be converted to these types
+            case PropertyType.STRING:
+            case PropertyType.UNDEFINED:
+                return true;
+            default:
+                throw new IllegalStateException("Invalid required property type " + property.getRequiredType() + " for property "
+                                                + property.getName());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value[])
+     */
+    public boolean canSetProperty( String propertyName,
+                                   Value[] values ) {
+        CheckArg.isNotNull(propertyName, "propertyName");
+
+        JcrPropertyDefinition residual = null;
+
+        for (JcrPropertyDefinition property : propertyDefinitions) {
+            if (propertyName.equals(property.getName())) {
+                if (!property.isMultiple()) {
+                    return false;
+                }
+
+                return canSetProperty(property, values);
+            } else if (property.name == null && property.isMultiple()) {
+                residual = property;
+            }
+        }
+
+        return canSetProperty(residual, values);
+    }
+
+    /**
+     * Internal method to validate that an array of values can be set on a given property. This method returns <code>true</code>
+     * if the following algorithm would return <code>true</code> when applied to each non-null value in <code>values</code>:
+     * <ol>
+     * <li>If the value is null, return the value of {@link JcrNodeType#canRemoveItem(String)}</li>
+     * <li>If the property definition has a no required type ({@link PropertyType#UNDEFINED}), return <code>true</code></li>
+     * <li>Compare the type of the given value to the required type and see if they are compatible</li>
+     * </ol>
+     * 
+     * @param property the property to be set
+     * @param values the array of values to set (may be <code>null</code>)
+     * @return whether the property can be set to the value based on the described rules
+     */
+    private boolean canSetProperty( JcrPropertyDefinition property,
+                                    Value[] values ) {
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] != null) {
+                    if (!canSetProperty(property, values[i])) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return !property.isProtected();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getDeclaredChildNodeDefinitions()
+     */
+    public NodeDefinition[] getDeclaredChildNodeDefinitions() {
+        return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions()
+     */
+    public NodeDefinition[] getChildNodeDefinitions() {
+        Set<NodeDefinition> nodeDefs = new HashSet<NodeDefinition>();
+        NodeType[] supertypes = getSupertypes();
+
+        // TODO: This could be cached after being calculated once
+        for (int i = 0; i < supertypes.length; i++) {
+            NodeDefinition[] childNodeDefinitions = supertypes[i].getChildNodeDefinitions();
+            for (int j = 0; j < childNodeDefinitions.length; i++) {
+
+                // TODO: Could add sanity check here (assertion?) that definitions of the same child node in multiple supertypes
+                // are consistent
+                nodeDefs.add(childNodeDefinitions[j]);
+            }
+        }
+
+        nodeDefs.addAll(childNodeDefinitions);
+
+        return nodeDefs.toArray(new JcrNodeDefinition[nodeDefs.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions()
+     */
+    public PropertyDefinition[] getPropertyDefinitions() {
+        Set<PropertyDefinition> propDefs = new HashSet<PropertyDefinition>();
+        NodeType[] supertypes = getSupertypes();
+
+        // TODO: This could be cached after being calculated once
+        for (int i = 0; i < supertypes.length; i++) {
+            PropertyDefinition[] childPropertyDefinitions = supertypes[i].getPropertyDefinitions();
+            for (int j = 0; j < childPropertyDefinitions.length; j++) {
+
+                // TODO: Could add sanity check here (assertion?) that definitions of the same child node in multiple supertypes
+                // are consistent
+                propDefs.add(childPropertyDefinitions[j]);
+            }
+        }
+        propDefs.addAll(propertyDefinitions);
+
+        return propDefs.toArray(new JcrPropertyDefinition[propDefs.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes()
+     */
+    public NodeType[] getDeclaredSupertypes() {
+        return declaredSupertypes.toArray(new NodeType[declaredSupertypes.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getName()
+     */
+    public String getName() {
+        // Translate the name to the correct prefix. Need to check the session to support url-remapping.
+        return name.getString(session.getExecutionContext().getNamespaceRegistry());
+    }
+
+    /**
+     * Returns the internal {@link Name} object for the note 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.
+     */
+    Name getInternalName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getPrimaryItemName()
+     */
+    public String getPrimaryItemName() {
+        if (primaryItemName == null) {
+            return null;
+        }
+
+        // Translate the name to the correct prefix. Need to check the session to support url-remapping.
+        return primaryItemName.getString(session.getExecutionContext().getNamespaceRegistry());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getDeclaredPropertyDefinitions()
+     */
+    public PropertyDefinition[] getDeclaredPropertyDefinitions() {
+        return propertyDefinitions.toArray(new JcrPropertyDefinition[propertyDefinitions.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#getSupertypes()
+     */
+    public NodeType[] getSupertypes() {
+        Set<NodeType> supertypes = new HashSet<NodeType>();
+        Stack<NodeType> unvisitedSupertypes = new Stack<NodeType>();
+
+        assert declaredSupertypes != null;
+        unvisitedSupertypes.addAll(declaredSupertypes);
+
+        // TODO: If this ends up getting called frequently, it should probably be executed once in the constructor and have the
+        // results cached.
+        while (!unvisitedSupertypes.isEmpty()) {
+            NodeType nodeType = unvisitedSupertypes.pop();
+
+            /*
+             * If we haven't already visited this nodeType (which we can
+             * infer by whether or not it was already added to the return set),
+             * then add the supertypes of this new node to the unvisited set for 
+             * further inspection.
+             */
+            if (!supertypes.contains(nodeType)) {
+                supertypes.add(nodeType);
+                // Violating encapsulation to avoid going from List to array back to List
+                unvisitedSupertypes.addAll(((JcrNodeType)nodeType).declaredSupertypes);
+            }
+        }
+
+        return supertypes.toArray(new NodeType[0]);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#hasOrderableChildNodes()
+     */
+    public boolean hasOrderableChildNodes() {
+        return orderableChildNodes;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#isMixin()
+     */
+    public boolean isMixin() {
+        return mixin;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeType#isNodeType(java.lang.String)
+     */
+    public boolean isNodeType( String nodeTypeName ) {
+        if (this.getName().equals(nodeTypeName)) return true;
+
+        // TODO: This could be optimized
+        NodeType[] supertypes = getSupertypes();
+        for (int i = 0; i < supertypes.length; i++) {
+            if (supertypes[i].isNodeType(nodeTypeName)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,118 @@
+/*
+ * 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.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+import net.jcip.annotations.Immutable;
+
+/**
+ * Type-safe {@link Iterator} implementation for NodeTypes, as per the JCR specification.
+ */
+ at Immutable
+final class JcrNodeTypeIterator implements NodeTypeIterator {
+
+    private int size;
+    private int position;
+    private Iterator<NodeType> iterator;
+
+    JcrNodeTypeIterator( Collection<? extends NodeType> values ) {
+        this.iterator = Collections.unmodifiableCollection(values).iterator();
+        this.size = values.size();
+        this.position = 0;
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeTypeIterator#nextNodeType()
+     */
+    public NodeType nextNodeType() {
+        // TODO: Does this really need to return a copy of the node type to prevent manipulation?
+        position++;
+        return iterator.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getPosition()
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getSize()
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#skip(long)
+     */
+    public void skip( long count ) {
+        position += count;
+        while (count-- > 0)
+            iterator.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return iterator.hasNext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public Object next() {
+        position++;
+        return iterator.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("Node types cannot be removed through their iterator");
+    }
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,28 @@
+package org.jboss.dna.jcr;
+
+import java.util.Collection;
+import javax.jcr.nodetype.NodeType;
+
+/**
+ * Interface for any potential provider of {@link JcrNodeType} definitions, the DNA implementation of {@link NodeType}. Possible
+ * sources of node type definitions include CND files, repository metadata, and mock types for testing.
+ * 
+ * @see JcrWorkspace#getNodeTypeManager()
+ */
+public interface JcrNodeTypeSource {
+
+    /**
+     * Returns the list of primary node types provided by this source
+     * 
+     * @return the list of primary node types provided by this source
+     */
+    public Collection<JcrNodeType> getPrimaryNodeTypes();
+
+    /**
+     * Returns the list of mixin node types provided by this source
+     * 
+     * @return the list of mixin node types provided by this source
+     */
+    public Collection<JcrNodeType> getMixinNodeTypes();
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,43 @@
+/*
+ * 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.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+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");
+    public static final Name VERSION_HISTORY = new BasicName(Namespace.URI, "versionHistory");
+    public static final Name VERSION_LABELS = new BasicName(Namespace.URI, "versionLabels");
+
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -131,7 +131,7 @@
 
     /**
      * {@inheritDoc}
-     * 
+     *  
      * @see javax.jcr.Property#getString()
      */
     public String getString() throws RepositoryException {
@@ -163,7 +163,8 @@
      * @see javax.jcr.Property#getValues()
      */
     public Value[] getValues() throws ValueFormatException {
-        throw new ValueFormatException();
+
+        return new Value[] {jcrValue};
     }
 
     /*

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,112 @@
+/*
+ * 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 javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+
+
+/**
+ * DNA implementation of the {@link PropertyDefinition} interface.  This implementation is immutable and has all fields initialized
+ * through its constructor. 
+ */
+ at Immutable
+class JcrPropertyDefinition extends JcrItemDefinition implements PropertyDefinition {
+
+    private final Value[] defaultValues;
+    private final int requiredType;
+    private final String[] valueConstraints;
+    private final boolean multiple;
+
+    JcrPropertyDefinition( JcrSession session,
+                           NodeType declaringNodeType,
+                           Name name,
+                           int onParentVersion,
+                           boolean autoCreated,
+                           boolean mandatory,
+                           boolean protectedItem,
+                           Value[] defaultValues,
+                           int requiredType,
+                           String[] valueConstraints,
+                           boolean multiple ) {
+        super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        this.defaultValues = defaultValues;
+        this.requiredType = requiredType;
+        this.valueConstraints = valueConstraints;
+        this.multiple = multiple;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues()
+     */
+    public Value[] getDefaultValues() {
+        return defaultValues;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.PropertyDefinition#getRequiredType()
+     */
+    public int getRequiredType() {
+        return requiredType;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.PropertyDefinition#getValueConstraints()
+     */
+    public String[] getValueConstraints() {
+        return valueConstraints;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.PropertyDefinition#isMultiple()
+     */
+    public boolean isMultiple() {
+        return multiple;
+    }
+
+    /**
+     * Creates a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+     * <code>declaringNodeType</code>. Provided to support immutable pattern for this class.
+     * 
+     * @param declaringNodeType the declaring node type for the new <code>JcrPropertyDefinition</code>
+     * @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+     *         <code>declaringNodeType</code>.
+     */
+    JcrPropertyDefinition with( NodeType declaringNodeType ) {
+        return new JcrPropertyDefinition(this.session, declaringNodeType, this.name, this.getOnParentVersion(),
+                                         this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
+                                         this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
+    }
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -27,19 +27,23 @@
 import java.util.Set;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 
 /**
  * @author jverhaeg
  */
+ at Immutable
 final class JcrPropertyIterator implements PropertyIterator {
 
     private final Iterator<Property> iterator;
     private int ndx;
+    private int size;
 
     JcrPropertyIterator( Set<Property> properties ) {
         assert properties != null;
         iterator = properties.iterator();
+        size = properties.size();
     }
 
     /**
@@ -54,11 +58,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @return -1L
      * @see javax.jcr.RangeIterator#getSize()
      */
     public long getSize() {
-        return -1L;
+        return size;
     }
 
     /**

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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -423,6 +423,15 @@
         rootNode = new JcrRootNode(this);
         // Get root node from source
         populateNode(rootNode, graph.getNodeAt(executionContext.getValueFactories().getPathFactory().createRootPath()));
+
+        // Root nodes need to have a type in JCR land
+        // JcrProperty primaryType = new JcrProperty(rootNode, getExecutionContext(), JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.BASE);
+        String typeValue = JcrNtLexicon.BASE.getString(executionContext.getNamespaceRegistry());
+        JcrProperty primaryType = new JcrProperty(rootNode, executionContext, JcrLexicon.PRIMARY_TYPE, typeValue);
+
+        // TODO: Not liking the hard-code
+        rootNode.properties.add(primaryType);
+
         return rootNode;
     }
 
@@ -594,8 +603,7 @@
                 if (uuid == null && DnaLexicon.UUID.equals(name)) uuid = uuidFactory.create(dnaProp.getValues()).next();
                 else if (jcrUuidName.equals(name)) dnaUuidProp = dnaProp;
                 else if (jcrMixinTypesName.equals(name)) {
-                    org.jboss.dna.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories()
-                                                                                                      .getStringFactory();
+                    org.jboss.dna.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories().getStringFactory();
                     for (String mixin : stringFactory.create(dnaProp)) {
                         if ("mix:referenceable".equals(mixin)) referenceable = true;
                     }

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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSvLexicon.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -4,13 +4,13 @@
  * 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. 
+ * 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
@@ -23,9 +23,11 @@
  */
 package org.jboss.dna.jcr;
 
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
 
 /**
- * @author Randall Hauch
+ *
  */
 public class JcrSvLexicon {
 
@@ -34,4 +36,9 @@
         public static final String PREFIX = "sv";
     }
 
+    public static final Name NODE = new BasicName(Namespace.URI, "node");
+    public static final Name PROPERTY = new BasicName(Namespace.URI, "property");
+    public static final Name NAME = new BasicName(Namespace.URI, "name");
+    public static final Name TYPE = new BasicName(Namespace.URI, "type");
+    public static final Name VALUE = new BasicName(Namespace.URI, "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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -25,6 +25,10 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.jcr.AccessDeniedException;
@@ -37,11 +41,15 @@
 import javax.jcr.Workspace;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.observation.ObservationManager;
 import javax.jcr.query.QueryManager;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
+import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -64,6 +72,7 @@
  * @author John Verhaeg
  * @author Randall Hauch
  */
+ at NotThreadSafe
 final class JcrWorkspace implements Workspace {
 
     /**
@@ -101,10 +110,16 @@
     private final JcrNamespaceRegistry workspaceRegistry;
 
     /**
+     * Reference to the JCR type manager for this workspace.
+     */
+    private final NodeTypeManager nodeTypeManager;
+
+    /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
     private final JcrSession session;
 
+    @SuppressWarnings( "synthetic-access" )
     JcrWorkspace( JcrRepository repository,
                   String workspaceName,
                   ExecutionContext context,
@@ -141,6 +156,10 @@
 
         // Set up the session for this workspace ...
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
+
+        // This must be initialized after the session
+        this.nodeTypeManager = new JcrNodeTypeManager(new JcrBuiltinNodeTypeSource(this.session));
+
     }
 
     String getSourceName() {
@@ -186,7 +205,7 @@
      * {@inheritDoc}
      */
     public NodeTypeManager getNodeTypeManager() {
-        throw new UnsupportedOperationException();
+        return nodeTypeManager;
     }
 
     /**
@@ -331,4 +350,96 @@
                          boolean removeExisting ) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * Local implementation of @{link NodeTypeManager}. Initialized with {@link NodeType} source data when it is created (in the
+     * {@link JcrWorkspace} constructor.
+     */
+    @NotThreadSafe
+    private class JcrNodeTypeManager implements NodeTypeManager {
+
+        private final Map<Name, JcrNodeType> primaryNodeTypes;
+        private final Map<Name, JcrNodeType> mixinNodeTypes;
+
+        private JcrNodeTypeManager( JcrNodeTypeSource source ) {
+            Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
+            Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+
+            primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
+            for (JcrNodeType nodeType : primary) {
+                primaryNodeTypes.put(nodeType.getInternalName(), nodeType);
+            }
+
+            mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
+            for (JcrNodeType nodeType : mixins) {
+                mixinNodeTypes.put(nodeType.getInternalName(), nodeType);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes()
+         */
+        public NodeTypeIterator getAllNodeTypes() {
+
+            // TODO: Can revisit this approach later if it becomes a performance issue
+            /*
+             * Note also that this creates a subtle difference in behavior for concurrent modification
+             * between this method and the specific get*NodeTypes methods.  That is, if a type is added
+             * while an iterator from the corresponding specific get*NodeType method is being traversed,
+             * a ConcurrentModificationException will be thrown.  Because this iterator is based on a copy
+             * of the underlying maps, no exception would be thrown in the same case.
+             */
+
+            List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
+            allTypes.addAll(primaryNodeTypes.values());
+            allTypes.addAll(mixinNodeTypes.values());
+            return new JcrNodeTypeIterator(allTypes);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
+         */
+        public NodeTypeIterator getMixinNodeTypes() {
+            return new JcrNodeTypeIterator(mixinNodeTypes.values());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
+         */
+        @SuppressWarnings( "synthetic-access" )
+        public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
+            Name ntName = session.getExecutionContext().getValueFactories().getNameFactory().create(nodeTypeName);
+
+            NodeType nodeType = primaryNodeTypes.get(ntName);
+
+            if (nodeType != null) {
+                return nodeType;
+            }
+
+            nodeType = mixinNodeTypes.get(ntName);
+
+            if (nodeType != null) {
+                return nodeType;
+            }
+
+            throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
+         */
+        public NodeTypeIterator getPrimaryNodeTypes() {
+            return new JcrNodeTypeIterator(primaryNodeTypes.values());
+        }
+
+    }
+
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.jcr.version.OnParentVersionAction;
+
+/**
+ * Enumeration of possible behaviors for on-parent-version setting of properties and child nodes in JCR specification.
+ */
+public enum OnParentVersionBehavior {
+    /** @see OnParentVersionAction#ABORT */
+    ABORT(OnParentVersionAction.ABORT, OnParentVersionAction.ACTIONNAME_ABORT),
+    /** @see OnParentVersionAction#COMPUTE */
+    COMPUTE(OnParentVersionAction.COMPUTE, OnParentVersionAction.ACTIONNAME_COMPUTE),
+    /** @see OnParentVersionAction#COPY */
+    COPY(OnParentVersionAction.COPY, OnParentVersionAction.ACTIONNAME_COPY),
+    /** @see OnParentVersionAction#IGNORE */
+    IGNORE(OnParentVersionAction.IGNORE, OnParentVersionAction.ACTIONNAME_IGNORE),
+    /** @see OnParentVersionAction#INITIALIZE */
+    INITIALIZE(OnParentVersionAction.INITIALIZE, OnParentVersionAction.ACTIONNAME_INITIALIZE),
+    /** @see OnParentVersionAction#VERSION */
+    VERSION(OnParentVersionAction.VERSION, OnParentVersionAction.ACTIONNAME_VERSION);
+
+    private final int jcrValue;
+    private final String name;
+
+    OnParentVersionBehavior( int jcrValue,
+                             String name ) {
+        this.jcrValue = jcrValue;
+        this.name = name;
+    }
+
+    public int getJcrValue() {
+        return jcrValue;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static OnParentVersionBehavior fromValue( int onParentVersionAction ) {
+        for (OnParentVersionBehavior opvb : OnParentVersionBehavior.values()) {
+            if (opvb.jcrValue == onParentVersionAction) {
+                return opvb;
+            }
+        }
+
+        throw new IllegalStateException("No matching version for value: " + onParentVersionAction);
+    }
+}


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

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-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-02 17:40:37 UTC (rev 742)
@@ -55,4 +55,5 @@
 errorObtainingWorkspaceNames = Error while obtaining the workspace names for the "{0}" repository: {1}
 errorObtainingDefaultWorkspaceName = Error while obtaining the default workspace names for the "{0}" repository: {1}
 workspaceNameIsInvalid = "{1}" is not a valid workspace name for the "{0}" repository
-errorVerifyingWorkspaceName = Error validating the workspace name "{1}" for the "{0}" repository: {2}
\ No newline at end of file
+errorVerifyingWorkspaceName =Error validating the workspace name "{1}" for the "{0}" repository\: {2}
+typeNotFound=No type exists with name "{0}"

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-02 17:40:37 UTC (rev 742)
@@ -63,7 +63,7 @@
         properties.add(Mockito.mock(Property.class));
         PropertyIterator iter = node.getProperties();
         assertThat(iter, notNullValue());
-        assertThat(iter.getSize(), is(-1L));
+        assertThat(iter.getSize(), is(4L));
         assertThat(iter.getPosition(), is(0L));
         assertThat(iter.hasNext(), is(true));
         assertThat(iter.next(), notNullValue());

Modified: trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml	2009-03-02 00:30:52 UTC (rev 741)
+++ trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml	2009-03-02 17:40:37 UTC (rev 742)
@@ -24,9 +24,11 @@
   ~ 51 Franklin Street, Fifth Floor
   ~ Boston, MA  02110-1301  USA
   -->
-<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0">
-	<node jcr:name="node1" prop1="foo" />
-	<node jcr:name="node2" prop2="bar" />
-	<node jcr:name="node3" />
-	<node jcr:name="node4" />
+<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
+            xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+    jcr:primaryType="nt:unstructured">
+	<nt:unstructured jcr:name="node1" jcr:primaryType="nt:unstructured" prop1="foo" />
+	<nt:unstructured jcr:name="node2" jcr:primaryType="nt:unstructured" prop2="bar" />
+	<nt:unstructured jcr:name="node3" jcr:primaryType="nt:unstructured" />
+	<nt:unstructured jcr:name="node4" jcr:primaryType="nt:unstructured" />
 </testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Mon Mar  2 15:03:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 02 Mar 2009 15:03:51 -0500
Subject: [dna-commits] DNA SVN: r743 - in trunk:
	dna-graph/src/test/java/org/jboss/dna/graph and 4 other directories.
Message-ID: <E1LeEMh-0007ay-G1@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-02 15:03:51 -0500 (Mon, 02 Mar 2009)
New Revision: 743

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StringValueFactory.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathOldTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BooleanValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DecimalValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DoubleValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/InMemoryBinaryValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/JodaDateTimeValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/LongValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/PathValueFactoryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/UuidValueFactoryTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.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/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java
Log:
DNA-286 Implement namespace management

This change corrects the behavior of the Session's namespace-related methods, which are not the same as those in the NamespaceRegistry implementation associated with the workspace.  This also corrected several of the TCK tests associated with namespaces.

Note that the StringValueFactory in 'dna-graph' was changed to use a DNA namespace registry to convert a Name and Path to a string using the namespace prefixes.  (This change affected a number of unit tests.)  The result is that any Name and Path property values are treated correctly and properly converted to a string using the current namespace prefixes in the namespace registry.  However, the BinaryValueFactory implementations should not use this StringValueFactory, since they need to convert values using the non-prefixed form (that is, the namespace URI).

In the case of our JCR implementation, the above change to the Graph factories means that property values are properly mapped to strings using the Session's local mappings (which fall back on the Workspace's mappings).  This corrected at least one TCK test.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StandardValueFactories.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -105,9 +105,12 @@
         }
 
         // Now assign the members, using the factories in the map or (if null) the supplied default ...
-        this.stringFactory = getFactory(factories, new StringValueFactory(this.decoder, this.encoder));
-        this.binaryFactory = (BinaryFactory)getFactory(factories,
-                                                       new InMemoryBinaryValueFactory(this.decoder, this.stringFactory));
+        this.stringFactory = getFactory(factories, new StringValueFactory(this.namespaceRegistry, this.decoder, this.encoder));
+
+        // The binary factory should NOT use the string factory that converts namespaces to prefixes ...
+        StringValueFactory stringFactoryWithoutNamespaces = new StringValueFactory(this.decoder, this.encoder);
+        this.binaryFactory = (BinaryFactory)getFactory(factories, new InMemoryBinaryValueFactory(this.decoder,
+                                                                                                 stringFactoryWithoutNamespaces));
         this.booleanFactory = getFactory(factories, new BooleanValueFactory(this.decoder, this.stringFactory));
         this.dateFactory = (DateTimeFactory)getFactory(factories, new JodaDateTimeValueFactory(this.decoder, this.stringFactory));
         this.decimalFactory = getFactory(factories, new DecimalValueFactory(this.decoder, this.stringFactory));

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StringValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StringValueFactory.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/StringValueFactory.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -43,6 +43,7 @@
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.IoException;
 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.PropertyType;
 import org.jboss.dna.graph.property.Reference;
@@ -59,14 +60,26 @@
 public class StringValueFactory extends AbstractValueFactory<String> {
 
     private final TextEncoder encoder;
+    private final NamespaceRegistry namespaceRegistry;
 
     public StringValueFactory( TextDecoder decoder,
                                TextEncoder encoder ) {
         super(PropertyType.STRING, decoder, null);
         CheckArg.isNotNull(encoder, "encoder");
         this.encoder = encoder;
+        this.namespaceRegistry = null;
     }
 
+    public StringValueFactory( NamespaceRegistry namespaceRegistry,
+                               TextDecoder decoder,
+                               TextEncoder encoder ) {
+        super(PropertyType.STRING, decoder, null);
+        CheckArg.isNotNull(encoder, "encoder");
+        CheckArg.isNotNull(namespaceRegistry, "namespaceRegistry");
+        this.encoder = encoder;
+        this.namespaceRegistry = namespaceRegistry;
+    }
+
     /**
      * @return encoder
      */
@@ -173,6 +186,9 @@
      */
     public String create( Name value ) {
         if (value == null) return null;
+        if (this.namespaceRegistry != null) {
+            return value.getString(this.namespaceRegistry, getEncoder());
+        }
         return value.getString(getEncoder());
     }
 
@@ -181,6 +197,9 @@
      */
     public String create( Path value ) {
         if (value == null) return null;
+        if (this.namespaceRegistry != null) {
+            return value.getString(this.namespaceRegistry, getEncoder());
+        }
         return value.getString(getEncoder());
     }
 
@@ -220,8 +239,8 @@
         } catch (UnsupportedEncodingException err) {
             throw new ValueFormatException(value, getPropertyType(),
                                            GraphI18n.errorConvertingType.text(byte[].class.getSimpleName(),
-                                                                            String.class.getSimpleName(),
-                                                                            value), err);
+                                                                              String.class.getSimpleName(),
+                                                                              value), err);
         }
     }
 
@@ -262,12 +281,11 @@
         } catch (UnsupportedEncodingException err) {
             throw new ValueFormatException(value, getPropertyType(),
                                            GraphI18n.errorConvertingType.text(InputStream.class.getSimpleName(),
-                                                                            String.class.getSimpleName(),
-                                                                            value), err);
+                                                                              String.class.getSimpleName(),
+                                                                              value), err);
         } catch (IOException err) {
-            throw new IoException(
-                                  GraphI18n.errorConvertingIo.text(InputStream.class.getSimpleName(), String.class.getSimpleName()),
-                                  err);
+            throw new IoException(GraphI18n.errorConvertingIo.text(InputStream.class.getSimpleName(),
+                                                                   String.class.getSimpleName()), err);
         }
     }
 
@@ -280,7 +298,8 @@
         try {
             return IoUtil.read(reader);
         } catch (IOException err) {
-            throw new IoException(GraphI18n.errorConvertingIo.text(Reader.class.getSimpleName(), String.class.getSimpleName()), err);
+            throw new IoException(GraphI18n.errorConvertingIo.text(Reader.class.getSimpleName(), String.class.getSimpleName()),
+                                  err);
         }
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/LocationTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -30,6 +30,7 @@
 import java.util.List;
 import java.util.UUID;
 import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.Property;
@@ -47,11 +48,11 @@
 public class LocationTest {
 
     private static final NoOpEncoder NO_OP_ENCODER = new NoOpEncoder();
-    private static final StringValueFactory STRING_VALUE_FACTORY = new StringValueFactory(NO_OP_ENCODER, NO_OP_ENCODER);
-    private static final NameValueFactory NAME_VALUE_FACTORY = new NameValueFactory(
-                                                                                    new SimpleNamespaceRegistry(
-                                                                                                                "http://www.jboss.org/dna/1.0"),
-                                                                                    NO_OP_ENCODER, STRING_VALUE_FACTORY);
+    private static final NamespaceRegistry NAMESPACE_REGISTRY = new SimpleNamespaceRegistry("http://www.jboss.org/dna/1.0");
+    private static final StringValueFactory STRING_VALUE_FACTORY = new StringValueFactory(NAMESPACE_REGISTRY, NO_OP_ENCODER,
+                                                                                          NO_OP_ENCODER);
+    private static final NameValueFactory NAME_VALUE_FACTORY = new NameValueFactory(NAMESPACE_REGISTRY, NO_OP_ENCODER,
+                                                                                    STRING_VALUE_FACTORY);
 
     private PathFactory pathFactory = new PathValueFactory(NO_OP_ENCODER, STRING_VALUE_FACTORY, NAME_VALUE_FACTORY);
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathOldTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathOldTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathOldTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -78,7 +78,8 @@
         path = new BasicPath(validSegmentsList, true);
         namespaceRegistry = new SimpleNamespaceRegistry();
         namespaceRegistry.register(validNamespacePrefix, validNamespaceUri);
-        StringValueFactory stringValueFactory = new StringValueFactory(Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
+        StringValueFactory stringValueFactory = new StringValueFactory(namespaceRegistry, Path.DEFAULT_DECODER,
+                                                                       Path.DEFAULT_ENCODER);
         NameValueFactory nameValueFactory = new NameValueFactory(namespaceRegistry, Path.DEFAULT_DECODER, stringValueFactory);
         pathFactory = new PathValueFactory(Path.DEFAULT_DECODER, stringValueFactory, nameValueFactory);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -55,7 +55,7 @@
     public void beforeEach() {
         this.registry = new SimpleNamespaceRegistry();
         this.registry.register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
-        this.stringValueFactory = new StringValueFactory(Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
+        this.stringValueFactory = new StringValueFactory(registry, Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
         this.nameFactory = new NameValueFactory(registry, Path.DEFAULT_DECODER, stringValueFactory);
         this.validName = nameFactory.create("dna:something");
         this.factory = new PathValueFactory(Path.DEFAULT_DECODER, stringValueFactory, nameFactory);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -77,7 +77,8 @@
         super.path = new BasicPath(validSegmentsList, true);
         namespaceRegistry = new SimpleNamespaceRegistry();
         namespaceRegistry.register(validNamespacePrefix, validNamespaceUri);
-        StringValueFactory stringValueFactory = new StringValueFactory(Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
+        StringValueFactory stringValueFactory = new StringValueFactory(namespaceRegistry, Path.DEFAULT_DECODER,
+                                                                       Path.DEFAULT_ENCODER);
         NameValueFactory nameValueFactory = new NameValueFactory(namespaceRegistry, Path.DEFAULT_DECODER, stringValueFactory);
         pathFactory = new PathValueFactory(Path.DEFAULT_DECODER, stringValueFactory, nameValueFactory);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BooleanValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BooleanValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BooleanValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -35,11 +35,10 @@
 import java.util.Iterator;
 import java.util.List;
 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.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.BooleanValueFactory;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -49,6 +48,7 @@
  */
 public class BooleanValueFactoryTest {
 
+    private NamespaceRegistry registry;
     private BooleanValueFactory factory;
     private StringValueFactory stringFactory;
 
@@ -57,7 +57,8 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.DEFAULT_ENCODER);
+        registry = new SimpleNamespaceRegistry();
+        stringFactory = new StringValueFactory(registry, Path.URL_DECODER, Path.DEFAULT_ENCODER);
         factory = new BooleanValueFactory(Path.URL_DECODER, stringFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DecimalValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DecimalValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DecimalValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -36,11 +36,10 @@
 import java.util.Iterator;
 import java.util.List;
 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.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.DecimalValueFactory;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,6 +49,7 @@
  */
 public class DecimalValueFactoryTest {
 
+    private NamespaceRegistry registry;
     private DecimalValueFactory factory;
     private StringValueFactory stringFactory;
 
@@ -58,7 +58,8 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.DEFAULT_ENCODER);
+        registry = new SimpleNamespaceRegistry();
+        stringFactory = new StringValueFactory(registry, Path.URL_DECODER, Path.DEFAULT_ENCODER);
         factory = new DecimalValueFactory(Path.URL_DECODER, stringFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DoubleValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DoubleValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/DoubleValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -36,11 +36,10 @@
 import java.util.Iterator;
 import java.util.List;
 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.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.DoubleValueFactory;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,6 +49,7 @@
  */
 public class DoubleValueFactoryTest {
 
+    private NamespaceRegistry registry;
     private DoubleValueFactory factory;
     private StringValueFactory stringFactory;
 
@@ -58,7 +58,8 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.URL_ENCODER);
+        registry = new SimpleNamespaceRegistry();
+        stringFactory = new StringValueFactory(registry, Path.URL_DECODER, Path.URL_ENCODER);
         factory = new DoubleValueFactory(Path.URL_DECODER, stringFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/InMemoryBinaryValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/InMemoryBinaryValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/InMemoryBinaryValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -64,13 +64,16 @@
     @Before
     public void setUp() throws Exception {
         encoder = Path.URL_ENCODER;
-        stringFactory = new StringValueFactory(Path.URL_DECODER, encoder);
-        factory = new InMemoryBinaryValueFactory(Path.URL_DECODER, stringFactory);
         namespaceRegistry = new SimpleNamespaceRegistry();
         namespaceRegistry.register("jboss", "http://www.jboss.org");
         namespaceRegistry.register("dna", "http://www.jboss.org/dna");
+        stringFactory = new StringValueFactory(namespaceRegistry, Path.URL_DECODER, encoder);
         nameFactory = new NameValueFactory(namespaceRegistry, Path.URL_DECODER, stringFactory);
         pathFactory = new PathValueFactory(Path.URL_DECODER, stringFactory, nameFactory);
+
+        // The binary factory should convert between names and paths without using prefixes ...
+        StringValueFactory noNamespaceStringFactory = new StringValueFactory(Path.URL_DECODER, encoder);
+        factory = new InMemoryBinaryValueFactory(Path.URL_DECODER, noNamespaceStringFactory);
     }
 
     @Test

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/JodaDateTimeValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/JodaDateTimeValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/JodaDateTimeValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -39,9 +39,6 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.JodaDateTime;
-import org.jboss.dna.graph.property.basic.JodaDateTimeValueFactory;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -68,7 +65,7 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.URL_ENCODER);
+        stringFactory = new StringValueFactory(new SimpleNamespaceRegistry(), Path.URL_DECODER, Path.URL_ENCODER);
         factory = new JodaDateTimeValueFactory(Path.URL_DECODER, stringFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/LongValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/LongValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/LongValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -39,8 +39,6 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.LongValueFactory;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -58,7 +56,7 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.URL_ENCODER);
+        stringFactory = new StringValueFactory(new SimpleNamespaceRegistry(), Path.URL_DECODER, Path.URL_ENCODER);
         factory = new LongValueFactory(Path.URL_DECODER, stringFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -58,7 +58,7 @@
         this.registry.register("dna", "http://www.jboss.org/dna/namespace");
         this.encoder = Path.DEFAULT_ENCODER;
         this.decoder = Path.DEFAULT_DECODER;
-        this.stringValueFactory = new StringValueFactory(decoder, encoder);
+        this.stringValueFactory = new StringValueFactory(registry, decoder, encoder);
         this.factory = new NameValueFactory(registry, decoder, stringValueFactory);
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/PathValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/PathValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/PathValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -54,7 +54,7 @@
     public void beforeEach() {
         this.registry = new SimpleNamespaceRegistry();
         this.registry.register("dna", "http://www.jboss.org/dna/namespace");
-        this.stringValueFactory = new StringValueFactory(Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
+        this.stringValueFactory = new StringValueFactory(registry, Path.DEFAULT_DECODER, Path.DEFAULT_ENCODER);
         this.nameFactory = new NameValueFactory(registry, Path.DEFAULT_DECODER, stringValueFactory);
         this.factory = new PathValueFactory(Path.DEFAULT_DECODER, stringValueFactory, nameFactory);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/UuidValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/UuidValueFactoryTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/UuidValueFactoryTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -39,9 +39,6 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.basic.StringValueFactory;
-import org.jboss.dna.graph.property.basic.UuidReference;
-import org.jboss.dna.graph.property.basic.UuidValueFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -60,7 +57,7 @@
      */
     @Before
     public void setUp() throws Exception {
-        stringFactory = new StringValueFactory(Path.URL_DECODER, Path.URL_ENCODER);
+        stringFactory = new StringValueFactory(new SimpleNamespaceRegistry(), Path.URL_DECODER, Path.URL_ENCODER);
         factory = new UuidValueFactory(Path.URL_DECODER, stringFactory);
         uuid = UUID.randomUUID();
     }

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -54,12 +54,14 @@
     public static I18n unableToUnregisterReservedNamespacePrefix;
     public static I18n unableToUnregisterReservedNamespaceUri;
     public static I18n unableToUnregisterPrefixForNamespaceThatIsNotRegistered;
+    public static I18n unableToRemapUriNotRegisteredInNamespaceRegistry;
+    public static I18n unableToRemapUriUsingPrefixUsedInNamespaceRegistry;
 
     public static I18n errorWhileInitializingTheNamespaceRegistry;
     public static I18n invalidPathParameter;
-    
+
     public static I18n typeNotFound;
-    
+
     public static I18n REP_NAME_DESC;
     public static I18n REP_VENDOR_DESC;
     public static I18n SPEC_NAME_DESC;

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -32,7 +32,7 @@
 import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
 import javax.xml.XMLConstants;
-import net.jcip.annotations.ThreadSafe;
+import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.common.xml.XmlCharacters;
 import org.jboss.dna.graph.JcrLexicon;
@@ -40,17 +40,25 @@
 import org.jboss.dna.graph.JcrNtLexicon;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.NamespaceRegistry.Namespace;
-import org.jboss.dna.graph.property.basic.SimpleNamespaceRegistry;
-import org.jboss.dna.graph.property.basic.ThreadSafeNamespaceRegistry;
 
 /**
  * A thread-safe JCR {@link javax.jcr.NamespaceRegistry} implementation that has the standard JCR namespaces pre-registered and
  * enforces the JCR semantics for {@link #registerNamespace(String, String) registering} and {@link #unregisterNamespace(String)
  * unregistering} namespaces.
+ * <p>
+ * Note that this implementation is {@link NotThreadSafe not thread safe}, since it is used within a single {@link JcrWorkspace}
+ * and single {@link JcrSession}, and according to the JCR specification these interfaces are not thread safe.
+ * </p>
  */
- at ThreadSafe
+ at NotThreadSafe
 class JcrNamespaceRegistry implements javax.jcr.NamespaceRegistry {
 
+    public static enum Behavior {
+        JSR170_SESSION,
+        JSR283_SESSION,
+        WORKSPACE;
+    }
+
     static final String DEFAULT_NAMESPACE_PREFIX = "";
     static final String DEFAULT_NAMESPACE_URI = "";
 
@@ -89,18 +97,27 @@
         STANDARD_BUILT_IN_URIS = Collections.unmodifiableSet(new HashSet<String>(namespaces.values()));
     }
 
+    private final Behavior behavior;
     private final NamespaceRegistry registry;
+    private final NamespaceRegistry workspaceRegistry;
 
-    JcrNamespaceRegistry() {
-        this(new ThreadSafeNamespaceRegistry(new SimpleNamespaceRegistry())); // thread-safe implementation
+    JcrNamespaceRegistry( NamespaceRegistry workspaceRegistry ) {
+        this(Behavior.WORKSPACE, null, workspaceRegistry);
     }
 
-    JcrNamespaceRegistry( NamespaceRegistry dnaRegistry ) {
-        this.registry = dnaRegistry;
+    JcrNamespaceRegistry( Behavior behavior,
+                          NamespaceRegistry localRegistry,
+                          NamespaceRegistry workspaceRegistry ) {
+        this.behavior = behavior;
+        this.registry = localRegistry != null ? localRegistry : workspaceRegistry;
+        this.workspaceRegistry = workspaceRegistry;
         // Add the built-ins, ensuring we overwrite any badly-initialized values ...
         for (Map.Entry<String, String> builtIn : STANDARD_BUILT_IN_NAMESPACES_BY_PREFIX.entrySet()) {
             this.registry.register(builtIn.getKey(), builtIn.getValue());
         }
+        assert this.behavior != null;
+        assert this.registry != null;
+        assert this.workspaceRegistry != null;
     }
 
     /**
@@ -109,11 +126,13 @@
      * @see javax.jcr.NamespaceRegistry#getPrefix(java.lang.String)
      */
     public String getPrefix( String uri ) throws NamespaceException, RepositoryException {
-        // Check the standard ones first, ensuring that invalid changes to the persistent storage don't matter ...
-        String prefix = STANDARD_BUILT_IN_PREFIXES_BY_NAMESPACE.get(uri);
-        if (prefix != null) return prefix;
+        if (behavior == Behavior.WORKSPACE) {
+            // Check the standard ones first, ensuring that invalid changes to the persistent storage don't matter ...
+            String prefix = STANDARD_BUILT_IN_PREFIXES_BY_NAMESPACE.get(uri);
+            if (prefix != null) return prefix;
+        }
         // Now check the underlying registry ...
-        prefix = registry.getPrefixForNamespaceUri(uri, false);
+        String prefix = registry.getPrefixForNamespaceUri(uri, false);
         if (prefix == null) {
             throw new NamespaceException(JcrI18n.noNamespaceWithUri.text(uri));
         }
@@ -141,11 +160,13 @@
      * @see javax.jcr.NamespaceRegistry#getURI(java.lang.String)
      */
     public String getURI( String prefix ) throws NamespaceException, RepositoryException {
-        // Check the standard ones first, ensuring that invalid changes to the persistent storage don't matter ...
-        String uri = STANDARD_BUILT_IN_NAMESPACES_BY_PREFIX.get(prefix);
-        if (uri != null) return uri;
+        if (behavior == Behavior.WORKSPACE) {
+            // Check the standard ones first, ensuring that invalid changes to the persistent storage don't matter ...
+            String uri = STANDARD_BUILT_IN_NAMESPACES_BY_PREFIX.get(prefix);
+            if (uri != null) return uri;
+        }
         // Now check the underlying registry ...
-        uri = registry.getNamespaceForPrefix(prefix);
+        String uri = registry.getNamespaceForPrefix(prefix);
         if (uri == null) {
             throw new NamespaceException(JcrI18n.noNamespaceWithPrefix.text(prefix));
         }
@@ -176,17 +197,94 @@
                                                 String uri ) throws NamespaceException, RepositoryException {
         CheckArg.isNotNull(prefix, "prefix");
         CheckArg.isNotNull(uri, "uri");
+
+        switch (behavior) {
+            case JSR170_SESSION:
+                // ----------------------------------------------------------
+                // JSR-170 Session remapping behavior (see Section 6.3.3) ...
+                // ----------------------------------------------------------
+                // Section 6.3.3:
+                // "If existingUri is not registered in the NamespaceRegistry a NamespaceException will be thrown.
+                //
+                // If newPrefix is already locally mapped to existingUri (i.e., within this Session, by virtue
+                // of an earlier setNamespaceRegistry call) then this method returns silently and has no effect.
+                //
+                // If newPrefix is already locally mapped to a URI other than existingUri, then that URI reverts to its
+                // globally mapped prefix (as set in the NamespaceRegistry) and newPrefix is locally mapped to existingUri.
+                //
+                // If newPrefix is already assigned in the global NamespaceRegistry to otheruri (which differs from
+                // existingUri) and otherUri has not been locally mapped to another prefix which differs from newPrefix,
+                // then a NamespaceException will be thrown. In order to successfully locally map newPrefix to existingUri,
+                // otherUri must first be locally mapped to another prefix."
+
+                // The URI must already be registered ...
+                String existingPrefix = registry.getPrefixForNamespaceUri(uri, false);
+                if (existingPrefix == null) {
+                    // Paragraph 1 ...
+                    throw new NamespaceException(JcrI18n.unableToRemapUriNotRegisteredInNamespaceRegistry.text(prefix, uri));
+                }
+                if (existingPrefix.equals(prefix)) return; // Paragraph 2
+
+                // Is the prefix already used in a mapping ...
+                String existingUri = registry.getNamespaceForPrefix(prefix);
+                if (existingUri != null) {
+                    // Is this existing mapping local, or is it in the global (workspace) registry?
+                    String globalPrefix = workspaceRegistry.getPrefixForNamespaceUri(existingUri, false);
+                    if (!prefix.equals(globalPrefix)) {
+                        // Paragraph 3: The mapping is local to the session, so this local mapping should just be reverted ...
+                        registry.unregister(existingUri);
+                    }
+
+                    // Paragraph 4: The mapping is global, so this is not allowed; the existing ...
+                    String msg = JcrI18n.unableToRemapUriUsingPrefixUsedInNamespaceRegistry.text(prefix, uri, existingUri);
+                    throw new NamespaceException(msg);
+                }
+
+                // Otherwise, the prefix is not already used in a mapping, so we can continue ...
+
+                break;
+
+            case JSR283_SESSION:
+                // --------------------------------------
+                // JSR-283 Session remapping behavior ...
+                // --------------------------------------
+                // Section 4.3.3 (of the Draft specification):
+                // "All local mappings already present in the Session that include either the specified prefix
+                // or the specified uri are removed and the new mapping is added."
+                String existingUriForPrefix = registry.getNamespaceForPrefix(prefix);
+                if (existingUriForPrefix != null) {
+                    registry.unregister(existingUriForPrefix);
+                }
+                registry.unregister(uri);
+
+                break;
+
+            case WORKSPACE:
+                // --------------------------------------------------
+                // JSR-170 & JSR-283 Workspace namespace registry ...
+                // --------------------------------------------------
+
+                // Check the zero-length prefix and zero-length URI ...
+                if (DEFAULT_NAMESPACE_PREFIX.equals(prefix) || DEFAULT_NAMESPACE_URI.equals(uri)) {
+                    throw new NamespaceException(JcrI18n.unableToChangeTheDefaultNamespace.text());
+                }
+                // Check whether the prefix or URI are reserved (case-sensitive) ...
+                if (STANDARD_BUILT_IN_PREFIXES.contains(prefix)) {
+                    throw new NamespaceException(JcrI18n.unableToRegisterReservedNamespacePrefix.text(prefix, uri));
+                }
+                if (STANDARD_BUILT_IN_URIS.contains(uri)) {
+                    throw new NamespaceException(JcrI18n.unableToRegisterReservedNamespaceUri.text(prefix, uri));
+                }
+                break;
+            default:
+                assert false; // should never happen
+        }
+
         // Check the zero-length prefix and zero-length URI ...
         if (DEFAULT_NAMESPACE_PREFIX.equals(prefix) || DEFAULT_NAMESPACE_URI.equals(uri)) {
             throw new NamespaceException(JcrI18n.unableToChangeTheDefaultNamespace.text());
         }
-        // Check whether the prefix or URI are reserved (case-sensitive) ...
-        if (STANDARD_BUILT_IN_PREFIXES.contains(prefix)) {
-            throw new NamespaceException(JcrI18n.unableToRegisterReservedNamespacePrefix.text(prefix, uri));
-        }
-        if (STANDARD_BUILT_IN_URIS.contains(uri)) {
-            throw new NamespaceException(JcrI18n.unableToRegisterReservedNamespaceUri.text(prefix, uri));
-        }
+
         // Check whether the prefix begins with 'xml' (in any case) ...
         if (prefix.toLowerCase().startsWith(XML_NAMESPACE_PREFIX)) {
             throw new NamespaceException(JcrI18n.unableToRegisterNamespaceUsingXmlPrefix.text(prefix, uri));

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -131,7 +131,7 @@
 
     /**
      * {@inheritDoc}
-     *  
+     * 
      * @see javax.jcr.Property#getString()
      */
     public String getString() throws RepositoryException {
@@ -163,8 +163,7 @@
      * @see javax.jcr.Property#getValues()
      */
     public Value[] getValues() throws ValueFormatException {
-
-        return new Value[] {jcrValue};
+        throw new ValueFormatException();
     }
 
     /*

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -61,6 +61,7 @@
 import org.jboss.dna.graph.property.UuidFactory;
 import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
+import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.xml.sax.ContentHandler;
 import com.google.common.base.ReferenceType;
 import com.google.common.collect.ReferenceMap;
@@ -93,7 +94,7 @@
     /**
      * The execution context for this session, which uses the {@link #sessionRegistry session's namespace registry}
      */
-    private final ExecutionContext executionContext;
+    protected final ExecutionContext executionContext;
 
     /**
      * The graph representing this session, which uses the {@link #graph session's graph}.
@@ -123,9 +124,10 @@
         this.workspace = workspace;
 
         // Create an execution context for this session, which should use the local namespace registry ...
-        NamespaceRegistry local = new LocalNamespaceRegistry(workspaceContext.getNamespaceRegistry());
+        NamespaceRegistry workspaceRegistry = workspaceContext.getNamespaceRegistry();
+        NamespaceRegistry local = new LocalNamespaceRegistry(workspaceRegistry);
         this.executionContext = workspaceContext.with(local);
-        this.sessionRegistry = new JcrNamespaceRegistry(local);
+        this.sessionRegistry = new JcrNamespaceRegistry(Behavior.JSR170_SESSION, local, workspaceRegistry);
 
         // Set up the graph to use for this session (which uses the session's namespace registry and context) ...
         this.graph = Graph.create(this.repository.getRepositorySourceName(),
@@ -425,10 +427,7 @@
         populateNode(rootNode, graph.getNodeAt(executionContext.getValueFactories().getPathFactory().createRootPath()));
 
         // Root nodes need to have a type in JCR land
-        // JcrProperty primaryType = new JcrProperty(rootNode, getExecutionContext(), JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.BASE);
-        String typeValue = JcrNtLexicon.BASE.getString(executionContext.getNamespaceRegistry());
-        JcrProperty primaryType = new JcrProperty(rootNode, executionContext, JcrLexicon.PRIMARY_TYPE, typeValue);
-
+        JcrProperty primaryType = new JcrProperty(rootNode, executionContext, JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.BASE);
         // TODO: Not liking the hard-code
         rootNode.properties.add(primaryType);
 
@@ -603,7 +602,8 @@
                 if (uuid == null && DnaLexicon.UUID.equals(name)) uuid = uuidFactory.create(dnaProp.getValues()).next();
                 else if (jcrUuidName.equals(name)) dnaUuidProp = dnaProp;
                 else if (jcrMixinTypesName.equals(name)) {
-                    org.jboss.dna.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories().getStringFactory();
+                    org.jboss.dna.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories()
+                                                                                                      .getStringFactory();
                     for (String mixin : stringFactory.create(dnaProp)) {
                         if ("mix:referenceable".equals(mixin)) referenceable = true;
                     }

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -65,7 +65,6 @@
 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.graph.property.basic.ThreadSafeNamespaceRegistry;
 import org.xml.sax.ContentHandler;
 
 /**
@@ -144,10 +143,9 @@
         org.jboss.dna.graph.property.NamespaceRegistry persistentRegistry = new GraphNamespaceRegistry(namespaceGraph,
                                                                                                        namespacesPath,
                                                                                                        uriProperty, namespaceType);
-        persistentRegistry = new ThreadSafeNamespaceRegistry(persistentRegistry);
         this.context = context.with(persistentRegistry);
 
-        // Set up and initialize the persistent (and thread-safe) JCR namespace registry ...
+        // Set up and initialize the persistent JCR namespace registry ...
         this.workspaceRegistry = new JcrNamespaceRegistry(persistentRegistry);
 
         // Now create a graph with this new execution context ...

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-02 20:03:51 UTC (rev 743)
@@ -44,6 +44,8 @@
 unableToUnregisterReservedNamespacePrefix = Unable to unregister the namespace "{1}" with prefix "{0}" because this prefix is reserved
 unableToUnregisterReservedNamespaceUri = Unable to unregister the namespace "{1}" with prefix "{0}" because this URI is reserved
 unableToUnregisterPrefixForNamespaceThatIsNotRegistered = The namespace with prefix "{0}" is not registered and cannot be unregistered
+unableToRemapUriNotRegisteredInNamespaceRegistry = Unable to remap the namespace "{1}" to prefix "{0}" because the URI is not already registered in the workspace's namespace registry
+unableToRemapUriUsingPrefixUsedInNamespaceRegistry = Unable to remap the namespace "{1}" to prefix "{0}" because the prefix is already used as the prefix for the namespace "{2}" in the workspace's namespace registry
 
 errorWhileInitializingTheNamespaceRegistry = Error while initializing the namespace registry for workspace "{0}"
 invalidPathParameter = The "{1}" parameter value "{0}" was not a valid path

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -87,7 +87,7 @@
             // addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);

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-03-02 17:40:37 UTC (rev 742)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-03-02 20:03:51 UTC (rev 743)
@@ -150,9 +150,9 @@
         assertThat(registry.getURI(JcrLexicon.Namespace.PREFIX), is(JcrLexicon.Namespace.URI));
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowGetNodeTypeManager() throws Exception {
-        workspace.getNodeTypeManager();
+    @Test
+    public void shouldGetNodeTypeManager() throws Exception {
+        assertThat(workspace.getNodeTypeManager(), is(notNullValue()));
     }
 
     @Test( expected = UnsupportedOperationException.class )



From dna-commits at lists.jboss.org  Mon Mar  2 15:11:29 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 02 Mar 2009 15:11:29 -0500
Subject: [dna-commits] DNA SVN:  r744 - trunk.
Message-ID: <E1LeEU5-0002A5-Nr@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-02 15:11:29 -0500 (Mon, 02 Mar 2009)
New Revision: 744

Modified:
   trunk/pom.xml
Log:
Added 'dna-jcr' back into the build.

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-03-02 20:03:51 UTC (rev 743)
+++ trunk/pom.xml	2009-03-02 20:11:29 UTC (rev 744)
@@ -114,7 +114,7 @@
 		<module>dna-common</module>
 		<module>dna-graph</module>
 		<module>dna-repository</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>



From dna-commits at lists.jboss.org  Mon Mar  2 20:30:03 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 02 Mar 2009 20:30:03 -0500
Subject: [dna-commits] DNA SVN:  r745 - trunk/dna-jcr/src/test/resources.
Message-ID: <E1LeJSN-0007l1-RF@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-02 20:30:03 -0500 (Mon, 02 Mar 2009)
New Revision: 745

Added:
   trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml
Log:
Corrected name of file to have the same case as in the JackrabbitTckTest.java.

Added: trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	                        (rev 0)
+++ trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	2009-03-03 01:30:03 UTC (rev 745)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss DNA (http://www.jboss.org/dna)
+  ~
+  ~ See the COPYRIGHT.txt file distributed with this work for information
+  ~ regarding copyright ownership.  Some portions may be licensed
+  ~ to Red Hat, Inc. under one or more contributor license agreements.
+  ~ See the AUTHORS.txt file in the distribution for a full listing of 
+  ~ individual contributors.
+  ~
+  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+  ~ is licensed to you under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ JBoss DNA is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+  ~ for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public License
+  ~ along with this distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
+            xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+    jcr:primaryType="nt:unstructured">
+	<nt:unstructured jcr:name="node1" jcr:primaryType="nt:unstructured" prop1="foo" />
+	<nt:unstructured jcr:name="node2" jcr:primaryType="nt:unstructured" prop2="bar" />
+	<nt:unstructured jcr:name="node3" jcr:primaryType="nt:unstructured" />
+	<nt:unstructured jcr:name="node4" jcr:primaryType="nt:unstructured" />
+</testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Mon Mar  2 20:33:11 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 02 Mar 2009 20:33:11 -0500
Subject: [dna-commits] DNA SVN:  r746 - trunk/dna-jcr/src/test/resources.
Message-ID: <E1LeJVP-0000mT-1T@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-02 20:33:10 -0500 (Mon, 02 Mar 2009)
New Revision: 746

Removed:
   trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml
Log:
Attempted to correct case

Deleted: trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml	2009-03-03 01:30:03 UTC (rev 745)
+++ trunk/dna-jcr/src/test/resources/repositoryJackRabbitTck.xml	2009-03-03 01:33:10 UTC (rev 746)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-  ~ JBoss DNA (http://www.jboss.org/dna)
-  ~
-  ~ See the COPYRIGHT.txt file distributed with this work for information
-  ~ regarding copyright ownership.  Some portions may be licensed
-  ~ to Red Hat, Inc. under one or more contributor license agreements.
-  ~ See the AUTHORS.txt file in the distribution for a full listing of 
-  ~ individual contributors.
-  ~
-  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
-  ~ is licensed to you under the terms of the GNU Lesser General Public License as
-  ~ published by the Free Software Foundation; either version 2.1 of
-  ~ the License, or (at your option) any later version.
-  ~
-  ~ JBoss DNA is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-  ~ for more details.
-  ~
-  ~ You should have received a copy of the GNU Lesser General Public License
-  ~ along with this distribution; if not, write to:
-  ~ Free Software Foundation, Inc.
-  ~ 51 Franklin Street, Fifth Floor
-  ~ Boston, MA  02110-1301  USA
-  -->
-<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
-            xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
-    jcr:primaryType="nt:unstructured">
-	<nt:unstructured jcr:name="node1" jcr:primaryType="nt:unstructured" prop1="foo" />
-	<nt:unstructured jcr:name="node2" jcr:primaryType="nt:unstructured" prop2="bar" />
-	<nt:unstructured jcr:name="node3" jcr:primaryType="nt:unstructured" />
-	<nt:unstructured jcr:name="node4" jcr:primaryType="nt:unstructured" />
-</testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Tue Mar  3 14:20:59 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 03 Mar 2009 14:20:59 -0500
Subject: [dna-commits] DNA SVN: r747 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 2 other directories.
Message-ID: <E1LeaAl-0003f3-LB@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-03 14:20:59 -0500 (Tue, 03 Mar 2009)
New Revision: 747

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-288 JcrRepository.login Does Not Match JCR 1.0 Specification

Applied the patch that corrects the behavior when the workspace name submitted to the login method does not match the name of an existing workspace.  The patch also adds to the JCR TCK tests.

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-03-03 01:33:10 UTC (rev 746)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-03-03 19:20:59 UTC (rev 747)
@@ -295,8 +295,8 @@
                 // Verify that the workspace exists (or can be created) ...
                 Set<String> workspaces = graph.getWorkspaces();
                 if (!workspaces.contains(workspaceName)) {
-                    // Try to create it ...
-                    graph.createWorkspace().namedSomethingLike(workspaceName).getName();
+                    // 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();
             } catch (InvalidWorkspaceException e) {

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-03-03 01:33:10 UTC (rev 746)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-03 19:20:59 UTC (rev 747)
@@ -25,7 +25,7 @@
 credentialsMustProvideJaasMethod = The Credentials class "{0}" must implement either "public LoginContext getLoginContext();" or "public AccessControlContext getAccessControlContext();".
 credentialsMustReturnAccessControlContext = The "getAccessControlContext()" method in Credentials class "{0}" must not return a null.
 credentialsMustReturnLoginContext = The "getLoginContext()" method in Credentials class "{0}" must not return a null.
-defaultWorkspaceName = Default
+defaultWorkspaceName=
 inputStreamConsumed = This value was already consumed as an input stream.
 nonInputStreamConsumed = This value was already consumed as a non-input stream.
 pathNotFound = No item exists at path {0}

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-03 01:33:10 UTC (rev 746)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-03 19:20:59 UTC (rev 747)
@@ -75,7 +75,7 @@
             addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
@@ -96,7 +96,7 @@
             addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
 
             // These might not all be level one tests
             // addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-03-03 01:33:10 UTC (rev 746)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-03-03 19:20:59 UTC (rev 747)
@@ -6,4 +6,4 @@
 javax.jcr.tck.nodename4=node4
 javax.jcr.tck.propertyname1=prop1
 javax.jcr.tck.propertyname2=prop2
-javax.jcr.tck.workspacename=test
+javax.jcr.tck.workspacename=



From dna-commits at lists.jboss.org  Tue Mar  3 15:34:42 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 03 Mar 2009 15:34:42 -0500
Subject: [dna-commits] DNA SVN: r748 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LebK6-0008W4-9C@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-03 15:34:42 -0500 (Tue, 03 Mar 2009)
New Revision: 748

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/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.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
Log:
DNA-287 AbstractJcrNode.getAncestor Does Not Match JCR 1.0 Specification

Applied the patch that corrects the behavior of get ancestor, which per the spec is absolute and not relative (as was implemented).

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -24,6 +24,7 @@
 package org.jboss.dna.jcr;
 
 import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
 import javax.jcr.RepositoryException;
 
 /**
@@ -74,6 +75,45 @@
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Item#getAncestor(int)
+     */
+    public final Item getAncestor( int depth ) throws RepositoryException {
+        if (depth < 0) {
+            throw new ItemNotFoundException(JcrI18n.noNegativeDepth.text(depth));
+        }
+
+        /*
+         * depth argument is absolute depth from root of content graph, not relative depth from current node.
+         * That is, if current node is at path /foo/bar/baz, getAncestor(1) returns node at /foo, getAncestor(2)
+         * returns node at /foo/bar, getAncestor(3) returns node at /foo/bar/baz, getAncestor(0) returns root node,
+         * and any other argument results in ItemNotFoundException.
+         * Next statement converts depth parameter from a relative depth to an absolute depth.
+         */
+        depth = getDepth() - depth;
+
+        if (depth < 0) {
+            throw new ItemNotFoundException(JcrI18n.tooDeep.text(depth));
+        }
+
+        Item ancestor = this;
+        while (--depth >= 0) {
+            ancestor = ancestor.getParent();
+        }
+        return ancestor;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Item#getDepth()
+     */
+    public int getDepth() throws RepositoryException {
+        return getParent().getDepth() + 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Item#refresh(boolean)
      */

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -165,21 +165,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws IllegalArgumentException if <code>depth</code> is negative.
-     * @see javax.jcr.Item#getAncestor(int)
-     */
-    public final Item getAncestor( int depth ) throws RepositoryException {
-        CheckArg.isNonNegative(depth, "depth");
-        Node ancestor = this;
-        while (--depth >= 0) {
-            ancestor = ancestor.getParent();
-        }
-        return ancestor;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#getBaseVersion()
      */

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -88,25 +88,6 @@
         return new JcrValue<T>(valueFactories, propertyType, valueClass.cast(value));
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws IllegalArgumentException if <code>depth</code> is negative.
-     * @see javax.jcr.Item#getAncestor(int)
-     */
-    public final Item getAncestor( int depth ) throws RepositoryException {
-        return (depth == 0 ? this : node.getAncestor(depth - 1));
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Item#getDepth()
-     */
-    public int getDepth() throws RepositoryException {
-        return getParent().getDepth() + 1;
-    }
-
     final ExecutionContext getExecutionContext() {
         return executionContext;
     }

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -61,7 +61,11 @@
     public static I18n invalidPathParameter;
 
     public static I18n typeNotFound;
-
+    
+    // Used in AbstractJcrNode#getAncestor
+    public static I18n noNegativeDepth;
+    public static I18n tooDeep;
+    
     public static I18n REP_NAME_DESC;
     public static I18n REP_VENDOR_DESC;
     public static I18n SPEC_NAME_DESC;

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -52,15 +52,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Item#getDepth()
-     */
-    public int getDepth() throws RepositoryException {
-        return getParent().getDepth() + 1;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see javax.jcr.Node#getIndex()
      */
     public int getIndex() {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -43,6 +43,7 @@
      * @return 0;
      * @see javax.jcr.Item#getDepth()
      */
+    @Override
     public int getDepth() {
         return 0;
     }

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-03 20:34:42 UTC (rev 748)
@@ -59,3 +59,6 @@
 workspaceNameIsInvalid = "{1}" is not a valid workspace name for the "{0}" repository
 errorVerifyingWorkspaceName =Error validating the workspace name "{1}" for the "{0}" repository\: {2}
 typeNotFound=No type exists with name "{0}"
+
+noNegativeDepth=Depth parameter ({0}) cannot be negative
+tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this node

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -25,7 +25,6 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
-import javax.jcr.Item;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.Session;
@@ -46,14 +45,6 @@
             public void accept( ItemVisitor visitor ) {
             }
 
-            public Item getAncestor( int depth ) {
-                return null;
-            }
-
-            public int getDepth() {
-                return 0;
-            }
-
             public String getName() {
                 return null;
             }

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -86,6 +86,7 @@
             this.parent = parent;
         }
 
+        @Override
         public int getDepth() {
             return 0;
         }
@@ -143,7 +144,7 @@
         new MockAbstractJcrNode(null, null, null);
     }
 
-    @Test( expected = IllegalArgumentException.class )
+    @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowNegativeAncestorDepth() throws Exception {
         node.getAncestor(-1);
     }

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-03-03 19:20:59 UTC (rev 747)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-03 20:34:42 UTC (rev 748)
@@ -98,7 +98,7 @@
         new MockAbstractJcrProperty(node, executionContext, null);
     }
 
-    @Test( expected = IllegalArgumentException.class )
+    @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowNegativeAncestorDepth() throws Exception {
         stub(node.getAncestor(-2)).toThrow(new IllegalArgumentException());
         prop.getAncestor(-1);
@@ -106,9 +106,9 @@
 
     @Test
     public void shouldProvideAncestor() throws Exception {
-        assertThat(prop.getAncestor(0), is((Item)prop));
-        stub(node.getAncestor(0)).toReturn(node);
-        assertThat(prop.getAncestor(1), is((Item)node));
+        assertThat(prop.getAncestor(prop.getDepth()), is((Item)prop));
+        stub(node.getAncestor(node.getDepth())).toReturn(node);
+        assertThat(prop.getAncestor(prop.getDepth() - 1), is((Item)node));
     }
 
     @Test( expected = ItemNotFoundException.class )



From dna-commits at lists.jboss.org  Tue Mar  3 17:02:41 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 03 Mar 2009 17:02:41 -0500
Subject: [dna-commits] DNA SVN: r749 -
	trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn.
Message-ID: <E1LechF-0002ru-5S@committer01.frg.pub.inap.atl.jboss.com>

Author: spagop
Date: 2009-03-03 17:02:41 -0500 (Tue, 03 Mar 2009)
New Revision: 749

Modified:
   trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNConnectorTestUtil.java
   trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
   trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositorySourceTest.java
Log:
some coding enhancements

Modified: trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNConnectorTestUtil.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNConnectorTestUtil.java	2009-03-03 20:34:42 UTC (rev 748)
+++ trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNConnectorTestUtil.java	2009-03-03 22:02:41 UTC (rev 749)
@@ -23,6 +23,9 @@
  */
 package org.jboss.dna.connector.svn;
 
+import java.io.File;
+import java.io.IOException;
+import org.jboss.dna.common.util.FileUtil;
 import org.tmatesoft.svn.core.SVNException;
 import org.tmatesoft.svn.core.SVNURL;
 import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
@@ -38,16 +41,19 @@
  */
 public class SVNConnectorTestUtil {
 
-//    public static void main( String[] args ) {
-//        try {
-//            System.out.println("hello ......");
-//            SVNRepository repos = createRepository("file:////Users/sp/innoq-dev/innoq-jboss/jboss-dna/extensions/dna-connector-svn/src/test/resources/dummy_svn_repos", "sp", "p530020");
-//            System.out.println("Repository Root: " + repos.getRepositoryRoot(true));
-//            System.out.println("Repository UUID: " + repos.getRepositoryUUID(true));
-//            System.out.println("hello ......");
-//        } catch (SVNException e) {
-//        }
-//    }
+    public static void main( String[] args ) throws Exception {
+        try {
+            System.out.println("hello ......");
+            String svnUrl = SVNConnectorTestUtil.createURL("src/test/resources/dummy_svn_repos", "target/copy_of dummy_svn_repos");
+            String username = "sp";
+            String password = "";
+            SVNRepository repos = createRepository(svnUrl, username, password);
+            System.out.println("Repository Root: " + repos.getRepositoryRoot(true));
+            System.out.println("Repository UUID: " + repos.getRepositoryUUID(true));
+            System.out.println("hello ......");
+        } catch (SVNException e) {
+        }
+    }
 
     /**
      * Create a {@link SVNRepository} from a http protocol.
@@ -74,7 +80,21 @@
         repository.setAuthenticationManager(authManager);
         return repository;
     }
+    
+    public static String createURL(String src, String dst) throws IOException {
+        // First we need to find the absolute path. Note that Maven always runs the tests from the project's directory,
+        // so use new File to create an instance at the current location ...
+        File mySrc = new File(src);
+        File myDst = new File(dst);
 
+        // make sure the destination is empty before we copy
+        FileUtil.delete(myDst);
+        FileUtil.copy(mySrc, myDst);
+
+        // Now set the two path roots
+        String url = myDst.getCanonicalFile().toURL().toString();
+        return url.replaceFirst("file:/", "file://localhost/");
+    }
     private SVNConnectorTestUtil() {
         // prevent constructor
     }

Modified: trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java	2009-03-03 20:34:42 UTC (rev 748)
+++ trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java	2009-03-03 22:02:41 UTC (rev 749)
@@ -23,17 +23,19 @@
 */
 package org.jboss.dna.connector.svn;
 
+import static org.mockito.Mockito.stub;
+import static org.mockito.Mockito.verify;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.jboss.dna.common.text.UrlEncoder;
 import org.jboss.dna.common.util.FileUtil;
 import org.jboss.dna.graph.DnaLexicon;
@@ -78,6 +80,7 @@
     private String uuidPropertyName;
     private String sourceName;
     private Graph graph;
+    private String svnUrl;
 
     @Mock
     private CachePolicy policy;
@@ -95,24 +98,9 @@
         pathFactory = context.getValueFactories().getPathFactory();
         propertyFactory = context.getPropertyFactory();
         nameFactory = context.getValueFactories().getNameFactory();
-
-        // First we need to find the absolute path. Note that Maven always runs the tests from the project's directory,
-        // so use new File to create an instance at the current location ...
-        File src = new File("src/test/resources/dummy_svn_repos");
-        File dst = new File("target/copy_of dummy_svn_repos");
-
-        // make sure the destination is empty before we copy
-        FileUtil.delete(dst);
-        FileUtil.copy(src, dst);
-
-        // Now set the two path roots
-        String svnUrl = dst.getCanonicalFile().toURL().toString();
-        svnUrl = svnUrl.replaceFirst("file:/", "file://localhost/");
+        svnUrl = SVNConnectorTestUtil.createURL("src/test/resources/dummy_svn_repos", "target/copy_of dummy_svn_repos");
         String username = "sp";
         String password = "";
-        // Create a Repository instance from the http-protocol, that use a anonymous credential.
-        // String url = "http://anonsvn.jboss.org/repos/dna/trunk/extensions/dna-connector-svn/src/test/resources";
-
         // Set up the appropriate factory for a particular protocol
         repository = SVNConnectorTestUtil.createRepository(svnUrl, username, password);
         sourceName = "the source name";
@@ -157,15 +145,13 @@
         assertThat(connection.getDefaultCachePolicy(), is(sameInstance(policy)));
     }
 
-    // @Test
-    // public void shouldGetTheSVNRepositoryRootFromTheSVNRepositoryWhenPinged() throws Exception {
-    // CachePolicy policy = mock(CachePolicy.class);
-    // repository = mock(SVNRepository.class);
-    // connection = new SVNRepositoryConnection("the source name", policy, false, repository);
-    // // stub(repository.getRepositoryRoot(true)).toReturn(null);
-    // assertThat(connection.ping(1, TimeUnit.SECONDS), is(true));
-    // verify(repository).getRepositoryRoot(true);
-    // }
+    @Test
+    public void shouldGetTheSVNRepositoryRootFromTheSVNRepositoryWhenPinged() throws Exception {
+        CachePolicy policy = mock(CachePolicy.class);
+        repository  = SVNConnectorTestUtil.createRepository(svnUrl, "sp", "");
+        connection = new SVNRepositoryConnection("the source name", policy, false, repository);
+        assertThat(connection.ping(1, TimeUnit.SECONDS), is(true));
+    }
 
     @Test
     public void shouldHaveNoOpListenerWhenCreated() {

Modified: trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositorySourceTest.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositorySourceTest.java	2009-03-03 20:34:42 UTC (rev 748)
+++ trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositorySourceTest.java	2009-03-03 22:02:41 UTC (rev 749)
@@ -29,9 +29,11 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.mock;
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -66,22 +68,9 @@
         MockitoAnnotations.initMocks(this);
         validName = "svn source";
         validUuidPropertyName = "dna:uuid";
-
-        // For file protocol access
-        url = "file:///dummy_svn_repos";
-
-        // For http protocol access
-        // url = "http://anonsvn.jboss.org/repos/dna/trunk/extensions/dna-connector-svn/src/test/resources";
-        username = "anonymous";
-        password = "anonymous";
-
-        // For https protocol access
-        // For svn protocol access
-        // For svn-ssh protocol access
-
+        url = SVNConnectorTestUtil.createURL("src/test/resources/dummy_svn_repos", "target/copy_of dummy_svn_repos");
         validRootNodeUuid = UUID.randomUUID();
         source = new SVNRepositorySource();
-
     }
 
     @After
@@ -244,12 +233,36 @@
     }
 
     // Only with local file protocol
-
-//    @Test
-//    public void shouldCreateFSRepositoryIfProtocolIsOfTypeFile() throws Exception {
-//        this.source.setName(validName);
-//        this.source.setSVNURL(url);
-//        this.connection = source.getConnection();
-//        assertThat(this.connection, is(notNullValue()));
-//    }
+    @Test
+    public void shouldCreateFSRepositoryIfProtocolIsOfTypeFile() throws Exception {
+        this.source.setName(validName);
+        this.source.setSVNURL(url);
+        this.connection = source.getConnection();
+        assertThat(this.connection, is(notNullValue()));
+    }
+    
+    @Test
+    public void shouldAllowMultipleConnectionsToBeOpenAtTheSameTime() throws Exception {
+        List<RepositoryConnection> connections = new ArrayList<RepositoryConnection>();
+        try {
+            for (int i = 0; i != 10; ++i) {
+                this.source.setName(validName);
+                this.source.setSVNURL(url);
+                RepositoryConnection conn = source.getConnection();
+                assertThat(conn, is(notNullValue()));
+                connections.add(conn);
+            }
+        } finally {
+            // Close all open connections ...
+            for (RepositoryConnection conn : connections) {
+                if (conn != null) {
+                    try {
+                        conn.close();
+                    } catch (Throwable t) {
+                        t.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
 }



From dna-commits at lists.jboss.org  Tue Mar  3 19:36:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 03 Mar 2009 19:36:43 -0500
Subject: [dna-commits] DNA SVN: r750 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property and 8
	other directories.
Message-ID: <E1Lef6J-0000O8-CY@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-03 19:36:43 -0500 (Tue, 03 Mar 2009)
New Revision: 750

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java
Removed:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeIterator.java
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/property/Path.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicPathSegment.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.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/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
   trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java
   trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/ProjectionPathRuleTest.java
   trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/RequestProcessorCacheTest.java
Log:
DNA-290 Getting child nodes or properties by pattern is not implemented

Implemented the AbstractJcrNode.getNodes(String pattern) and AbstractJcrNode.getProperties(String pattern), which makes a few more of the JCR TCK tests pass.  In the process, the way AbstractJcrNode stores children was (dramatically) simplified, and a few different kinds of NodeIterator and PropertyIterator implementations were added (including empty ones).

Also changed how DNA Path.Segment objects are created when no SNS index is specified, so the objects are now created with an index of 1 rather than -1.  This was done because it was leaking -1 in some cases, and it also more closely aligns with JCR.  Found and corrected minor related bugs in a few test cases.

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-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -474,7 +474,7 @@
             if (childrenWithSameNames.size() == 0) return;
             if (childrenWithSameNames.size() == 1) {
                 InMemoryNode childWithSameName = childrenWithSameNames.get(0);
-                Path.Segment newName = context.getValueFactories().getPathFactory().createSegment(name, Path.NO_INDEX);
+                Path.Segment newName = context.getValueFactories().getPathFactory().createSegment(name, Path.DEFAULT_INDEX);
                 childWithSameName.setName(newName);
                 return;
             }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -139,9 +139,9 @@
     public static final String SELF = ".";
 
     /**
-     * The index that will be returned for a {@link Segment} that {@link Segment#hasIndex() has no index}.
+     * The default index for a {@link Segment}.
      */
-    public static final int NO_INDEX = -1;
+    public static final int DEFAULT_INDEX = 1;
 
     /**
      * Representation of the segments that occur within a path.
@@ -159,7 +159,7 @@
         public Name getName();
 
         /**
-         * Get the index for this segment, which will be {@link Path#NO_INDEX -1} if this segment has no specific index.
+         * Get the index for this segment, which will be 1 by default.
          * 
          * @return the index
          */

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicPathSegment.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicPathSegment.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicPathSegment.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -48,7 +48,9 @@
      * @throws IllegalArgumentException if the name is null or if the index is invalid
      */
     public BasicPathSegment( Name name ) {
-        this(name, Path.NO_INDEX);
+        assert name != null;
+        this.name = name;
+        this.index = Path.DEFAULT_INDEX;
     }
 
     /**
@@ -59,9 +61,9 @@
     public BasicPathSegment( Name name,
                              int index ) {
         assert name != null;
-        assert index >= Path.NO_INDEX;
+        assert index >= Path.DEFAULT_INDEX;
         this.name = name;
-        this.index = (this.isSelfReference() || this.isParentReference()) ? Path.NO_INDEX : index;
+        this.index = (this.isSelfReference() || this.isParentReference()) ? Path.DEFAULT_INDEX : index;
     }
 
     /**
@@ -82,7 +84,7 @@
      * {@inheritDoc}
      */
     public boolean hasIndex() {
-        return this.index != Path.NO_INDEX;
+        return this.index != Path.DEFAULT_INDEX;
     }
 
     /**

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathSegmentTest.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -84,7 +84,7 @@
     @Test
     public void shouldNotHaveIndexByDefault() {
         segment = new BasicPathSegment(validName);
-        assertThat(segment.getIndex(), is(Path.NO_INDEX));
+        assertThat(segment.getIndex(), is(Path.DEFAULT_INDEX));
         assertThat(segment.hasIndex(), is(false));
         assertThat(segment.isSelfReference(), is(false));
         assertThat(segment.isParentReference(), is(false));
@@ -94,7 +94,7 @@
     @Test
     public void shouldCreateSelfReferenceSegmentIfPassedSelfReferenceStringRegardlessOfIndex() {
         segment = factory.createSegment(Path.SELF);
-        assertThat(segment.getIndex(), is(Path.NO_INDEX));
+        assertThat(segment.getIndex(), is(Path.DEFAULT_INDEX));
         assertThat(segment.hasIndex(), is(false));
         assertThat(segment.isSelfReference(), is(true));
         assertThat(segment.isParentReference(), is(false));
@@ -102,7 +102,7 @@
         assertThat(segment.getName().getNamespaceUri().length(), is(0));
 
         segment = factory.createSegment(Path.SELF, 1);
-        assertThat(segment.getIndex(), is(Path.NO_INDEX));
+        assertThat(segment.getIndex(), is(Path.DEFAULT_INDEX));
         assertThat(segment.hasIndex(), is(false));
         assertThat(segment.isSelfReference(), is(true));
         assertThat(segment.isParentReference(), is(false));
@@ -113,7 +113,7 @@
     @Test
     public void shouldCreateParentReferenceSegmentIfPassedParentReferenceStringRegardlessOfIndex() {
         segment = factory.createSegment(Path.PARENT);
-        assertThat(segment.getIndex(), is(Path.NO_INDEX));
+        assertThat(segment.getIndex(), is(Path.DEFAULT_INDEX));
         assertThat(segment.hasIndex(), is(false));
         assertThat(segment.isSelfReference(), is(false));
         assertThat(segment.isParentReference(), is(true));
@@ -121,7 +121,7 @@
         assertThat(segment.getName().getNamespaceUri().length(), is(0));
 
         segment = factory.createSegment(Path.PARENT, 1);
-        assertThat(segment.getIndex(), is(Path.NO_INDEX));
+        assertThat(segment.getIndex(), is(Path.DEFAULT_INDEX));
         assertThat(segment.hasIndex(), is(false));
         assertThat(segment.isSelfReference(), is(false));
         assertThat(segment.isParentReference(), is(true));
@@ -143,14 +143,14 @@
 
     @Test
     public void shouldConsiderSegmentWithSameNameSiblingIndexOfOneToBeEqualToSegmentWithSameNameButNoIndex() {
-        segment = new BasicPathSegment(validName, Path.NO_INDEX);
+        segment = new BasicPathSegment(validName, Path.DEFAULT_INDEX);
         Path.Segment segment2 = new BasicPathSegment(validName, 1);
         assertThat(segment, is(segment2));
     }
 
     @Test
     public void shouldConsiderSegmentWithSameNameSiblingIndexOfTwoOrMoreToNotBeEqualToSegmentWithSameNameButNoIndex() {
-        segment = new BasicPathSegment(validName, Path.NO_INDEX);
+        segment = new BasicPathSegment(validName, Path.DEFAULT_INDEX);
         Path.Segment segment2 = new BasicPathSegment(validName, 2);
         assertThat(segment, is(not(segment2)));
         segment2 = new BasicPathSegment(validName, 3);
@@ -161,7 +161,7 @@
     public void shouldUseDelimiterEncoderToEncodeDelimiterBetweenPrefixAndLocalPart() {
         TextEncoder encoder = new Jsr283Encoder();
         validName = new BasicName(DnaLexicon.Namespace.URI, "some:name:with:colons");
-        segment = new BasicPathSegment(validName, Path.NO_INDEX);
+        segment = new BasicPathSegment(validName, Path.DEFAULT_INDEX);
         TextEncoder delimiterEncoder = new TextEncoder() {
             public String encode( String text ) {
                 if (":".equals(text)) return "\\:";

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-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -27,9 +27,11 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
+import java.util.regex.Pattern;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
@@ -51,6 +53,9 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 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.ValueFormatException;
 import org.jboss.dna.graph.property.Path.Segment;
 
 /**
@@ -61,8 +66,7 @@
 
     private final JcrSession session;
     Set<Property> properties;
-    List<Name> children;
-    List<Integer> childNameCounts;
+    List<Path.Segment> children;
     private UUID uuid;
 
     AbstractJcrNode( JcrSession session ) {
@@ -213,7 +217,8 @@
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
     public NodeType[] getMixinNodeTypes() throws RepositoryException {
-        PropertyIterator mixinProperties = getProperties(JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext().getNamespaceRegistry()));
+        PropertyIterator mixinProperties = getProperties(JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext()
+                                                                                                 .getNamespaceRegistry()));
         List<NodeType> mixinNodeTypes = new ArrayList<NodeType>((int)mixinProperties.getSize());
 
         while (mixinProperties.hasNext()) {
@@ -236,7 +241,8 @@
      */
     public final Node getNode( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
-        Item item = getSession().getItem(getPath(getPath(), relativePath));
+        String path = getPath(getPath(), relativePath);
+        Item item = getSession().getItem(path);
         if (item instanceof Node) {
             return (Node)item;
         }
@@ -249,7 +255,10 @@
      * @see javax.jcr.Node#getNodes()
      */
     public final NodeIterator getNodes() {
-        return new JcrNodeIterator(this, children, childNameCounts);
+        if (children == null) {
+            return new JcrEmptyNodeIterator();
+        }
+        return new JcrChildNodeIterator(this, this.session.getExecutionContext().getNamespaceRegistry(), children);
     }
 
     /**
@@ -258,32 +267,35 @@
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getNodes(java.lang.String)
      */
-    public NodeIterator getNodes( String namePattern ) {
-        // TODO: Implement after changing impl to delegate to Graph API
-        throw new UnsupportedOperationException();
-        /*
-        CheckArg.isNotEmpty(namePattern, "namePattern");
-        String[] disjuncts = namePattern.split("\\|");
-        List<Segment> nodes = new ArrayList<Segment>();
-        for (String disjunct : disjuncts) {
-            String pattern = disjunct.trim();
-            CheckArg.isNotEmpty(pattern, "namePattern");
-            String ndxPattern;
-            int endNdx = pattern.length() - 1;
-            if (pattern.charAt(endNdx) == ']') {
-                int ndx = pattern.indexOf('[');
-                ndxPattern = pattern.substring(ndx + 1, endNdx);
-                pattern = pattern.substring(0, ndx);
-            } else ndxPattern = null;
-            for (Entry<Name, Integer> child : getChildCountsByName().entrySet()) {
-                if (child.getKey().getLocalName().matches(pattern)) {
-                    if (ndxPattern != null && !child.getValue().toString().matches(ndxPattern)) continue;
-                    if (child.getValue() > 1) nodes.add(new BasicPathSegment(child.getKey(), child.getValue()));
-                    else nodes.add(new BasicPathSegment(child.getKey()));
+    public NodeIterator getNodes( String namePattern ) throws RepositoryException {
+        CheckArg.isNotNull(namePattern, "namePattern");
+        namePattern = namePattern.trim();
+        if (namePattern.length() == 0) return new JcrEmptyNodeIterator();
+        if ("*".equals(namePattern)) return getNodes();
+        List<Object> patterns = createPatternsFor(namePattern);
+
+        // Implementing exact-matching only for now to prototype types as properties
+        List<Path.Segment> matchingChildren = new LinkedList<Path.Segment>();
+        NamespaceRegistry registry = session.executionContext.getNamespaceRegistry();
+        boolean foundMatch = false;
+        for (Path.Segment child : children) {
+            String childName = child.getName().getString(registry);
+            for (Object patternOrMatch : patterns) {
+                if (patternOrMatch instanceof Pattern) {
+                    Pattern pattern = (Pattern)patternOrMatch;
+                    if (pattern.matcher(childName).matches()) foundMatch = true;
+                } else {
+                    String match = (String)patternOrMatch;
+                    if (childName.equals(match)) foundMatch = true;
                 }
+                if (foundMatch) {
+                    foundMatch = false;
+                    matchingChildren.add(child);
+                    break;
+                }
             }
         }
-        */
+        return new JcrChildNodeIterator(this, this.session.executionContext.getNamespaceRegistry(), matchingChildren);
     }
 
     /**
@@ -334,18 +346,89 @@
      * @see javax.jcr.Node#getProperties(java.lang.String)
      */
     public PropertyIterator getProperties( String namePattern ) throws RepositoryException {
-        // TODO: Implement after changing impl to delegate to Graph API
+        CheckArg.isNotNull(namePattern, "namePattern");
+        namePattern = namePattern.trim();
+        if (namePattern.length() == 0) return new JcrEmptyPropertyIterator();
+        if ("*".equals(namePattern)) return new JcrPropertyIterator(properties);
+        List<Object> patterns = createPatternsFor(namePattern);
 
         // Implementing exact-matching only for now to prototype types as properties
         Set<Property> matchingProps = new HashSet<Property>();
-        for (Property prop : properties) {
-            String propName = prop.getName();
-            if (propName.equals(namePattern)) matchingProps.add(prop);
+        for (Object patternOrMatch : patterns) {
+            if (patternOrMatch instanceof Pattern) {
+                Pattern pattern = (Pattern)patternOrMatch;
+                for (Property prop : properties) {
+                    String propName = prop.getName();
+                    if (pattern.matcher(propName).matches()) matchingProps.add(prop);
+                }
+            } else {
+                String match = (String)patternOrMatch;
+                for (Property prop : properties) {
+                    String propName = prop.getName();
+                    if (propName.equals(match)) matchingProps.add(prop);
+                }
+            }
         }
-
         return new JcrPropertyIterator(matchingProps);
     }
 
+    protected static List<Object> createPatternsFor( String namePattern ) throws RepositoryException {
+        List<Object> patterns = new LinkedList<Object>();
+        for (String stringPattern : namePattern.split("[|]")) {
+            stringPattern = stringPattern.trim();
+            int length = stringPattern.length();
+            if (length == 0) continue;
+            if (stringPattern.indexOf("*") == -1) {
+                // Doesn't use wildcard, so use String not Pattern
+                patterns.add(stringPattern);
+            } else {
+                // We need to escape the regular expression characters ...
+                StringBuilder sb = new StringBuilder(length);
+                for (int i = 0; i != length; i++) {
+                    char c = stringPattern.charAt(i);
+                    switch (c) {
+                        // Per the spec, the the following characters are not allowed in patterns:
+                        case '/':
+                        case '[':
+                        case ']':
+                        case '\'':
+                        case '"':
+                        case '|':
+                        case '\t':
+                        case '\n':
+                        case '\r':
+                            String msg = JcrI18n.invalidNamePattern.text(c, namePattern);
+                            throw new RepositoryException(msg);
+                            // The following characters must be escaped when used in regular expressions ...
+                        case '?':
+                        case '(':
+                        case ')':
+                        case '$':
+                        case '^':
+                        case '.':
+                        case '{':
+                        case '}':
+                        case '\\':
+                            sb.append("\\");
+                            sb.append(c);
+                            break;
+                        case '*':
+                            // replace with the regular expression wildcard
+                            sb.append(".*");
+                            break;
+                        default:
+                            sb.append(c);
+                            break;
+                    }
+                }
+                String escapedString = sb.toString();
+                Pattern pattern = Pattern.compile(escapedString);
+                patterns.add(pattern);
+            }
+        }
+        return patterns;
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -433,19 +516,17 @@
         if (relativePath.indexOf('/') >= 0) {
             return (getNode(relativePath) != null);
         }
-        int ndxNdx = relativePath.indexOf('[');
-        String name = (ndxNdx < 0 ? relativePath : relativePath.substring(0, ndxNdx));
-        CheckArg.isNotEmpty(name, "relativePath");
-        int childNdx = 0;
         if (children != null) {
-            for (Name child : children) {
-                if (name.equals(child.getString(session.getExecutionContext().getNamespaceRegistry()))) {
-                    if (ndxNdx >= 0) {
-                        return (Integer.parseInt(relativePath.substring(ndxNdx + 1, relativePath.length() - 1)) <= childNameCounts.get(childNdx));
-                    }
-                    return true;
+            try {
+                Path.Segment segment = session.getExecutionContext()
+                                              .getValueFactories()
+                                              .getPathFactory()
+                                              .createSegment(relativePath);
+                for (Path.Segment child : children) {
+                    if (child.equals(segment)) return true;
                 }
-                childNdx++;
+            } catch (ValueFormatException e) {
+                throw new RepositoryException(JcrI18n.invalidRelativePath.text(relativePath));
             }
         }
         return false;
@@ -662,21 +743,7 @@
 
     final void setChildren( List<Segment> children ) {
         assert children != null;
-        if (this.children == null) {
-            this.children = new ArrayList<Name>(children.size());
-            childNameCounts = new ArrayList<Integer>(children.size());
-        }
-        for (Segment seg : children) {
-            Name name = seg.getName();
-            int ndx = this.children.indexOf(name);
-            if (ndx >= 0) {
-                childNameCounts.set(ndx, childNameCounts.get(ndx) + 1);
-            } else {
-                this.children.add(name);
-                childNameCounts.add(1);
-            }
-        }
-        assert this.children.size() == childNameCounts.size();
+        this.children = children;
     }
 
     final void setInternalUuid( UUID uuid ) {

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -0,0 +1,134 @@
+/*
+ * 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.util.Iterator;
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * @author jverhaeg
+ */
+ at Immutable
+final class JcrChildNodeIterator implements NodeIterator {
+
+    private final NamespaceRegistry registry;
+    private final Node parent;
+    private final Iterator<Path.Segment> iterator;
+    private int ndx;
+    private int size;
+
+    JcrChildNodeIterator( Node parent,
+                          NamespaceRegistry registry,
+                          List<Path.Segment> children ) {
+        assert parent != null;
+        assert registry != null;
+        assert children != null;
+        this.registry = registry;
+        this.parent = parent;
+        iterator = children.iterator();
+        size = children.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getPosition()
+     */
+    public long getPosition() {
+        return ndx;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getSize()
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return iterator.hasNext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public Object next() {
+        return nextNode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.NodeIterator#nextNode()
+     */
+    public Node nextNode() {
+        Path.Segment childSegment = iterator.next();
+        ndx++;
+        String childName = childSegment.getString(registry);
+        try {
+            return parent.getNode(childName);
+        } catch (RepositoryException error) {
+            throw new RuntimeException(error);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws IllegalArgumentException if <code>count</code> is negative.
+     * @see javax.jcr.RangeIterator#skip(long)
+     */
+    public void skip( long count ) {
+        CheckArg.isNonNegative(count, "count");
+        while (--count >= 0) {
+            nextNode();
+        }
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyNodeIterator.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -0,0 +1,101 @@
+/*
+ * 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.NoSuchElementException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * 
+ */
+public class JcrEmptyNodeIterator implements NodeIterator {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.NodeIterator#nextNode()
+     */
+    public Node nextNode() {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getPosition()
+     */
+    public long getPosition() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getSize()
+     */
+    public long getSize() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#skip(long)
+     */
+    public void skip( long skipNum ) {
+        CheckArg.isNonNegative(skipNum, "skipNum");
+        if (skipNum == 0L) return;
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public Object next() {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -0,0 +1,101 @@
+/*
+ * 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.NoSuchElementException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * 
+ */
+public class JcrEmptyPropertyIterator implements PropertyIterator {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.PropertyIterator#nextProperty()
+     */
+    public Property nextProperty() {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getPosition()
+     */
+    public long getPosition() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#getSize()
+     */
+    public long getSize() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.RangeIterator#skip(long)
+     */
+    public void skip( long skipNum ) {
+        CheckArg.isNonNegative(skipNum, "skipNum");
+        if (skipNum == 0L) return;
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public Object next() {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEmptyPropertyIterator.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-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -58,14 +58,16 @@
     public static I18n unableToRemapUriUsingPrefixUsedInNamespaceRegistry;
 
     public static I18n errorWhileInitializingTheNamespaceRegistry;
+    public static I18n invalidRelativePath;
     public static I18n invalidPathParameter;
+    public static I18n invalidNamePattern;
 
     public static I18n typeNotFound;
-    
+
     // Used in AbstractJcrNode#getAncestor
     public static I18n noNegativeDepth;
     public static I18n tooDeep;
-    
+
     public static I18n REP_NAME_DESC;
     public static I18n REP_VENDOR_DESC;
     public static I18n SPEC_NAME_DESC;

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeIterator.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeIterator.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -1,143 +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.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.property.Name;
-
-/**
- * @author jverhaeg
- */
-final class JcrNodeIterator implements NodeIterator {
-
-    private final Node parent;
-    private final Iterator<Name> childIterator;
-    private final Iterator<Integer> childNameCountIterator;
-    private transient Name child;
-    private transient int childNameCount;
-    private transient int childNdx = 1;
-    private transient int ndx;
-    private transient Node node;
-
-    JcrNodeIterator( Node parent,
-                     List<Name> children,
-                     List<Integer> childNameCounts ) {
-        assert parent != null;
-        this.parent = parent;
-        childIterator = (children == null ? null : children.iterator());
-        childNameCountIterator = (childNameCounts == null ? null : childNameCounts.iterator());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.RangeIterator#getPosition()
-     */
-    public long getPosition() {
-        return ndx;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @return -1L
-     * @see javax.jcr.RangeIterator#getSize()
-     */
-    public long getSize() {
-        return -1L;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.util.Iterator#hasNext()
-     */
-    public boolean hasNext() {
-        return ((childIterator != null && childIterator.hasNext()) || (child != null && childNdx <= childNameCount));
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.util.Iterator#next()
-     */
-    public Object next() {
-        return nextNode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.NodeIterator#nextNode()
-     */
-    public Node nextNode() {
-        if (childIterator == null) {
-            throw new NoSuchElementException();
-        }
-        if (child == null || childNdx > childNameCount) {
-            child = childIterator.next();
-            childNameCount = childNameCountIterator.next();
-            childNdx = 1;
-        }
-        try {
-            node = parent.getNode(child.getString(((JcrSession)parent.getSession()).getExecutionContext().getNamespaceRegistry())
-                                  + '[' + childNdx + ']');
-            childNdx++;
-            ndx++;
-            return node;
-        } catch (RepositoryException error) {
-            // TODO: Change to DnaException once DNA-180 is addressed
-            throw new RuntimeException(error);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see java.util.Iterator#remove()
-     */
-    public void remove() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws IllegalArgumentException if <code>count</code> is negative.
-     * @see javax.jcr.RangeIterator#skip(long)
-     */
-    public void skip( long count ) {
-        CheckArg.isNonNegative(count, "count");
-        while (--count >= 0) {
-            nextNode();
-        }
-    }
-}

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-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-04 00:36:43 UTC (rev 750)
@@ -48,7 +48,9 @@
 unableToRemapUriUsingPrefixUsedInNamespaceRegistry = Unable to remap the namespace "{1}" to prefix "{0}" because the prefix is already used as the prefix for the namespace "{2}" in the workspace's namespace registry
 
 errorWhileInitializingTheNamespaceRegistry = Error while initializing the namespace registry for workspace "{0}"
+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
 
 REP_NAME_DESC = DNA Repository
 REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC

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-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -46,9 +46,7 @@
 import javax.jcr.Workspace;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path.Segment;
-import org.jboss.dna.graph.property.basic.BasicName;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -66,10 +64,11 @@
                                             List<Segment> children,
                                             Node parent ) throws Exception {
         MockAbstractJcrNode child = new MockAbstractJcrNode(session, name, parent);
-        Segment seg = Mockito.mock(Segment.class);
-        stub(seg.getName()).toReturn(new BasicName(null, name));
+        Segment seg = session.getExecutionContext().getValueFactories().getPathFactory().createSegment(name, index);
         children.add(seg);
-        stub(session.getItem(parent.getPath() + "/" + name + '[' + index + ']')).toReturn(child);
+        // Stub the session to return this node ...
+        String absolutePath = parent.getPath() + "/" + seg.getString(session.getExecutionContext().getNamespaceRegistry());
+        stub(session.getItem(absolutePath)).toReturn(child);
         return child;
     }
 
@@ -117,9 +116,7 @@
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        NamespaceRegistry registry = Mockito.mock(NamespaceRegistry.class);
-        ExecutionContext context = Mockito.mock(ExecutionContext.class);
-        stub(context.getNamespaceRegistry()).toReturn(registry);
+        ExecutionContext context = new ExecutionContext();
         stub(session.getExecutionContext()).toReturn(context);
         children = new ArrayList<Segment>();
         properties = new HashSet<Property>();

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -32,12 +32,10 @@
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path.Segment;
 import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoAnnotations.Mock;
 
@@ -54,9 +52,7 @@
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        NamespaceRegistry registry = Mockito.mock(NamespaceRegistry.class);
-        ExecutionContext context = Mockito.mock(ExecutionContext.class);
-        stub(context.getNamespaceRegistry()).toReturn(registry);
+        ExecutionContext context = new ExecutionContext();
         stub(session.getExecutionContext()).toReturn(context);
         children = new ArrayList<Segment>();
         node = new MockAbstractJcrNode(session, "node", null);
@@ -73,7 +69,7 @@
         node.setChildren(children);
         NodeIterator iter = node.getNodes();
         assertThat(iter, notNullValue());
-        assertThat(iter.getSize(), is(-1L));
+        assertThat(iter.getSize(), is(6L));
         assertThat(iter.getPosition(), is(0L));
         assertThat(iter.hasNext(), is(true));
         assertThat((Node)iter.next(), is(child1));

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-03-03 22:02:41 UTC (rev 749)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/strategy/SimpleMergeStrategy.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -131,7 +131,7 @@
                 while (childIterator.hasNext()) {
                     Location child = childIterator.next();
                     Name childName = child.getPath().getLastSegment().getName();
-                    int index = Path.NO_INDEX;
+                    int index = Path.DEFAULT_INDEX;
                     Integer previous = childNames.put(childName, 1);
                     if (previous != null) {
                         int previousValue = previous.intValue();

Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/ProjectionPathRuleTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/ProjectionPathRuleTest.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/ProjectionPathRuleTest.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -172,8 +172,8 @@
         assertThat(rule.getPathInRepository(sourcePath, pathFactory), is(repositoryPath));
         assertThatGetPathInRepositoryReturnsCorrectPathInSource("");
         assertThatGetPathInRepositoryReturnsCorrectPathInSource("m/n");
-        assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[0]");
-        assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[0]/n/o/p");
+        assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]");
+        assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]/n/o/p");
     }
 
     protected void assertThatGetPathInRepositoryReturnsCorrectPathInSource( String subpath ) {
@@ -199,8 +199,8 @@
         assertThat(rule.getPathInSource(repositoryPath, pathFactory), is(sourcePath));
         assertThatGetPathInSourceReturnsCorrectPathInRepository("");
         assertThatGetPathInSourceReturnsCorrectPathInRepository("m/n");
-        assertThatGetPathInSourceReturnsCorrectPathInRepository("m[0]");
-        assertThatGetPathInSourceReturnsCorrectPathInRepository("m[0]/n/o/p");
+        assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]");
+        assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]/n/o/p");
 
     }
 

Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/RequestProcessorCacheTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/RequestProcessorCacheTest.java	2009-03-03 22:02:41 UTC (rev 749)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/RequestProcessorCacheTest.java	2009-03-04 00:36:43 UTC (rev 750)
@@ -171,7 +171,7 @@
         Location oldLocation = location2;
         Location newLocation = Location.create(pathFactory.create("/a/b/c/d3/e[1]"));
         assertThat(oldLocation.getPath().getString(namespaces), is("/a/b/c/e[2]"));
-        assertThat(newLocation.getPath().getString(namespaces), is("/a/b/c/d3/e[1]"));
+        assertThat(newLocation.getPath().getString(namespaces), is("/a/b/c/d3/e")); // no SNS index
         cache.addNewNode(workspaceId, location);
         cache.addNewNode(workspaceId, location2);
         for (Location loc : children)
@@ -296,7 +296,7 @@
         Location oldLocation = location2;
         Location newLocation = Location.create(pathFactory.create("/a/b/c/d3/e[1]"));
         assertThat(oldLocation.getPath().getString(namespaces), is("/a/b/c/e[2]"));
-        assertThat(newLocation.getPath().getString(namespaces), is("/a/b/c/d3/e[1]"));
+        assertThat(newLocation.getPath().getString(namespaces), is("/a/b/c/d3/e")); // no SNS index
         cache.addNewNode(workspaceId, location);
         cache.addNewNode(workspaceId, location2);
         for (Location loc : children)



From dna-commits at lists.jboss.org  Tue Mar  3 19:54:32 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 03 Mar 2009 19:54:32 -0500
Subject: [dna-commits] DNA SVN: r751 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LefNY-00052d-Rh@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-03 19:54:32 -0500 (Tue, 03 Mar 2009)
New Revision: 751

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/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/JackrabbitJcrTckTest.java
Log:
DNA-289 AbstractJcrItem.isSame Does Not Match JCR 1.0 Specification

Applied the patch.  One test had to be updated (minor issue), since it was updated for DNA-290 after the patch was uploaded.  Also added another JCR TCK unit test to the mix, since all of its test methods were passing.

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-03-04 00:36:43 UTC (rev 750)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-03-04 00:54:32 UTC (rev 751)
@@ -69,7 +69,26 @@
      * @see javax.jcr.Item#isSame(javax.jcr.Item)
      */
     public boolean isSame( Item otherItem ) throws RepositoryException {
-        return (getSession().getWorkspace() == otherItem.getSession().getWorkspace());
+        assert getSession() != null;
+        assert otherItem.getSession() != null;
+        assert getSession().getRepository() != null;
+        assert otherItem.getSession().getRepository() != null;
+
+        if (getSession().getRepository() != otherItem.getSession().getRepository()) {
+            return false;
+        }
+
+        assert getSession().getWorkspace() != null;
+        assert otherItem.getSession().getWorkspace() != null;
+
+        String workspaceName = getSession().getWorkspace().getName();
+        String otherWorkspaceName = otherItem.getSession().getWorkspace().getName();
+
+        if (workspaceName == null) {
+            return otherWorkspaceName == null;
+        }
+
+        return workspaceName.equals(otherWorkspaceName);
     }
 
     /**

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-03-04 00:36:43 UTC (rev 750)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-04 00:54:32 UTC (rev 751)
@@ -40,6 +40,7 @@
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
@@ -461,9 +462,14 @@
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getReferences()
      */
-    public final PropertyIterator getReferences() {
-        // TODO: Need to provide this at the DNA layer first (probably via a connector query)
-        throw new UnsupportedOperationException();
+    public final PropertyIterator getReferences() throws RepositoryException {
+        // Iterate through the properties to see which ones have a REFERENCE type ...
+        Set<Property> references = new HashSet<Property>();
+        for (Property property : properties) {
+            if (property.getType() == PropertyType.REFERENCE) references.add(property);
+        }
+        if (references.isEmpty()) return new JcrEmptyPropertyIterator();
+        return new JcrPropertyIterator(references);
     }
 
     /**

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-03-04 00:36:43 UTC (rev 750)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-04 00:54:32 UTC (rev 751)
@@ -40,6 +40,8 @@
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
@@ -110,6 +112,10 @@
     private AbstractJcrNode node;
     @Mock
     private JcrSession session;
+    @Mock
+    private Workspace workspace;
+    @Mock
+    private Repository repository;
     private List<Segment> children;
     private Set<Property> properties;
 
@@ -317,9 +323,11 @@
         node.getProperty("child/prop");
     }
 
-    @Test( expected = UnsupportedOperationException.class )
+    @Test
     public void shouldNotAllowGetReferences() throws Exception {
-        node.getReferences();
+        PropertyIterator iter = node.getReferences();
+        assertThat(iter, is(notNullValue()));
+        assertThat(iter.getSize(), is(0L));
     }
 
     @Test
@@ -465,9 +473,15 @@
     @Test
     public void shouldProvideIsSame() throws Exception {
         stub(session.getWorkspace()).toReturn(Mockito.mock(Workspace.class));
+        stub(session.getRepository()).toReturn(repository);
         JcrSession session2 = Mockito.mock(JcrSession.class);
+        JcrRepository repository2 = Mockito.mock(JcrRepository.class);
+
+        stub(session2.getRepository()).toReturn(repository2);
+        stub(session2.getWorkspace()).toReturn(workspace);
         Node node2 = new MockAbstractJcrNode(session2, node.getName(), node.getParent());
         assertThat(node.isSame(node2), is(false));
+
         Property prop = Mockito.mock(Property.class);
         stub(prop.getSession()).toReturn(session);
         assertThat(node.isSame(prop), is(false));

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-03-04 00:36:43 UTC (rev 750)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-04 00:54:32 UTC (rev 751)
@@ -33,8 +33,10 @@
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.Property;
+import javax.jcr.Repository;
 import javax.jcr.Session;
 import javax.jcr.Value;
+import javax.jcr.Workspace;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
@@ -53,6 +55,10 @@
 
     private AbstractJcrProperty prop;
     @Mock
+    private Workspace workspace;
+    @Mock
+    private Repository repository;
+    @Mock
     private Session session;
     @Mock
     private Node node;
@@ -66,6 +72,8 @@
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
+        stub(session.getWorkspace()).toReturn(workspace);
+        stub(session.getRepository()).toReturn(repository);
         stub(node.getSession()).toReturn(session);
         stub(executionContext.getNamespaceRegistry()).toReturn(namespaceRegistry);
         prop = new MockAbstractJcrProperty(node, executionContext, name);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 00:36:43 UTC (rev 750)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 00:54:32 UTC (rev 751)
@@ -73,7 +73,7 @@
             // See https://jira.jboss.org/jira/browse/DNA-285
 
             addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);



From dna-commits at lists.jboss.org  Wed Mar  4 14:08:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 04 Mar 2009 14:08:46 -0500
Subject: [dna-commits] DNA SVN: r752 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 2 other directories.
Message-ID: <E1LewSU-00066q-Sc@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-04 14:08:45 -0500 (Wed, 04 Mar 2009)
New Revision: 752

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java
Removed:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrPropertyDefinition.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractValueFactory.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.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/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.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/AbstractJcrPropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
Log:
DNA-291 JCR property objects do not have PropertyDefinition

Restructured how our Property implementations create Value objects, since the Value objects cannot be cached in the Property.  Section 6.2.7 of JSR-170 says that Value objects can become unusable after Value.getStream() is called, and the proper procedure in this case is for the client to get a new Value object.  Therefore Property objects have to be able to re-create new Value objects.

This change led me to look more into how JcrAbstractProperty, JcrProperty, and JcrMultiValueProperty are implemented.  I was able to simplify them quite a bit by having them hold onto the DNA Property instance, and to pass in the PropertyDefinition into the constructor.  Several other errors were found in the current code, including "Property.getNode()" does not return the parent node that owns the property (that's "Property.getParent()"), but should instead return the node pointed to by the reference property value.

The JcrSession.populateNode(...) method was also refactored to validate the DNA graph node using the primary node type and mixin types, and to find and associate the appropriate PropertyDefinition with each of the node's properties.

All of these changes had a fairly large impact on the unit tests for these classes, so these were fixed.  Note that any test methods that check for AssertionException is not a valid test (as the tests should be able be run without assertions enabled to verify the behavior is still correct).  

Also enabled several JCR TCK tests, since we're now passing all of their test methods.  Not too many remain for JCR Level 1 compliance!

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractValueFactory.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractValueFactory.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -112,6 +112,7 @@
         if (value instanceof Float) return create(((Float)value).floatValue());
         if (value instanceof Boolean) return create(((Boolean)value).booleanValue());
         if (value instanceof BigDecimal) return create((BigDecimal)value);
+        if (value instanceof DateTime) return create((DateTime)value);
         if (value instanceof Calendar) return create((Calendar)value);
         if (value instanceof Date) return create((Date)value);
         if (value instanceof Name) return create((Name)value);

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -24,24 +24,18 @@
 package org.jboss.dna.jcr;
 
 import java.io.InputStream;
-import java.io.Reader;
 import java.util.Calendar;
-import java.util.Date;
-import java.util.UUID;
 import javax.jcr.Item;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.Property;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.Value;
+import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.ValueFactories;
 
 /**
  * @author jverhaeg
@@ -51,17 +45,21 @@
 
     private final Node node;
     private final ExecutionContext executionContext;
-    private final Name name;
+    private final org.jboss.dna.graph.property.Property dnaProperty;
+    private final PropertyDefinition jcrPropertyDefinition;
 
     AbstractJcrProperty( Node node,
                          ExecutionContext executionContext,
-                         Name name ) {
+                         PropertyDefinition definition,
+                         org.jboss.dna.graph.property.Property dnaProperty ) {
         assert node != null;
         assert executionContext != null;
-        assert name != null;
+        assert dnaProperty != null;
+        assert definition != null;
         this.node = node;
         this.executionContext = executionContext;
-        this.name = name;
+        this.dnaProperty = dnaProperty;
+        this.jcrPropertyDefinition = definition;
     }
 
     /**
@@ -75,39 +73,47 @@
         visitor.visit(this);
     }
 
-    JcrValue<?> createValue( ValueFactories valueFactories,
-                             ValueInfo valueInfo,
-                             Object value ) {
-        return createValue(valueFactories, valueInfo.valueClass, valueInfo.propertyType, value);
+    JcrValue createValue( Object value ) {
+        return new JcrValue(executionContext.getValueFactories(), getType(), value);
     }
 
-    private <T> JcrValue<T> createValue( ValueFactories valueFactories,
-                                         Class<T> valueClass,
-                                         int propertyType,
-                                         Object value ) {
-        return new JcrValue<T>(valueFactories, propertyType, valueClass.cast(value));
-    }
-
     final ExecutionContext getExecutionContext() {
         return executionContext;
     }
 
+    final org.jboss.dna.graph.property.Property getDnaProperty() {
+        return dnaProperty;
+    }
+
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getNode()
+     * @see javax.jcr.Property#getType()
      */
-    public final Node getNode() {
-        return node;
+    public final int getType() {
+        return jcrPropertyDefinition.getRequiredType();
     }
 
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Property#getDefinition()
+     */
+    public final PropertyDefinition getDefinition() {
+        return jcrPropertyDefinition;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This method returns the string form of the {@link org.jboss.dna.graph.property.Property#getName()}, computed dynamically
+     * each time this method is called to ensure that the property namespace prefix is used.
+     * </p>
+     * 
      * @see javax.jcr.Item#getName()
      */
     public final String getName() {
-        return name.getString(executionContext.getNamespaceRegistry());
+        return dnaProperty.getName().getString(executionContext.getNamespaceRegistry());
     }
 
     /**
@@ -154,9 +160,12 @@
      */
     @Override
     public final boolean isSame( Item otherItem ) throws RepositoryException {
-        if (super.isSame(otherItem) && otherItem instanceof Property) {
-            Property otherProp = (Property)otherItem;
-            return (getName().equals(otherProp.getName()) && getNode().isSame(otherProp.getNode()));
+        if (otherItem instanceof Property) {
+            Property otherProperty = (Property)otherItem;
+            // The nodes that own the properties must be the same ...
+            if (!getParent().isSame(otherProperty.getParent())) return false;
+            // The properties must have the same name ...
+            return getName().equals(otherProperty.getName());
         }
         return false;
     }
@@ -260,56 +269,4 @@
     public final void setValue( Node value ) {
         throw new UnsupportedOperationException();
     }
-
-    final class ValueInfo {
-
-        final Class<?> valueClass;
-        final int propertyType;
-
-        ValueInfo( Object value ) {
-            if (value instanceof Boolean) {
-                valueClass = Boolean.class;
-                propertyType = PropertyType.BOOLEAN;
-            } else if (value instanceof Date) {
-                valueClass = Date.class;
-                propertyType = PropertyType.DATE;
-            } else if (value instanceof Calendar) {
-                valueClass = Calendar.class;
-                propertyType = PropertyType.DATE;
-            } else if (value instanceof Double) {
-                valueClass = Double.class;
-                propertyType = PropertyType.DOUBLE;
-            } else if (value instanceof Float) {
-                valueClass = Float.class;
-                propertyType = PropertyType.DOUBLE;
-            } else if (value instanceof Integer) {
-                valueClass = Integer.class;
-                propertyType = PropertyType.LONG;
-            } else if (value instanceof Long) {
-                valueClass = Long.class;
-                propertyType = PropertyType.LONG;
-            } else if (value instanceof UUID) {
-                valueClass = UUID.class;
-                propertyType = PropertyType.REFERENCE;
-            } else if (value instanceof String) {
-                valueClass = String.class;
-                propertyType = PropertyType.STRING;
-            } else if (value instanceof Name) {
-                valueClass = Name.class;
-                propertyType = PropertyType.NAME;
-            } else if (value instanceof Path) {
-                valueClass = Path.class;
-                propertyType = PropertyType.PATH;
-            } else if (value instanceof InputStream) {
-                valueClass = InputStream.class;
-                propertyType = PropertyType.BINARY;
-            } else if (value instanceof Reader) {
-                valueClass = Reader.class;
-                propertyType = PropertyType.BINARY;
-            } else {
-                valueClass = Object.class;
-                propertyType = PropertyType.BINARY;
-            }
-        }
-    }
 }

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrPropertyDefinition.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrPropertyDefinition.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -1,124 +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 javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.PropertyDefinition;
-
-/**
- * @author jverhaeg
- */
-abstract class AbstractJcrPropertyDefinition implements PropertyDefinition {
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues()
-     */
-    public final Value[] getDefaultValues() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.PropertyDefinition#getRequiredType()
-     */
-    public final int getRequiredType() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.PropertyDefinition#getValueConstraints()
-     */
-    public final String[] getValueConstraints() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#getDeclaringNodeType()
-     */
-    public final NodeType getDeclaringNodeType() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#getName()
-     */
-    public final String getName() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#getOnParentVersion()
-     */
-    public final int getOnParentVersion() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#isAutoCreated()
-     */
-    public final boolean isAutoCreated() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#isMandatory()
-     */
-    public final boolean isMandatory() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.nodetype.ItemDefinition#isProtected()
-     */
-    public final boolean isProtected() {
-        throw new UnsupportedOperationException();
-    }
-}

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -69,10 +69,8 @@
     JcrBuiltinNodeTypeSource( JcrSession session ) {
         primaryNodeTypes = new ArrayList<JcrNodeType>();
 
-        Value trueValue = new JcrValue<Boolean>(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN,
-                                                Boolean.TRUE);
-        Value ntBaseValue = new JcrValue<Name>(session.getExecutionContext().getValueFactories(), PropertyType.NAME,
-                                               JcrNtLexicon.BASE);
+        Value trueValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN, Boolean.TRUE);
+        Value ntBaseValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.NAME, JcrNtLexicon.BASE);
 
         // Stubbing in child node and property definitions for now
         JcrNodeType base = new JcrNodeType(session, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
@@ -429,7 +427,7 @@
                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.DATE,
                                                                              NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.MIME_TYPE,
+                                                   new JcrPropertyDefinition(session, null, JcrLexicon.MIMETYPE,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.STRING,
                                                                              NO_CONSTRAINTS, false),}), NOT_MIXIN,

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -61,6 +61,8 @@
     public static I18n invalidRelativePath;
     public static I18n invalidPathParameter;
     public static I18n invalidNamePattern;
+    public static I18n itemNotFoundWithUuid;
+    public static I18n errorWhileFindingNodeWithUuid;
 
     public static I18n typeNotFound;
 

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -53,7 +53,6 @@
     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 MIME_TYPE = new BasicName(Namespace.URI, "mimeType");
     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_TYPE_NAME = new BasicName(Namespace.URI, "nodeTypeName");

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -24,10 +24,8 @@
 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.RepositoryException;
 import javax.jcr.Value;
@@ -35,8 +33,7 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.Property;
 
 /**
  * @author jverhaeg
@@ -44,22 +41,11 @@
 @NotThreadSafe
 final class JcrMultiValueProperty extends AbstractJcrProperty {
 
-    private List<JcrValue<?>> jcrValues = new ArrayList<JcrValue<?>>();
-
     JcrMultiValueProperty( Node node,
                            ExecutionContext executionContext,
-                           Name name,
-                           Iterable<?> values ) {
-        super(node, executionContext, name);
-        assert values != null;
-        ValueFactories valueFactories = executionContext.getValueFactories();
-        ValueInfo valueInfo = null;
-        for (Object value : values) {
-            if (valueInfo == null) {
-                valueInfo = new ValueInfo(value);
-            }
-            jcrValues.add(createValue(valueFactories, valueInfo, value));
-        }
+                           PropertyDefinition definition,
+                           Property dnaProperty ) {
+        super(node, executionContext, definition, dnaProperty);
     }
 
     /**
@@ -85,24 +71,19 @@
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getDefinition()
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#getDouble()
      */
-    public PropertyDefinition getDefinition() {
-        return new AbstractJcrPropertyDefinition() {
-
-            public boolean isMultiple() {
-                return true;
-            }
-        };
+    public double getDouble() throws ValueFormatException {
+        throw new ValueFormatException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws ValueFormatException always
-     * @see javax.jcr.Property#getDouble()
+     * @see javax.jcr.Property#getNode()
      */
-    public double getDouble() throws ValueFormatException {
+    public Node getNode() throws ValueFormatException, RepositoryException {
         throw new ValueFormatException();
     }
 
@@ -122,10 +103,11 @@
      * @see javax.jcr.Property#getLengths()
      */
     public long[] getLengths() throws RepositoryException {
-        long[] lengths = new long[jcrValues.size()];
-        Iterator<JcrValue<?>> iter = jcrValues.iterator();
+        Property dnaProperty = getDnaProperty();
+        long[] lengths = new long[dnaProperty.size()];
+        Iterator<?> iter = dnaProperty.iterator();
         for (int ndx = 0; iter.hasNext(); ndx++) {
-            lengths[ndx] = iter.next().getLength();
+            lengths[ndx] = createValue(iter.next()).getLength();
         }
         return lengths;
     }
@@ -163,15 +145,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getType()
-     */
-    public int getType() {
-        return jcrValues.get(0).getType();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws ValueFormatException always
      * @see javax.jcr.Property#getValue()
      */
@@ -181,10 +154,21 @@
 
     /**
      * {@inheritDoc}
+     * <p>
+     * Per the JCR specification, these values need to be created each time this method is called, since the Value cannot be used
+     * after {@link Value#getStream()} is called/processed. The spec says that the client simply needs to obtain a new Value (or
+     * {@link #getValues()} for {@link JcrMultiValueProperty multi-valued properites}).
+     * </p>
      * 
      * @see javax.jcr.Property#getValues()
      */
     public Value[] getValues() {
-        return jcrValues.toArray(new Value[jcrValues.size()]);
+        Property dnaProperty = getDnaProperty();
+        Value[] values = new JcrValue[dnaProperty.size()];
+        Iterator<?> iter = dnaProperty.iterator();
+        for (int ndx = 0; iter.hasNext(); ndx++) {
+            values[ndx] = createValue(iter.next());
+        }
+        return values;
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -25,13 +25,16 @@
 
 import java.io.InputStream;
 import java.util.Calendar;
+import java.util.UUID;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFactories;
 
 /**
@@ -39,16 +42,11 @@
  */
 final class JcrProperty extends AbstractJcrProperty {
 
-    private JcrValue<?> jcrValue;
-
     JcrProperty( Node node,
                  ExecutionContext executionContext,
-                 Name name,
-                 Object value ) {
-        super(node, executionContext, name);
-        assert value != null;
-        ValueFactories valueFactories = executionContext.getValueFactories();
-        jcrValue = createValue(valueFactories, new ValueInfo(value), value);
+                 PropertyDefinition definition,
+                 Property dnaProperty ) {
+        super(node, executionContext, definition, dnaProperty);
     }
 
     /**
@@ -57,7 +55,11 @@
      * @see javax.jcr.Property#getBoolean()
      */
     public boolean getBoolean() throws RepositoryException {
-        return jcrValue.getBoolean();
+        try {
+            return getExecutionContext().getValueFactories().getBooleanFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
@@ -66,30 +68,27 @@
      * @see javax.jcr.Property#getDate()
      */
     public Calendar getDate() throws RepositoryException {
-        return jcrValue.getDate();
+        try {
+            return getExecutionContext().getValueFactories()
+                                        .getDateFactory()
+                                        .create(getDnaProperty().getFirstValue())
+                                        .toCalendar();
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getDefinition()
-     */
-    public PropertyDefinition getDefinition() {
-        return new AbstractJcrPropertyDefinition() {
-
-            public boolean isMultiple() {
-                return false;
-            }
-        };
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @see javax.jcr.Property#getDouble()
      */
     public double getDouble() throws RepositoryException {
-        return jcrValue.getDouble();
+        try {
+            return getExecutionContext().getValueFactories().getDoubleFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
@@ -98,7 +97,7 @@
      * @see javax.jcr.Property#getLength()
      */
     public long getLength() throws RepositoryException {
-        return jcrValue.getLength();
+        return createValue(getDnaProperty().getFirstValue()).getLength();
     }
 
     /**
@@ -117,34 +116,54 @@
      * @see javax.jcr.Property#getLong()
      */
     public long getLong() throws RepositoryException {
-        return jcrValue.getLong();
+        try {
+            return getExecutionContext().getValueFactories().getLongFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getStream()
+     * @see javax.jcr.Property#getNode()
      */
-    public InputStream getStream() throws RepositoryException {
-        return jcrValue.getStream();
+    public final Node getNode() throws RepositoryException {
+        try {
+            ValueFactories factories = getExecutionContext().getValueFactories();
+            Reference dnaReference = factories.getReferenceFactory().create(getDnaProperty().getFirstValue());
+            UUID uuid = factories.getUuidFactory().create(dnaReference);
+            return ((JcrSession)getSession()).getNode(uuid);
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getString()
+     * @see javax.jcr.Property#getStream()
      */
-    public String getString() throws RepositoryException {
-        return jcrValue.getString();
+    public InputStream getStream() throws RepositoryException {
+        try {
+            Binary binary = getExecutionContext().getValueFactories().getBinaryFactory().create(getDnaProperty().getFirstValue());
+            return new SelfClosingInputStream(binary);
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Property#getType()
+     * @see javax.jcr.Property#getString()
      */
-    public int getType() {
-        return jcrValue.getType();
+    public String getString() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories().getStringFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
     }
 
     /**
@@ -153,7 +172,7 @@
      * @see javax.jcr.Property#getValue()
      */
     public Value getValue() {
-        return jcrValue;
+        return createValue(getDnaProperty().getFirstValue());
     }
 
     /**

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -29,12 +29,14 @@
 import java.security.Principal;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import javax.jcr.Credentials;
 import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
 import javax.jcr.NamespaceException;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
@@ -47,6 +49,9 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
@@ -56,10 +61,11 @@
 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.NameFactory;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.UuidFactory;
 import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.xml.sax.ContentHandler;
@@ -110,6 +116,7 @@
     private final ReferenceMap<String, Node> nodesByJcrUuid;
     private boolean isLive;
     private JcrRootNode rootNode;
+    private PropertyDefinition anyMultiplePropertyDefinition;
 
     JcrSession( JcrRepository repository,
                 JcrWorkspace workspace,
@@ -401,12 +408,21 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#getNodeByUUID(java.lang.String)
      */
-    public Node getNodeByUUID( String uuid ) {
-        // TODO: Need DNA command to get node by UUID before implementing
-        throw new UnsupportedOperationException();
+    public Node getNodeByUUID( String uuid ) throws ItemNotFoundException, RepositoryException {
+        Node result = null;
+        try {
+            result = nodesByUuid.get(UUID.fromString(uuid));
+        } catch (IllegalArgumentException e) {
+            throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
+        } catch (Throwable e) {
+            throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithUuid.text(uuid, workspace.getName()));
+        }
+        if (result == null) {
+            throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
+        }
+        return result;
     }
 
     /**
@@ -414,7 +430,7 @@
      * 
      * @see javax.jcr.Session#getRootNode()
      */
-    public Node getRootNode() {
+    public Node getRootNode() throws RepositoryException {
         // Return cached root node if available
         if (rootNode != null) {
             return rootNode;
@@ -423,14 +439,16 @@
         assert executionContext.getValueFactories() != null;
         assert executionContext.getValueFactories().getPathFactory() != null;
         rootNode = new JcrRootNode(this);
+
         // Get root node from source
-        populateNode(rootNode, graph.getNodeAt(executionContext.getValueFactories().getPathFactory().createRootPath()));
-
-        // Root nodes need to have a type in JCR land
-        JcrProperty primaryType = new JcrProperty(rootNode, executionContext, JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.BASE);
-        // TODO: Not liking the hard-code
-        rootNode.properties.add(primaryType);
-
+        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
+        org.jboss.dna.graph.Node dnaRootNode = graph.getNodeAt(rootPath);
+        if (dnaRootNode.getProperty(JcrLexicon.PRIMARY_TYPE) == null) {
+            // Add the primary type and update the source ...
+            graph.set(JcrLexicon.PRIMARY_TYPE).to(JcrNtLexicon.BASE).on(rootPath);
+            dnaRootNode = graph.getNodeAt(rootPath);
+        }
+        populateNode(rootNode, dnaRootNode);
         return rootNode;
     }
 
@@ -458,35 +476,35 @@
 
             public Value createValue( String value,
                                       int propertyType ) {
-                return new JcrValue<String>(valueFactories, propertyType, value);
+                return new JcrValue(valueFactories, propertyType, value);
             }
 
             public Value createValue( Node value ) throws RepositoryException {
-                return new JcrValue<UUID>(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
+                return new JcrValue(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
             }
 
             public Value createValue( InputStream value ) {
-                return new JcrValue<InputStream>(valueFactories, PropertyType.BINARY, value);
+                return new JcrValue(valueFactories, PropertyType.BINARY, value);
             }
 
             public Value createValue( Calendar value ) {
-                return new JcrValue<Calendar>(valueFactories, PropertyType.DATE, value);
+                return new JcrValue(valueFactories, PropertyType.DATE, value);
             }
 
             public Value createValue( boolean value ) {
-                return new JcrValue<Boolean>(valueFactories, PropertyType.BOOLEAN, value);
+                return new JcrValue(valueFactories, PropertyType.BOOLEAN, value);
             }
 
             public Value createValue( double value ) {
-                return new JcrValue<Double>(valueFactories, PropertyType.DOUBLE, value);
+                return new JcrValue(valueFactories, PropertyType.DOUBLE, value);
             }
 
             public Value createValue( long value ) {
-                return new JcrValue<Long>(valueFactories, PropertyType.LONG, value);
+                return new JcrValue(valueFactories, PropertyType.LONG, value);
             }
 
             public Value createValue( String value ) {
-                return new JcrValue<String>(valueFactories, PropertyType.STRING, value);
+                return new JcrValue(valueFactories, PropertyType.STRING, value);
             }
         };
     }
@@ -580,47 +598,184 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * Compute the JCR {@link PropertyType} for the given DNA {@link org.jboss.dna.graph.property.PropertyType}.
+     * 
+     * @param dnaPropertyType the DNA property type; never null
+     * @return the JCR property type
+     */
+    static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.PropertyType dnaPropertyType ) {
+        // Get the DNA property type for this ...
+        switch (dnaPropertyType) {
+            case STRING:
+                return PropertyType.STRING;
+            case NAME:
+                return PropertyType.NAME;
+            case LONG:
+                return PropertyType.LONG;
+            case UUID:
+                return PropertyType.STRING; // JCR treats UUID properties as strings
+            case URI:
+                return PropertyType.STRING;
+            case PATH:
+                return PropertyType.PATH;
+            case BOOLEAN:
+                return PropertyType.BOOLEAN;
+            case DATE:
+                return PropertyType.DATE;
+            case DECIMAL:
+                return PropertyType.UNDEFINED;
+            case DOUBLE:
+                return PropertyType.DOUBLE;
+            case BINARY:
+                return PropertyType.BINARY;
+            case OBJECT:
+                return PropertyType.UNDEFINED;
+            case REFERENCE:
+                return PropertyType.REFERENCE;
+        }
+        assert false;
+        return PropertyType.UNDEFINED;
+    }
+
     private void populateNode( AbstractJcrNode node,
-                               org.jboss.dna.graph.Node graphNode ) {
-        // TODO: What do we do to validate node against its primary type?
+                               org.jboss.dna.graph.Node graphNode ) throws RepositoryException {
         assert node != null;
         assert graphNode != null;
+
+        // --------------------------------------------------
         // Create JCR children for corresponding DNA children
+        // --------------------------------------------------
         node.setChildren(graphNode.getChildrenSegments());
+
+        // ------------------------------------------------------
         // Create JCR properties for corresponding DNA properties
+        // ------------------------------------------------------
+        // First get the property type for each property, based upon the primary type and mixins ...
+        Map<Name, PropertyDefinition> propertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+        boolean referenceable = false;
+
+        NamespaceRegistry registry = executionContext.getNamespaceRegistry();
+        ValueFactories factories = executionContext.getValueFactories();
+        NameFactory nameFactory = factories.getNameFactory();
+        NodeTypeManager nodeTypeManager = getWorkspace().getNodeTypeManager();
+        org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+        if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
+            Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
+            String primaryTypeNameString = primaryTypeName.getString(registry);
+            NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
+            for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+                Name name = nameFactory.create(propertyDefn.getName());
+                propertyDefinitionsByPropertyName.put(name, propertyDefn);
+            }
+        }
+        org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
+        if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
+            for (Object mixinTypeValue : mixinTypesProperty) {
+                Name mixinTypeName = nameFactory.create(mixinTypeValue);
+                if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
+                String mixinTypeNameString = mixinTypeName.getString(registry);
+                NodeType primaryType = nodeTypeManager.getNodeType(mixinTypeNameString);
+                for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+                    Name name = nameFactory.create(propertyDefn.getName());
+                    propertyDefinitionsByPropertyName.put(name, propertyDefn);
+                }
+            }
+        }
+
+        // Look for the UUID property ...
+        UUID uuid = null;
+        org.jboss.dna.graph.property.Property uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
+        Name jcrUuidPropertyName = null;
+        Name dnaUuidPropertyName = null;
+        if (uuidProperty != null) {
+            jcrUuidPropertyName = uuidProperty.getName();
+            // Grab the first 'good' UUID value ...
+            for (Object uuidValue : uuidProperty) {
+                try {
+                    uuid = factories.getUuidFactory().create(uuidValue);
+                    break;
+                } catch (ValueFormatException e) {
+                    // Ignore; just continue with the next property value
+                }
+            }
+        }
+        if (uuid == null) {
+            // Look for the DNA UUID property ...
+            org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
+            if (dnaUuidProperty != null) {
+                dnaUuidPropertyName = dnaUuidProperty.getName();
+                // Grab the first 'good' UUID value ...
+                for (Object uuidValue : dnaUuidProperty) {
+                    try {
+                        uuid = factories.getUuidFactory().create(uuidValue);
+                        break;
+                    } catch (ValueFormatException e) {
+                        // Ignore; just continue with the next property value
+                    }
+                }
+            }
+        }
+
+        // Now create the JCR property object wrapper around the "jcr:uuid" property ...
         Set<Property> properties = new HashSet<Property>();
-        UUID uuid = null;
-        Name jcrUuidName = executionContext.getValueFactories().getNameFactory().create("jcr:uuid");
-        Name jcrMixinTypesName = executionContext.getValueFactories().getNameFactory().create("jcr:mixinTypes");
-        UuidFactory uuidFactory = executionContext.getValueFactories().getUuidFactory();
-        org.jboss.dna.graph.property.Property dnaUuidProp = null;
-        boolean referenceable = false;
+        if (uuid == null) uuid = UUID.randomUUID();
+        if (referenceable) {
+            if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
+            PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
+            properties.add(new JcrProperty(node, executionContext, propertyDefinition, uuidProperty));
+        }
+
+        // Now create the JCR property object wrappers around the other properties ...
         for (org.jboss.dna.graph.property.Property dnaProp : graphNode.getProperties()) {
             Name name = dnaProp.getName();
-            if (dnaProp.isMultiple()) properties.add(new JcrMultiValueProperty(node, executionContext, name, dnaProp));
-            else {
-                if (uuid == null && DnaLexicon.UUID.equals(name)) uuid = uuidFactory.create(dnaProp.getValues()).next();
-                else if (jcrUuidName.equals(name)) dnaUuidProp = dnaProp;
-                else if (jcrMixinTypesName.equals(name)) {
-                    org.jboss.dna.graph.property.ValueFactory<String> stringFactory = executionContext.getValueFactories()
-                                                                                                      .getStringFactory();
-                    for (String mixin : stringFactory.create(dnaProp)) {
-                        if ("mix:referenceable".equals(mixin)) referenceable = true;
+
+            // Skip the JCR and DNA UUID properties (using the EXACT Name instances on the Property) ...
+            if (name == jcrUuidPropertyName || name == dnaUuidPropertyName) continue;
+
+            // Figure out the JCR property type for this property ...
+            PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(name);
+
+            // If no property type was specified, then use "nt:unstructured" property definitions for any property
+            if (propertyDefinition == null) {
+                if (anyMultiplePropertyDefinition == null) {
+                    String unstructuredName = JcrNtLexicon.UNSTRUCTURED.getString(registry);
+                    NodeType unstructured = nodeTypeManager.getNodeType(unstructuredName);
+                    for (PropertyDefinition definition : unstructured.getDeclaredPropertyDefinitions()) {
+                        if (definition.isMultiple()) {
+                            anyMultiplePropertyDefinition = definition;
+                        }
                     }
                 }
-                properties.add(new JcrProperty(node, executionContext, name, dnaProp.getValues().next()));
+                assert anyMultiplePropertyDefinition != null;
+                propertyDefinition = anyMultiplePropertyDefinition;
             }
+            assert propertyDefinition != null;
 
+            // Figure out if this is a multi-valued property ...
+            boolean isMultiple = propertyDefinition.isMultiple();
+            if (!isMultiple && dnaProp.isEmpty()) {
+                // Only multi-valued properties can have no values; so if not multi-valued, then skip ...
+                continue;
+            }
+
+            // Create the appropriate JCR property wrapper ...
+            if (isMultiple) {
+                properties.add(new JcrMultiValueProperty(node, executionContext, propertyDefinition, dnaProp));
+            } else {
+                properties.add(new JcrProperty(node, executionContext, propertyDefinition, dnaProp));
+            }
         }
+
+        if (referenceable) {
+            assert uuidProperty != null;
+        }
+
+        // Now set the properties on the node ...
         node.setProperties(properties);
+
         // Set node's UUID, creating one if necessary
-        if (uuid == null) {
-            if (dnaUuidProp == null || !referenceable) uuid = UUID.randomUUID();
-            else {
-                uuid = uuidFactory.create(dnaUuidProp.getValues()).next();
-                nodesByJcrUuid.put(uuid.toString(), node);
-            }
-        }
+        nodesByJcrUuid.put(uuid.toString(), node);
         node.setInternalUuid(uuid);
         // Setup node to be retrieved by DNA UUID
         nodesByUuid.put(uuid, node);

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -30,26 +30,27 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.ValueFactories;
 
 /**
- * @param <T> the type of value to create.
  * @author jverhaeg
  */
 @NotThreadSafe
-final class JcrValue<T> implements Value {
+final class JcrValue implements Value {
 
     private final ValueFactories valueFactories;
     private final int type;
-    private final T value;
+    private final Object value;
 
     JcrValue( ValueFactories valueFactories,
               int type,
-              T value ) {
+              Object value ) {
         assert valueFactories != null;
         assert type == PropertyType.BINARY || type == PropertyType.BOOLEAN || type == PropertyType.DATE
                || type == PropertyType.DOUBLE || type == PropertyType.LONG || type == PropertyType.NAME
-               || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING;
+               || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING
+               || type == PropertyType.UNDEFINED;
         assert value != null;
         this.valueFactories = valueFactories;
         this.type = type;
@@ -143,7 +144,8 @@
             throw new IllegalStateException(JcrI18n.nonInputStreamConsumed.text());
         }
         try {
-            InputStream convertedValue = valueFactories.getBinaryFactory().create(value).getStream();
+            Binary binary = valueFactories.getBinaryFactory().create(value);
+            InputStream convertedValue = new SelfClosingInputStream(binary);
             state = State.INPUT_STREAM_CONSUMED;
             return convertedValue;
         } catch (RuntimeException error) {

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SelfClosingInputStream.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -0,0 +1,245 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.property.Binary;
+
+/**
+ * An {@link InputStream} implementation that can be used to access the content of a supplied {@link Binary} value. An instance of
+ * this class immediately {@link Binary#acquire() acquires} the binary's lock, and allows the InputStream to be processed and used
+ * normally. This class, however, guarantees that the binary lock is {@link Binary#release() released} whenever this class throws
+ * an exception or when the instance is {@link #close() closed}.
+ * <p>
+ * The draft version of the JSR-283 specification outlines a new mechanism for obtaining a lock on a binary value, and in fact
+ * this mechanism was used as the baseline for the design of DNA's Binary value. Therefore, when DNA's JCR implementation supports
+ * JCR-283, this class will probably no longer be needed.
+ * </p>
+ */
+ at NotThreadSafe
+public class SelfClosingInputStream extends InputStream {
+
+    private final Binary binary;
+    private final InputStream stream;
+
+    /**
+     * Create a self-closing {@link InputStream} to access the content of the supplied {@link Binary} value. This construct
+     * immediately {@link Binary#acquire() acquires} the binary's lock, which is {@link Binary#release() released} whenever this
+     * class throws an exception or when the instance is {@link #close() closed}.
+     * 
+     * @param binary the {@link Binary} object that this stream accesses; may not be null
+     */
+    public SelfClosingInputStream( Binary binary ) {
+        assert binary != null;
+        this.binary = binary;
+        this.binary.acquire();
+        this.stream = binary.getStream();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#available()
+     */
+    @Override
+    public int available() throws IOException {
+        try {
+            return stream.available();
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#close()
+     */
+    @Override
+    public void close() throws IOException {
+        try {
+            stream.close();
+        } finally {
+            this.binary.release();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        return stream.equals(obj);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return stream.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#mark(int)
+     */
+    @Override
+    public void mark( int readlimit ) {
+        try {
+            stream.mark(readlimit);
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#markSupported()
+     */
+    @Override
+    public boolean markSupported() {
+        return stream.markSupported();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    @Override
+    public int read( byte[] b,
+                     int off,
+                     int len ) throws IOException {
+        try {
+            return stream.read(b, off, len);
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#read(byte[])
+     */
+    @Override
+    public int read( byte[] b ) throws IOException {
+        try {
+            return stream.read(b);
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#read()
+     */
+    @Override
+    public int read() throws IOException {
+        try {
+            return stream.read();
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#reset()
+     */
+    @Override
+    public void reset() throws IOException {
+        try {
+            stream.reset();
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.io.InputStream#skip(long)
+     */
+    @Override
+    public long skip( long n ) throws IOException {
+        try {
+            return stream.skip(n);
+        } catch (IOException e) {
+            this.binary.release();
+            throw e;
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        try {
+            return stream.toString();
+        } catch (RuntimeException e) {
+            this.binary.release();
+            throw e;
+        }
+    }
+}


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

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-04 19:08:45 UTC (rev 752)
@@ -51,6 +51,8 @@
 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
+itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}"
+errorWhileFindingNodeWithUuid = Error while finding the item with UUID "{0}" in workspace "{1}"
 
 REP_NAME_DESC = DNA Repository
 REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC

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-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -33,6 +33,7 @@
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.Property;
+import javax.jcr.PropertyType;
 import javax.jcr.Repository;
 import javax.jcr.Session;
 import javax.jcr.Value;
@@ -40,8 +41,6 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -63,20 +62,20 @@
     @Mock
     private Node node;
     @Mock
-    private NamespaceRegistry namespaceRegistry;
-    @Mock
+    private PropertyDefinition propertyDefinition;
     private ExecutionContext executionContext;
-    @Mock
-    private Name name;
+    private org.jboss.dna.graph.property.Property dnaProperty;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
+        executionContext = new ExecutionContext();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
+        stub(propertyDefinition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(session.getWorkspace()).toReturn(workspace);
         stub(session.getRepository()).toReturn(repository);
         stub(node.getSession()).toReturn(session);
-        stub(executionContext.getNamespaceRegistry()).toReturn(namespaceRegistry);
-        prop = new MockAbstractJcrProperty(node, executionContext, name);
+        prop = new MockAbstractJcrProperty(node, executionContext, propertyDefinition, dnaProperty);
     }
 
     @Test
@@ -91,21 +90,6 @@
         prop.accept(null);
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoSession() throws Exception {
-        new MockAbstractJcrProperty(null, executionContext, name);
-    }
-
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoExecutionContext() throws Exception {
-        new MockAbstractJcrProperty(node, null, name);
-    }
-
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoName() throws Exception {
-        new MockAbstractJcrProperty(node, executionContext, null);
-    }
-
     @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowNegativeAncestorDepth() throws Exception {
         stub(node.getAncestor(-2)).toThrow(new IllegalArgumentException());
@@ -136,14 +120,8 @@
     }
 
     @Test
-    public void shouldProvideNode() throws Exception {
-        assertThat(prop.getNode(), is(node));
-    }
-
-    @Test
     public void shouldProvideName() throws Exception {
-        stub(name.getString(namespaceRegistry)).toReturn("name");
-        assertThat(prop.getName(), is("name"));
+        assertThat(prop.getName(), is("jcr:mimeType"));
     }
 
     @Test
@@ -154,8 +132,7 @@
     @Test
     public void shouldProvidePath() throws Exception {
         stub(node.getPath()).toReturn("/nodeName");
-        stub(name.getString(namespaceRegistry)).toReturn("propertyName");
-        assertThat(prop.getPath(), is("/nodeName/propertyName"));
+        assertThat(prop.getPath(), is("/nodeName/jcr:mimeType"));
     }
 
     @Test
@@ -171,38 +148,33 @@
     }
 
     @Test
-    public void shouldIndicateSameAsNodeWithSameParentAndName() throws Exception {
-        stub(name.getString(namespaceRegistry)).toReturn("propertyName");
+    public void shouldIndicateSameAsNodeWithSameParentAndSamePropertyName() throws Exception {
+        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
+                                                                                 .create(dnaProperty.getName());
         Node otherNode = Mockito.mock(Node.class);
         stub(otherNode.getSession()).toReturn(session);
-        Name otherName = Mockito.mock(Name.class);
-        stub(otherName.getString(namespaceRegistry)).toReturn("propertyName");
         stub(node.isSame(otherNode)).toReturn(true);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, otherName);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(true));
     }
 
     @Test
     public void shouldIndicateDifferentThanNodeWithDifferentParent() throws Exception {
-        stub(name.getString(namespaceRegistry)).toReturn("propertyName");
+        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
         Node otherNode = Mockito.mock(Node.class);
         stub(otherNode.getSession()).toReturn(session);
-        Name otherName = Mockito.mock(Name.class);
-        stub(otherName.getString(namespaceRegistry)).toReturn("propertyName");
         stub(node.isSame(otherNode)).toReturn(false);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, otherName);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
     @Test
-    public void shouldIndicateDifferentThanNodeWithDifferentName() throws Exception {
-        stub(name.getString(namespaceRegistry)).toReturn("propertyName");
+    public void shouldIndicateDifferentThanPropertyWithSameNodeWithDifferentPropertyName() throws Exception {
+        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
         Node otherNode = Mockito.mock(Node.class);
         stub(otherNode.getSession()).toReturn(session);
-        Name otherName = Mockito.mock(Name.class);
-        stub(otherName.getString(namespaceRegistry)).toReturn("propertyName2");
         stub(node.isSame(otherNode)).toReturn(true);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, otherName);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
@@ -260,34 +232,35 @@
 
         MockAbstractJcrProperty( Node node,
                                  ExecutionContext executionContext,
-                                 Name name ) {
-            super(node, executionContext, name);
+                                 PropertyDefinition propertyDefinition,
+                                 org.jboss.dna.graph.property.Property dnaProperty ) {
+            super(node, executionContext, propertyDefinition, dnaProperty);
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see javax.jcr.Property#getBoolean()
+         * @see javax.jcr.Property#getNode()
          */
-        public boolean getBoolean() {
-            return false;
+        public Node getNode() {
+            throw new UnsupportedOperationException(); // shouldn't be called
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see javax.jcr.Property#getDate()
+         * @see javax.jcr.Property#getBoolean()
          */
-        public Calendar getDate() {
-            return null;
+        public boolean getBoolean() {
+            return false;
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see javax.jcr.Property#getDefinition()
+         * @see javax.jcr.Property#getDate()
          */
-        public PropertyDefinition getDefinition() {
+        public Calendar getDate() {
             return null;
         }
 
@@ -348,15 +321,6 @@
         /**
          * {@inheritDoc}
          * 
-         * @see javax.jcr.Property#getType()
-         */
-        public int getType() {
-            return 0;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
          * @see javax.jcr.Property#getValue()
          */
         public Value getValue() {

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -74,24 +74,24 @@
 
             addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -26,11 +26,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
-import java.io.InputStream;
-import java.io.Reader;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.UUID;
+import static org.mockito.Mockito.stub;
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
@@ -38,11 +34,8 @@
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoAnnotations.Mock;
 
@@ -54,120 +47,31 @@
     private Property prop;
     @Mock
     private Node node;
-    private ExecutionContext executionContext = new ExecutionContext();
+    private ExecutionContext executionContext;
     @Mock
-    Name name;
+    private PropertyDefinition definition;
+    private org.jboss.dna.graph.property.Property dnaProperty;
 
     @Before
     public void before() {
         MockitoAnnotations.initMocks(this);
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(true));
+        executionContext = new ExecutionContext();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
+        stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
+        stub(definition.isMultiple()).toReturn(true);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoValue() {
-        new JcrMultiValueProperty(node, executionContext, name, null);
-    }
-
     @Test
     public void shouldProvideAppropriateType() throws Exception {
-        assertThat(prop.getType(), is(PropertyType.BOOLEAN));
-        Calendar cal = Calendar.getInstance();
-        Property prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(cal));
-        assertThat(prop.getType(), is(PropertyType.DATE));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(cal.getTime()));
-        assertThat(prop.getType(), is(PropertyType.DATE));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1.0));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1.0F));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1));
-        assertThat(prop.getType(), is(PropertyType.LONG));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1L));
-        assertThat(prop.getType(), is(PropertyType.LONG));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(new Object()));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList("value"));
-        assertThat(prop.getType(), is(PropertyType.STRING));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(UUID.randomUUID()));
-        assertThat(prop.getType(), is(PropertyType.REFERENCE));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(Mockito.mock(Reader.class)));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(Mockito.mock(InputStream.class)));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(new Object()));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(name));
-        assertThat(prop.getType(), is(PropertyType.NAME));
-        prop = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(Mockito.mock(Path.class)));
-        assertThat(prop.getType(), is(PropertyType.PATH));
+        assertThat(prop.getType(), is(definition.getRequiredType()));
     }
 
-    @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideBoolean() throws Exception {
-        prop.getBoolean();
-    }
-
-    @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideDateForCalendar() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(Calendar.getInstance())).getDate();
-    }
-
-    @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideDateForDate() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(Calendar.getInstance().getTime())).getDate();
-    }
-
     @Test
     public void shouldProvidePropertyDefinition() throws Exception {
         assertThat(prop.getDefinition(), notNullValue());
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionDeclaringNodeType() throws Exception {
-        prop.getDefinition().getDeclaringNodeType();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionDefaultValues() throws Exception {
-        prop.getDefinition().getDefaultValues();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionName() throws Exception {
-        prop.getDefinition().getName();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetOnParentVersion() throws Exception {
-        prop.getDefinition().getOnParentVersion();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetRequiredType() throws Exception {
-        prop.getDefinition().getRequiredType();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetValueConstraints() throws Exception {
-        prop.getDefinition().getValueConstraints();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsAutoCreated() throws Exception {
-        prop.getDefinition().isAutoCreated();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsMandatory() throws Exception {
-        prop.getDefinition().isMandatory();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsProtected() throws Exception {
-        prop.getDefinition().isProtected();
-    }
-
     @Test
     public void shouldIndicateHasMultipleValues() throws Exception {
         PropertyDefinition def = prop.getDefinition();
@@ -176,33 +80,33 @@
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideDoubleForDouble() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1.0)).getDouble();
+    public void shouldNotProvideBooleanForMultiValuedProperty() throws Exception {
+        prop.getBoolean();
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideDoubleForFloat() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1.0F)).getDouble();
+    public void shouldNotProvideDateForMultiValuedProperty() throws Exception {
+        prop.getDate();
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideLongForInteger() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1)).getLong();
+    public void shouldNotProvideDoubleForMultiValuedProperty() throws Exception {
+        prop.getDouble();
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideLongForLong() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(1L)).getLong();
+    public void shouldNotProvideLongForMultiValuedProperty() throws Exception {
+        prop.getLong();
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideStream() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(new Object())).getStream();
+    public void shouldNotProvideStreamForMultiValuedProperty() throws Exception {
+        prop.getStream();
     }
 
     @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideString() throws Exception {
-        new JcrMultiValueProperty(node, executionContext, name, Arrays.asList("value")).getString();
+    public void shouldNotProvideStringForMultiValuedProperty() throws Exception {
+        prop.getString();
     }
 
     @Test( expected = ValueFormatException.class )
@@ -229,14 +133,38 @@
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
         assertThat(lengths[0], is(4L));
-        lengths = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList("value")).getLengths();
+
+        Object value = "value";
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        stub(definition.isMultiple()).toReturn(true);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
         assertThat(lengths[0], is(5L));
-        Object obj = new Object();
-        lengths = new JcrMultiValueProperty(node, executionContext, name, Arrays.asList(obj)).getLengths();
+
+        value = new Object();
+        long expectedLength = executionContext.getValueFactories().getBinaryFactory().create(value).getSize();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        stub(definition.isMultiple()).toReturn(true);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
-        assertThat(lengths[0], is((long)obj.toString().length()));
+        assertThat(lengths[0], is(expectedLength));
+
+        String[] values = new String[] {"value1", "value2", "value 3 is longer"};
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object[])values);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        stub(definition.isMultiple()).toReturn(true);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        lengths = prop.getLengths();
+        assertThat(lengths, notNullValue());
+        assertThat(lengths.length, is(values.length));
+        assertThat(lengths[0], is((long)values[0].length()));
+        assertThat(lengths[1], is((long)values[1].length()));
+        assertThat(lengths[2], is((long)values[2].length()));
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -26,9 +26,9 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
 import java.io.InputStream;
-import java.io.Reader;
-import java.util.Calendar;
 import java.util.UUID;
 import javax.jcr.Node;
 import javax.jcr.Property;
@@ -37,11 +37,11 @@
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoAnnotations.Mock;
 
@@ -53,122 +53,109 @@
     private Property prop;
     @Mock
     private Node node;
-    private ExecutionContext executionContext = new ExecutionContext();
+    private ExecutionContext executionContext;
     @Mock
     Name name;
+    @Mock
+    private PropertyDefinition definition;
+    private org.jboss.dna.graph.property.Property dnaProperty;
 
     @Before
     public void before() {
         MockitoAnnotations.initMocks(this);
-        prop = new JcrProperty(node, executionContext, name, true);
+        executionContext = new ExecutionContext();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        stub(definition.isMultiple()).toReturn(false);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoValue() {
-        new JcrProperty(node, executionContext, name, null);
-    }
-
     @Test
     public void shouldProvideBoolean() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
+        stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getBoolean(), is(true));
         assertThat(prop.getType(), is(PropertyType.BOOLEAN));
     }
 
     @Test
-    public void shouldProvideDate() throws Exception {
-        Calendar cal = Calendar.getInstance();
-        Property prop = new JcrProperty(node, executionContext, name, cal);
-        assertThat(prop.getDate(), is(cal));
-        assertThat(prop.getType(), is(PropertyType.DATE));
-        prop = new JcrProperty(node, executionContext, name, cal.getTime());
-        assertThat(prop.getDate(), is(cal));
-        assertThat(prop.getType(), is(PropertyType.DATE));
+    public void shouldIndicateHasSingleValue() throws Exception {
+        PropertyDefinition def = prop.getDefinition();
+        assertThat(def, notNullValue());
+        assertThat(def.isMultiple(), is(false));
     }
 
     @Test
-    public void shouldProvidePropertyDefinition() throws Exception {
-        assertThat(prop.getDefinition(), notNullValue());
+    public void shouldProvideDate() throws Exception {
+        DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DATE);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getDate(), is(dnaDate.toCalendar()));
+        assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
+        assertThat(prop.getType(), is(PropertyType.DATE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionDeclaringNodeType() throws Exception {
-        prop.getDefinition().getDeclaringNodeType();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionDefaultValues() throws Exception {
-        prop.getDefinition().getDefaultValues();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionName() throws Exception {
-        prop.getDefinition().getName();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetOnParentVersion() throws Exception {
-        prop.getDefinition().getOnParentVersion();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetRequiredType() throws Exception {
-        prop.getDefinition().getRequiredType();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionGetValueConstraints() throws Exception {
-        prop.getDefinition().getValueConstraints();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsAutoCreated() throws Exception {
-        prop.getDefinition().isAutoCreated();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsMandatory() throws Exception {
-        prop.getDefinition().isMandatory();
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowPropertyDefinitionIsProtected() throws Exception {
-        prop.getDefinition().isProtected();
-    }
-
     @Test
-    public void shouldIndicateHasSingleValue() throws Exception {
-        PropertyDefinition def = prop.getDefinition();
-        assertThat(def, notNullValue());
-        assertThat(def.isMultiple(), is(false));
+    public void shouldProvideNode() throws Exception {
+        UUID referencedUuid = UUID.randomUUID();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
+        stub(definition.getRequiredType()).toReturn(PropertyType.REFERENCE);
+        Node referencedNode = mock(Node.class);
+        JcrSession session = mock(JcrSession.class);
+        stub(node.getSession()).toReturn(session);
+        stub(session.getNode(referencedUuid)).toReturn(referencedNode);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getNode(), is(referencedNode));
+        assertThat(prop.getType(), is(PropertyType.REFERENCE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
     public void shouldProvideDouble() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, 1.0);
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        prop = new JcrProperty(node, executionContext, name, 1.0F);
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
     public void shouldProvideLong() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, 1);
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getLong(), is(1L));
-        assertThat(prop.getType(), is(PropertyType.LONG));
-        prop = new JcrProperty(node, executionContext, name, 1L);
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getLong(), is(1L));
-        assertThat(prop.getType(), is(PropertyType.LONG));
+        assertThat(prop.getString(), is("1"));
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
     public void shouldProvideStream() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, new Object());
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
+        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getType(), is(PropertyType.BINARY));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         InputStream stream = prop.getStream();
         try {
             assertThat(stream, notNullValue());
-            assertThat(prop.getType(), is(PropertyType.BINARY));
         } finally {
             if (stream != null) {
                 stream.close();
@@ -178,46 +165,59 @@
 
     @Test
     public void shouldProvideString() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, "value");
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getString(), is("value"));
         assertThat(prop.getType(), is(PropertyType.STRING));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
     public void shouldAllowReferenceValue() throws Exception {
         UUID uuid = UUID.randomUUID();
-        Property prop = new JcrProperty(node, executionContext, name, uuid);
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
         assertThat(prop.getString(), is(uuid.toString()));
-        assertThat(prop.getType(), is(PropertyType.REFERENCE));
+        assertThat(prop.getType(), is(PropertyType.STRING));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
-    public void shouldAllowBinaryValue() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, Mockito.mock(Reader.class));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrProperty(node, executionContext, name, Mockito.mock(InputStream.class));
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        prop = new JcrProperty(node, executionContext, name, new Object());
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-    }
-
-    @Test
     public void shouldAllowNameValue() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, name);
+        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
+        Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
+        stub(definition.getRequiredType()).toReturn(PropertyType.NAME);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is("acme:something"));
         assertThat(prop.getType(), is(PropertyType.NAME));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        // Change the namespace registry ...
+        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
+        assertThat(prop.getString(), is("acme2:something"));
     }
 
     @Test
     public void shouldAllowPathValue() throws Exception {
-        Property prop = new JcrProperty(node, executionContext, name, Mockito.mock(Path.class));
+        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
+        Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
+        stub(definition.getRequiredType()).toReturn(PropertyType.PATH);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is("/a/b/acme:c"));
         assertThat(prop.getType(), is(PropertyType.PATH));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        // Change the namespace registry ...
+        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
+        assertThat(prop.getString(), is("/a/b/acme2:c"));
     }
 
     @Test
     public void shouldProvideValue() throws Exception {
         Value val = prop.getValue();
         assertThat(val, notNullValue());
-        assertThat(val.getBoolean(), is(true));
     }
 
     @Test( expected = ValueFormatException.class )
@@ -227,14 +227,31 @@
 
     @Test
     public void shouldProvideLength() throws Exception {
-        assertThat(prop.getLength(), is(4L));
-        assertThat(new JcrProperty(node, executionContext, name, "value").getLength(), is(5L));
+        String dnaValue = executionContext.getValueFactories().getStringFactory().create(dnaProperty.getFirstValue());
+        assertThat(prop.getLength(), is((long)dnaValue.length()));
+
+        dnaValue = "some other value";
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLength(), is((long)dnaValue.length()));
+
         Object obj = new Object();
-        assertThat(new JcrProperty(node, executionContext, name, obj).getLength(), is((long)obj.toString().length()));
+        long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
+        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
+        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLength(), is(binaryLength));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideLengths() throws Exception {
         prop.getLengths();
     }
+
+    @Test
+    public void shouldProvidePropertyDefinition() throws Exception {
+        assertThat(prop.getDefinition(), notNullValue());
+    }
+
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java	2009-03-04 00:54:32 UTC (rev 751)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java	2009-03-04 19:08:45 UTC (rev 752)
@@ -45,29 +45,14 @@
 public class JcrValueTest {
 
     private ValueFactories factories;
-    private JcrValue<Boolean> value;
+    private JcrValue value;
 
     @Before
     public void before() {
         factories = new StandardValueFactories(Mockito.mock(NamespaceRegistry.class));
-        value = new JcrValue<Boolean>(factories, PropertyType.BOOLEAN, true);
+        value = new JcrValue(factories, PropertyType.BOOLEAN, true);
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoValueFactories() throws Exception {
-        new JcrValue<Object>(null, PropertyType.BINARY, true);
-    }
-
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowInvalidType() throws Exception {
-        new JcrValue<Object>(factories, 0, true);
-    }
-
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoValue() throws Exception {
-        new JcrValue<Object>(factories, PropertyType.BINARY, null);
-    }
-
     @Test
     public void shouldProvideType() throws Exception {
         assertThat(value.getType(), is(PropertyType.BOOLEAN));
@@ -117,30 +102,30 @@
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForDate() throws Exception {
-        new JcrValue<Date>(factories, PropertyType.DATE, new Date()).getBoolean();
+        new JcrValue(factories, PropertyType.DATE, new Date()).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue<Date>(factories, PropertyType.DATE, date).getDate().getTime(), is(date));
+        assertThat(new JcrValue(factories, PropertyType.DATE, date).getDate().getTime(), is(date));
     }
 
     @Test
     public void shouldProvideDoubleForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue<Date>(factories, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
+        assertThat(new JcrValue(factories, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
     }
 
     @Test
     public void shouldProvideLongForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue<Date>(factories, PropertyType.DATE, date).getLong(), is(date.getTime()));
+        assertThat(new JcrValue(factories, PropertyType.DATE, date).getLong(), is(date.getTime()));
     }
 
     @Test
     public void shouldProvideStreamForDate() throws Exception {
-        testProvidesStream(new JcrValue<Date>(factories, PropertyType.DATE, new Date()));
+        testProvidesStream(new JcrValue(factories, PropertyType.DATE, new Date()));
     }
 
     @Test
@@ -149,163 +134,161 @@
         date.set(2008, 7, 18, 12, 0, 0);
         date.set(Calendar.MILLISECOND, 0);
         String expectedValue = "2008-08-18T12:00:00.000";
-        assertThat(new JcrValue<Calendar>(factories, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
+        assertThat(new JcrValue(factories, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
                    is(expectedValue));
-        assertThat(new JcrValue<Date>(factories, PropertyType.DATE, date.getTime()).getString().substring(0,
-                                                                                                          expectedValue.length()),
+        assertThat(new JcrValue(factories, PropertyType.DATE, date.getTime()).getString().substring(0, expectedValue.length()),
                    is(expectedValue));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForDouble() throws Exception {
-        new JcrValue<Double>(factories, PropertyType.DOUBLE, 0.0).getBoolean();
+        new JcrValue(factories, PropertyType.DOUBLE, 0.0).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForDouble() throws Exception {
         Calendar expectedValue = Calendar.getInstance();
         expectedValue.setTime(new Date(0L));
-        assertThat(new JcrValue<Double>(factories, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
+        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
     }
 
     @Test
     public void shouldProvideDoubleForDouble() throws Exception {
-        assertThat(new JcrValue<Double>(factories, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
+        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
     }
 
     @Test
     public void shouldProvideLongForDouble() throws Exception {
-        assertThat(new JcrValue<Double>(factories, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
-        assertThat(new JcrValue<Double>(factories, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
+        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
+        assertThat(new JcrValue(factories, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
     }
 
     @Test
     public void shouldProvideStreamForDouble() throws Exception {
-        testProvidesStream(new JcrValue<Double>(factories, PropertyType.DOUBLE, 1.0));
+        testProvidesStream(new JcrValue(factories, PropertyType.DOUBLE, 1.0));
     }
 
     @Test
     public void shouldProvideStringForDouble() throws Exception {
-        assertThat(new JcrValue<Double>(factories, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
+        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForLong() throws Exception {
-        new JcrValue<Long>(factories, PropertyType.LONG, 0L).getBoolean();
+        new JcrValue(factories, PropertyType.LONG, 0L).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForLong() throws Exception {
         Calendar expectedValue = Calendar.getInstance();
         expectedValue.setTime(new Date(0L));
-        assertThat(new JcrValue<Long>(factories, PropertyType.LONG, 0L).getDate(), is(expectedValue));
+        assertThat(new JcrValue(factories, PropertyType.LONG, 0L).getDate(), is(expectedValue));
     }
 
     @Test
     public void shouldProvideDoubleForLong() throws Exception {
-        assertThat(new JcrValue<Long>(factories, PropertyType.LONG, 1L).getDouble(), is(1.0));
+        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getDouble(), is(1.0));
     }
 
     @Test
     public void shouldProvideLongForLong() throws Exception {
-        assertThat(new JcrValue<Long>(factories, PropertyType.LONG, 1L).getLong(), is(1L));
+        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getLong(), is(1L));
     }
 
     @Test
     public void shouldProvideStreamForLong() throws Exception {
-        testProvidesStream(new JcrValue<Long>(factories, PropertyType.LONG, 1L));
+        testProvidesStream(new JcrValue(factories, PropertyType.LONG, 1L));
     }
 
     @Test
     public void shouldProvideStringForLong() throws Exception {
-        assertThat(new JcrValue<Long>(factories, PropertyType.LONG, 1L).getString(), is("1"));
+        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getString(), is("1"));
     }
 
     @Test
     public void shouldProvideBooleanForString() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "true").getBoolean(), is(true));
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "yes").getBoolean(), is(false));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "true").getBoolean(), is(true));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "yes").getBoolean(), is(false));
     }
 
     @Test
     public void shouldProvideDateForString() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "2008").getDate(), notNullValue());
+        assertThat(new JcrValue(factories, PropertyType.STRING, "2008").getDate(), notNullValue());
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDateForInvalidString() throws Exception {
-        new JcrValue<String>(factories, PropertyType.STRING, "true").getDate();
+        new JcrValue(factories, PropertyType.STRING, "true").getDate();
     }
 
     @Test
     public void shouldProvideDoubleForString() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "1").getDouble(), is(1.0));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "1").getDouble(), is(1.0));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDoubleForInvalidString() throws Exception {
-        new JcrValue<String>(factories, PropertyType.STRING, "true").getDouble();
+        new JcrValue(factories, PropertyType.STRING, "true").getDouble();
     }
 
     @Test
     public void shouldProvideLongForString() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "1").getLong(), is(1L));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "1").getLong(), is(1L));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideLongForInvalidString() throws Exception {
-        new JcrValue<String>(factories, PropertyType.STRING, "true").getLong();
+        new JcrValue(factories, PropertyType.STRING, "true").getLong();
     }
 
     @Test
     public void shouldProvideStreamForString() throws Exception {
-        testProvidesStream(new JcrValue<String>(factories, PropertyType.STRING, "true"));
+        testProvidesStream(new JcrValue(factories, PropertyType.STRING, "true"));
     }
 
     @Test
     public void shouldProvideStringForString() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "true").getString(), is("true"));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "true").getString(), is("true"));
     }
 
     @Test
     public void shouldProvideBooleanForUuid() throws Exception {
-        assertThat(new JcrValue<UUID>(factories, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
+        assertThat(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDateForUuid() throws Exception {
-        new JcrValue<UUID>(factories, PropertyType.STRING, UUID.randomUUID()).getDate();
+        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDate();
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDoubleForUuid() throws Exception {
-        new JcrValue<UUID>(factories, PropertyType.STRING, UUID.randomUUID()).getDouble();
+        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDouble();
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideLongForUuid() throws Exception {
-        new JcrValue<UUID>(factories, PropertyType.STRING, UUID.randomUUID()).getLong();
+        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getLong();
     }
 
     @Test
     public void shouldProvideStreamForUuid() throws Exception {
-        testProvidesStream(new JcrValue<UUID>(factories, PropertyType.STRING, UUID.randomUUID()));
+        testProvidesStream(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()));
     }
 
     @Test
     public void shouldProvideStringForUuid() throws Exception {
         String expectedValue = "40d373f7-75ad-4d84-900e-c72ebd98abb9";
-        assertThat(new JcrValue<UUID>(factories, PropertyType.STRING, UUID.fromString(expectedValue)).getString(),
-                   is(expectedValue));
+        assertThat(new JcrValue(factories, PropertyType.STRING, UUID.fromString(expectedValue)).getString(), is(expectedValue));
     }
 
     @Test
     public void shouldProvideLength() throws Exception {
-        assertThat(new JcrValue<String>(factories, PropertyType.STRING, "test").getLength(), is(4L));
-        assertThat(new JcrValue<Object>(factories, PropertyType.BINARY, "test").getLength(), is(4L));
+        assertThat(new JcrValue(factories, PropertyType.STRING, "test").getLength(), is(4L));
+        assertThat(new JcrValue(factories, PropertyType.BINARY, "test").getLength(), is(4L));
     }
 
-    private void testProvidesStream( JcrValue<?> value ) throws Exception {
+    private void testProvidesStream( JcrValue value ) throws Exception {
         InputStream stream = value.getStream();
         assertThat(stream, notNullValue());
         stream.close();



From dna-commits at lists.jboss.org  Wed Mar  4 14:11:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 04 Mar 2009 14:11:34 -0500
Subject: [dna-commits] DNA SVN: r753 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LewVC-0006Fb-Ez@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-04 14:11:34 -0500 (Wed, 04 Mar 2009)
New Revision: 753

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
Removed:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
Log:
DNA-291 JCR property objects do not have PropertyDefinition

Renamed JcrProperty to JcrSingleValueProperty to more closely match the existing JcrMultiValueProperty and reduce confusion.

Deleted: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-04 19:08:45 UTC (rev 752)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java	2009-03-04 19:11:34 UTC (rev 753)
@@ -1,244 +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.InputStream;
-import java.util.Calendar;
-import java.util.UUID;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.Reference;
-import org.jboss.dna.graph.property.ValueFactories;
-
-/**
- * @author jverhaeg
- */
-final class JcrProperty extends AbstractJcrProperty {
-
-    JcrProperty( Node node,
-                 ExecutionContext executionContext,
-                 PropertyDefinition definition,
-                 Property dnaProperty ) {
-        super(node, executionContext, definition, dnaProperty);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getBoolean()
-     */
-    public boolean getBoolean() throws RepositoryException {
-        try {
-            return getExecutionContext().getValueFactories().getBooleanFactory().create(getDnaProperty().getFirstValue());
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getDate()
-     */
-    public Calendar getDate() throws RepositoryException {
-        try {
-            return getExecutionContext().getValueFactories()
-                                        .getDateFactory()
-                                        .create(getDnaProperty().getFirstValue())
-                                        .toCalendar();
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getDouble()
-     */
-    public double getDouble() throws RepositoryException {
-        try {
-            return getExecutionContext().getValueFactories().getDoubleFactory().create(getDnaProperty().getFirstValue());
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getLength()
-     */
-    public long getLength() throws RepositoryException {
-        return createValue(getDnaProperty().getFirstValue()).getLength();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws ValueFormatException always
-     * @see javax.jcr.Property#getLengths()
-     */
-    public long[] getLengths() throws ValueFormatException {
-        throw new ValueFormatException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getLong()
-     */
-    public long getLong() throws RepositoryException {
-        try {
-            return getExecutionContext().getValueFactories().getLongFactory().create(getDnaProperty().getFirstValue());
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getNode()
-     */
-    public final Node getNode() throws RepositoryException {
-        try {
-            ValueFactories factories = getExecutionContext().getValueFactories();
-            Reference dnaReference = factories.getReferenceFactory().create(getDnaProperty().getFirstValue());
-            UUID uuid = factories.getUuidFactory().create(dnaReference);
-            return ((JcrSession)getSession()).getNode(uuid);
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getStream()
-     */
-    public InputStream getStream() throws RepositoryException {
-        try {
-            Binary binary = getExecutionContext().getValueFactories().getBinaryFactory().create(getDnaProperty().getFirstValue());
-            return new SelfClosingInputStream(binary);
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getString()
-     */
-    public String getString() throws RepositoryException {
-        try {
-            return getExecutionContext().getValueFactories().getStringFactory().create(getDnaProperty().getFirstValue());
-        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
-            throw new ValueFormatException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Property#getValue()
-     */
-    public Value getValue() {
-        return createValue(getDnaProperty().getFirstValue());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws ValueFormatException always
-     * @see javax.jcr.Property#getValues()
-     */
-    public Value[] getValues() throws ValueFormatException {
-        throw new ValueFormatException();
-    }
-
-    /*
-     * {@inheritDoc}
-     * 
-     * @throws IllegalArgumentException if <code>value</code> is <code>null</code>.
-     * @see javax.jcr.Property#setValue(javax.jcr.Value)
-     *
-    @SuppressWarnings( "fallthrough" )
-    public void setValue( Value value ) throws RepositoryException {
-        CheckArg.isNotNull(value, "value");
-        // TODOx: Check node type constraint
-        try {
-            jcrValue = JcrValue.class.cast(value);
-        } catch (ClassCastException error) {
-            // TODOx: not sure if this is even possible
-            ValueFactories valueFactories = getExecutionContext().getValueFactories();
-            int type = value.getType();
-            switch (type) {
-                case PropertyType.BINARY: {
-                    jcrValue = new JcrValue<InputStream>(valueFactories, type, value.getStream());
-                    break;
-                }
-                case PropertyType.BOOLEAN: {
-                    jcrValue = new JcrValue<Boolean>(valueFactories, type, value.getBoolean());
-                    break;
-                }
-                case PropertyType.DATE: {
-                    jcrValue = new JcrValue<Calendar>(valueFactories, type, value.getDate());
-                    break;
-                }
-                case PropertyType.DOUBLE: {
-                    jcrValue = new JcrValue<Double>(valueFactories, type, value.getDouble());
-                    break;
-                }
-                case PropertyType.LONG: {
-                    jcrValue = new JcrValue<Long>(valueFactories, type, value.getLong());
-                    break;
-                }
-                case PropertyType.REFERENCE: {
-                    try {
-                        jcrValue = new JcrValue<UUID>(valueFactories, type, UUID.fromString(value.getString()));
-                    } catch (IllegalArgumentException fallsThroughToString) {
-                    }
-                }
-                case PropertyType.NAME:
-                case PropertyType.PATH:
-                case PropertyType.STRING: {
-                    jcrValue = new JcrValue<String>(valueFactories, type, value.getString());
-                    break;
-                }
-                default: {
-                    throw new AssertionError("Unsupported PropertyType: " + value.getType());
-                }
-            }
-        }
-    }
-    */
-}

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-03-04 19:08:45 UTC (rev 752)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-04 19:11:34 UTC (rev 753)
@@ -723,7 +723,7 @@
         if (referenceable) {
             if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
-            properties.add(new JcrProperty(node, executionContext, propertyDefinition, uuidProperty));
+            properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, uuidProperty));
         }
 
         // Now create the JCR property object wrappers around the other properties ...
@@ -763,7 +763,7 @@
             if (isMultiple) {
                 properties.add(new JcrMultiValueProperty(node, executionContext, propertyDefinition, dnaProp));
             } else {
-                properties.add(new JcrProperty(node, executionContext, propertyDefinition, dnaProp));
+                properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, dnaProp));
             }
         }
 

Copied: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java (from rev 752, trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrProperty.java)
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-03-04 19:11:34 UTC (rev 753)
@@ -0,0 +1,244 @@
+/*
+ * 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.InputStream;
+import java.util.Calendar;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.PropertyDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Reference;
+import org.jboss.dna.graph.property.ValueFactories;
+
+/**
+ * @author jverhaeg
+ */
+final class JcrSingleValueProperty extends AbstractJcrProperty {
+
+    JcrSingleValueProperty( Node node,
+                 ExecutionContext executionContext,
+                 PropertyDefinition definition,
+                 Property dnaProperty ) {
+        super(node, executionContext, definition, dnaProperty);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getBoolean()
+     */
+    public boolean getBoolean() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories().getBooleanFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getDate()
+     */
+    public Calendar getDate() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories()
+                                        .getDateFactory()
+                                        .create(getDnaProperty().getFirstValue())
+                                        .toCalendar();
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getDouble()
+     */
+    public double getDouble() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories().getDoubleFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getLength()
+     */
+    public long getLength() throws RepositoryException {
+        return createValue(getDnaProperty().getFirstValue()).getLength();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#getLengths()
+     */
+    public long[] getLengths() throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getLong()
+     */
+    public long getLong() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories().getLongFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getNode()
+     */
+    public final Node getNode() throws RepositoryException {
+        try {
+            ValueFactories factories = getExecutionContext().getValueFactories();
+            Reference dnaReference = factories.getReferenceFactory().create(getDnaProperty().getFirstValue());
+            UUID uuid = factories.getUuidFactory().create(dnaReference);
+            return ((JcrSession)getSession()).getNode(uuid);
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getStream()
+     */
+    public InputStream getStream() throws RepositoryException {
+        try {
+            Binary binary = getExecutionContext().getValueFactories().getBinaryFactory().create(getDnaProperty().getFirstValue());
+            return new SelfClosingInputStream(binary);
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getString()
+     */
+    public String getString() throws RepositoryException {
+        try {
+            return getExecutionContext().getValueFactories().getStringFactory().create(getDnaProperty().getFirstValue());
+        } catch (org.jboss.dna.graph.property.ValueFormatException e) {
+            throw new ValueFormatException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#getValue()
+     */
+    public Value getValue() {
+        return createValue(getDnaProperty().getFirstValue());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#getValues()
+     */
+    public Value[] getValues() throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /*
+     * {@inheritDoc}
+     * 
+     * @throws IllegalArgumentException if <code>value</code> is <code>null</code>.
+     * @see javax.jcr.Property#setValue(javax.jcr.Value)
+     *
+    @SuppressWarnings( "fallthrough" )
+    public void setValue( Value value ) throws RepositoryException {
+        CheckArg.isNotNull(value, "value");
+        // TODOx: Check node type constraint
+        try {
+            jcrValue = JcrValue.class.cast(value);
+        } catch (ClassCastException error) {
+            // TODOx: not sure if this is even possible
+            ValueFactories valueFactories = getExecutionContext().getValueFactories();
+            int type = value.getType();
+            switch (type) {
+                case PropertyType.BINARY: {
+                    jcrValue = new JcrValue<InputStream>(valueFactories, type, value.getStream());
+                    break;
+                }
+                case PropertyType.BOOLEAN: {
+                    jcrValue = new JcrValue<Boolean>(valueFactories, type, value.getBoolean());
+                    break;
+                }
+                case PropertyType.DATE: {
+                    jcrValue = new JcrValue<Calendar>(valueFactories, type, value.getDate());
+                    break;
+                }
+                case PropertyType.DOUBLE: {
+                    jcrValue = new JcrValue<Double>(valueFactories, type, value.getDouble());
+                    break;
+                }
+                case PropertyType.LONG: {
+                    jcrValue = new JcrValue<Long>(valueFactories, type, value.getLong());
+                    break;
+                }
+                case PropertyType.REFERENCE: {
+                    try {
+                        jcrValue = new JcrValue<UUID>(valueFactories, type, UUID.fromString(value.getString()));
+                    } catch (IllegalArgumentException fallsThroughToString) {
+                    }
+                }
+                case PropertyType.NAME:
+                case PropertyType.PATH:
+                case PropertyType.STRING: {
+                    jcrValue = new JcrValue<String>(valueFactories, type, value.getString());
+                    break;
+                }
+                default: {
+                    throw new AssertionError("Unsupported PropertyType: " + value.getType());
+                }
+            }
+        }
+    }
+    */
+}


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

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java	2009-03-04 19:08:45 UTC (rev 752)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java	2009-03-04 19:11:34 UTC (rev 753)
@@ -1,257 +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 static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.stub;
-import java.io.InputStream;
-import java.util.UUID;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
-
-/**
- * @author jverhaeg
- */
-public class JcrPropertyTest {
-
-    private Property prop;
-    @Mock
-    private Node node;
-    private ExecutionContext executionContext;
-    @Mock
-    Name name;
-    @Mock
-    private PropertyDefinition definition;
-    private org.jboss.dna.graph.property.Property dnaProperty;
-
-    @Before
-    public void before() {
-        MockitoAnnotations.initMocks(this);
-        executionContext = new ExecutionContext();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        stub(definition.isMultiple()).toReturn(false);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-    }
-
-    @Test
-    public void shouldProvideBoolean() throws Exception {
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
-        stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getBoolean(), is(true));
-        assertThat(prop.getType(), is(PropertyType.BOOLEAN));
-    }
-
-    @Test
-    public void shouldIndicateHasSingleValue() throws Exception {
-        PropertyDefinition def = prop.getDefinition();
-        assertThat(def, notNullValue());
-        assertThat(def.isMultiple(), is(false));
-    }
-
-    @Test
-    public void shouldProvideDate() throws Exception {
-        DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DATE);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getDate(), is(dnaDate.toCalendar()));
-        assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
-        assertThat(prop.getType(), is(PropertyType.DATE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldProvideNode() throws Exception {
-        UUID referencedUuid = UUID.randomUUID();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
-        stub(definition.getRequiredType()).toReturn(PropertyType.REFERENCE);
-        Node referencedNode = mock(Node.class);
-        JcrSession session = mock(JcrSession.class);
-        stub(node.getSession()).toReturn(session);
-        stub(session.getNode(referencedUuid)).toReturn(referencedNode);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getNode(), is(referencedNode));
-        assertThat(prop.getType(), is(PropertyType.REFERENCE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldProvideDouble() throws Exception {
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getDouble(), is(1.0));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getDouble(), is(1.0));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldProvideLong() throws Exception {
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getLong(), is(1L));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getLong(), is(1L));
-        assertThat(prop.getString(), is("1"));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldProvideStream() throws Exception {
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
-        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getType(), is(PropertyType.BINARY));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-        InputStream stream = prop.getStream();
-        try {
-            assertThat(stream, notNullValue());
-        } finally {
-            if (stream != null) {
-                stream.close();
-            }
-        }
-    }
-
-    @Test
-    public void shouldProvideString() throws Exception {
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getString(), is("value"));
-        assertThat(prop.getType(), is(PropertyType.STRING));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldAllowReferenceValue() throws Exception {
-        UUID uuid = UUID.randomUUID();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getString(), is(uuid.toString()));
-        assertThat(prop.getType(), is(PropertyType.STRING));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-    }
-
-    @Test
-    public void shouldAllowNameValue() throws Exception {
-        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
-        Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
-        stub(definition.getRequiredType()).toReturn(PropertyType.NAME);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getString(), is("acme:something"));
-        assertThat(prop.getType(), is(PropertyType.NAME));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-        // Change the namespace registry ...
-        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
-        assertThat(prop.getString(), is("acme2:something"));
-    }
-
-    @Test
-    public void shouldAllowPathValue() throws Exception {
-        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
-        Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
-        stub(definition.getRequiredType()).toReturn(PropertyType.PATH);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getString(), is("/a/b/acme:c"));
-        assertThat(prop.getType(), is(PropertyType.PATH));
-        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
-        // Change the namespace registry ...
-        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
-        assertThat(prop.getString(), is("/a/b/acme2:c"));
-    }
-
-    @Test
-    public void shouldProvideValue() throws Exception {
-        Value val = prop.getValue();
-        assertThat(val, notNullValue());
-    }
-
-    @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideValues() throws Exception {
-        prop.getValues();
-    }
-
-    @Test
-    public void shouldProvideLength() throws Exception {
-        String dnaValue = executionContext.getValueFactories().getStringFactory().create(dnaProperty.getFirstValue());
-        assertThat(prop.getLength(), is((long)dnaValue.length()));
-
-        dnaValue = "some other value";
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getLength(), is((long)dnaValue.length()));
-
-        Object obj = new Object();
-        long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
-        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
-        prop = new JcrProperty(node, executionContext, definition, dnaProperty);
-        assertThat(prop.getLength(), is(binaryLength));
-    }
-
-    @Test( expected = ValueFormatException.class )
-    public void shouldNotProvideLengths() throws Exception {
-        prop.getLengths();
-    }
-
-    @Test
-    public void shouldProvidePropertyDefinition() throws Exception {
-        assertThat(prop.getDefinition(), notNullValue());
-    }
-
-}

Copied: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java (from rev 752, trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyTest.java)
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-04 19:11:34 UTC (rev 753)
@@ -0,0 +1,257 @@
+/*
+ * 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.mock;
+import static org.mockito.Mockito.stub;
+import java.io.InputStream;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.PropertyDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author jverhaeg
+ */
+public class JcrSingleValuePropertyTest {
+
+    private Property prop;
+    @Mock
+    private Node node;
+    private ExecutionContext executionContext;
+    @Mock
+    Name name;
+    @Mock
+    private PropertyDefinition definition;
+    private org.jboss.dna.graph.property.Property dnaProperty;
+
+    @Before
+    public void before() {
+        MockitoAnnotations.initMocks(this);
+        executionContext = new ExecutionContext();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        stub(definition.isMultiple()).toReturn(false);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+    }
+
+    @Test
+    public void shouldProvideBoolean() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
+        stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getBoolean(), is(true));
+        assertThat(prop.getType(), is(PropertyType.BOOLEAN));
+    }
+
+    @Test
+    public void shouldIndicateHasSingleValue() throws Exception {
+        PropertyDefinition def = prop.getDefinition();
+        assertThat(def, notNullValue());
+        assertThat(def.isMultiple(), is(false));
+    }
+
+    @Test
+    public void shouldProvideDate() throws Exception {
+        DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DATE);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getDate(), is(dnaDate.toCalendar()));
+        assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
+        assertThat(prop.getType(), is(PropertyType.DATE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldProvideNode() throws Exception {
+        UUID referencedUuid = UUID.randomUUID();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
+        stub(definition.getRequiredType()).toReturn(PropertyType.REFERENCE);
+        Node referencedNode = mock(Node.class);
+        JcrSession session = mock(JcrSession.class);
+        stub(node.getSession()).toReturn(session);
+        stub(session.getNode(referencedUuid)).toReturn(referencedNode);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getNode(), is(referencedNode));
+        assertThat(prop.getType(), is(PropertyType.REFERENCE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldProvideDouble() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getDouble(), is(1.0));
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getDouble(), is(1.0));
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldProvideLong() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLong(), is(1L));
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
+        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLong(), is(1L));
+        assertThat(prop.getString(), is("1"));
+        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldProvideStream() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
+        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getType(), is(PropertyType.BINARY));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        InputStream stream = prop.getStream();
+        try {
+            assertThat(stream, notNullValue());
+        } finally {
+            if (stream != null) {
+                stream.close();
+            }
+        }
+    }
+
+    @Test
+    public void shouldProvideString() throws Exception {
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is("value"));
+        assertThat(prop.getType(), is(PropertyType.STRING));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldAllowReferenceValue() throws Exception {
+        UUID uuid = UUID.randomUUID();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is(uuid.toString()));
+        assertThat(prop.getType(), is(PropertyType.STRING));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+    }
+
+    @Test
+    public void shouldAllowNameValue() throws Exception {
+        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
+        Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
+        stub(definition.getRequiredType()).toReturn(PropertyType.NAME);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is("acme:something"));
+        assertThat(prop.getType(), is(PropertyType.NAME));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        // Change the namespace registry ...
+        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
+        assertThat(prop.getString(), is("acme2:something"));
+    }
+
+    @Test
+    public void shouldAllowPathValue() throws Exception {
+        executionContext.getNamespaceRegistry().register("acme", "http://example.com");
+        Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
+        stub(definition.getRequiredType()).toReturn(PropertyType.PATH);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getString(), is("/a/b/acme:c"));
+        assertThat(prop.getType(), is(PropertyType.PATH));
+        assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
+        // Change the namespace registry ...
+        executionContext.getNamespaceRegistry().register("acme2", "http://example.com");
+        assertThat(prop.getString(), is("/a/b/acme2:c"));
+    }
+
+    @Test
+    public void shouldProvideValue() throws Exception {
+        Value val = prop.getValue();
+        assertThat(val, notNullValue());
+    }
+
+    @Test( expected = ValueFormatException.class )
+    public void shouldNotProvideValues() throws Exception {
+        prop.getValues();
+    }
+
+    @Test
+    public void shouldProvideLength() throws Exception {
+        String dnaValue = executionContext.getValueFactories().getStringFactory().create(dnaProperty.getFirstValue());
+        assertThat(prop.getLength(), is((long)dnaValue.length()));
+
+        dnaValue = "some other value";
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLength(), is((long)dnaValue.length()));
+
+        Object obj = new Object();
+        long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
+        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
+        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        assertThat(prop.getLength(), is(binaryLength));
+    }
+
+    @Test( expected = ValueFormatException.class )
+    public void shouldNotProvideLengths() throws Exception {
+        prop.getLengths();
+    }
+
+    @Test
+    public void shouldProvidePropertyDefinition() throws Exception {
+        assertThat(prop.getDefinition(), notNullValue());
+    }
+
+}


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



From dna-commits at lists.jboss.org  Wed Mar  4 14:40:03 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 04 Mar 2009 14:40:03 -0500
Subject: [dna-commits] DNA SVN: r754 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lewwl-0003i0-6b@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-04 14:40:01 -0500 (Wed, 04 Mar 2009)
New Revision: 754

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
Log:
DNA-291 JCR property objects do not have PropertyDefinition

Changed the JcrSession.populateNode(...) method to look for "*" property definitions in the primary type or mixins' NodeType, and to use that for properties for which there is no explicit property defintion.

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-03-04 19:11:34 UTC (rev 753)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-04 19:40:01 UTC (rev 754)
@@ -31,6 +31,9 @@
 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;
@@ -79,6 +82,12 @@
 @NotThreadSafe
 class JcrSession implements Session {
 
+    /**
+     * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be
+     * included anyway. This is currently {@value} .
+     */
+    private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
+
     private static final String[] NO_ATTRIBUTES_NAMES = new String[] {};
 
     /**
@@ -659,14 +668,21 @@
         ValueFactories factories = executionContext.getValueFactories();
         NameFactory nameFactory = factories.getNameFactory();
         NodeTypeManager nodeTypeManager = getWorkspace().getNodeTypeManager();
+        List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
         org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
         if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
             Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
             String primaryTypeNameString = primaryTypeName.getString(registry);
             NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
             for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
-                Name name = nameFactory.create(propertyDefn.getName());
-                propertyDefinitionsByPropertyName.put(name, propertyDefn);
+                String nameString = propertyDefn.getName();
+                if ("*".equals(nameString)) {
+                    anyPropertyDefinitions.add(propertyDefn);
+                    continue;
+                }
+                Name name = nameFactory.create(nameString);
+                PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
+                if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
             }
         }
         org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
@@ -677,8 +693,14 @@
                 String mixinTypeNameString = mixinTypeName.getString(registry);
                 NodeType primaryType = nodeTypeManager.getNodeType(mixinTypeNameString);
                 for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
-                    Name name = nameFactory.create(propertyDefn.getName());
-                    propertyDefinitionsByPropertyName.put(name, propertyDefn);
+                    String nameString = propertyDefn.getName();
+                    if ("*".equals(nameString)) {
+                        anyPropertyDefinitions.add(propertyDefn);
+                        continue;
+                    }
+                    Name name = nameFactory.create(nameString);
+                    PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
+                    if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
                 }
             }
         }
@@ -736,21 +758,45 @@
             // Figure out the JCR property type for this property ...
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(name);
 
-            // If no property type was specified, then use "nt:unstructured" property definitions for any property
+            // If no property type was found for this property, see if there is a wildcard property ...
+            if (propertyDefinition == null && !anyPropertyDefinitions.isEmpty()) {
+                Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator();
+                propertyDefinition = iter.next(); // use the first one by default ...
+                if (iter.hasNext()) {
+                    // Look for a better one if not multiple but the property has multiple values ...
+                    if (!propertyDefinition.isMultiple() && dnaProp.isMultiple()) {
+                        while (iter.hasNext()) {
+                            PropertyDefinition next = iter.next();
+                            if (next.isMultiple()) {
+                                propertyDefinition = next;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            // If there still is no property type defined ...
             if (propertyDefinition == null) {
-                if (anyMultiplePropertyDefinition == null) {
-                    String unstructuredName = JcrNtLexicon.UNSTRUCTURED.getString(registry);
-                    NodeType unstructured = nodeTypeManager.getNodeType(unstructuredName);
-                    for (PropertyDefinition definition : unstructured.getDeclaredPropertyDefinitions()) {
-                        if (definition.isMultiple()) {
-                            anyMultiplePropertyDefinition = definition;
+                assert anyPropertyDefinitions.isEmpty();
+                if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+                    // We can use the "nt:unstructured" property definitions for any property ...
+                    if (anyMultiplePropertyDefinition == null) {
+                        String unstructuredName = JcrNtLexicon.UNSTRUCTURED.getString(registry);
+                        NodeType unstructured = nodeTypeManager.getNodeType(unstructuredName);
+                        for (PropertyDefinition definition : unstructured.getDeclaredPropertyDefinitions()) {
+                            if (definition.isMultiple()) {
+                                anyMultiplePropertyDefinition = definition;
+                            }
                         }
                     }
+                    propertyDefinition = anyMultiplePropertyDefinition;
                 }
-                assert anyMultiplePropertyDefinition != null;
-                propertyDefinition = anyMultiplePropertyDefinition;
             }
-            assert propertyDefinition != null;
+            if (propertyDefinition == null) {
+                // We're supposed to skip this property (since we don't have a definition for it) ...
+                continue;
+            }
 
             // Figure out if this is a multi-valued property ...
             boolean isMultiple = propertyDefinition.isMultiple();



From dna-commits at lists.jboss.org  Wed Mar  4 16:58:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 04 Mar 2009 16:58:43 -0500
Subject: [dna-commits] DNA SVN: r755 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lez6x-0004kv-Ro@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-04 16:58:43 -0500 (Wed, 04 Mar 2009)
New Revision: 755

Modified:
   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/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/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
Log:
DNA-293 JcrSession creates Property instances that use the PropertyType.UNDEFINED, which is not allowed

Changed JcrSession.populateNode(...) to never use PropertyType.UNDEFINED for the actual property type on a property.  This means that even though UNDEFINED is allows on PropertyDefinition, it is not allowed on an actual Property instance.  Therefore, I added the actual property type as an int field to JcrAbstractProperty, and modified the subclasses and the code where the constructors are called (mostly JcrSession and unit tests).

The current choice is to use PropertyType.STRING, since all property values can be converted to a String.  See the issue for more details and a discussion of this logic.

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -47,10 +47,12 @@
     private final ExecutionContext executionContext;
     private final org.jboss.dna.graph.property.Property dnaProperty;
     private final PropertyDefinition jcrPropertyDefinition;
+    private final int propertyType;
 
     AbstractJcrProperty( Node node,
                          ExecutionContext executionContext,
                          PropertyDefinition definition,
+                         int propertyType,
                          org.jboss.dna.graph.property.Property dnaProperty ) {
         assert node != null;
         assert executionContext != null;
@@ -60,6 +62,7 @@
         this.executionContext = executionContext;
         this.dnaProperty = dnaProperty;
         this.jcrPropertyDefinition = definition;
+        this.propertyType = propertyType;
     }
 
     /**
@@ -91,7 +94,7 @@
      * @see javax.jcr.Property#getType()
      */
     public final int getType() {
-        return jcrPropertyDefinition.getRequiredType();
+        return propertyType;
     }
 
     /**

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -44,8 +44,9 @@
     JcrMultiValueProperty( Node node,
                            ExecutionContext executionContext,
                            PropertyDefinition definition,
+                           int propertyType,
                            Property dnaProperty ) {
-        super(node, executionContext, definition, dnaProperty);
+        super(node, executionContext, definition, propertyType, dnaProperty);
     }
 
     /**

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -633,7 +633,7 @@
             case DATE:
                 return PropertyType.DATE;
             case DECIMAL:
-                return PropertyType.UNDEFINED;
+                return PropertyType.STRING; // better than losing information
             case DOUBLE:
                 return PropertyType.DOUBLE;
             case BINARY:
@@ -745,7 +745,8 @@
         if (referenceable) {
             if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
-            properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, uuidProperty));
+            properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, PropertyType.STRING,
+                                                      uuidProperty));
         }
 
         // Now create the JCR property object wrappers around the other properties ...
@@ -805,11 +806,26 @@
                 continue;
             }
 
+            // Figure out the property type ...
+            int propertyType = propertyDefinition.getRequiredType();
+            if (propertyType == PropertyType.UNDEFINED) {
+                // See DNA-293 for discussion. Best option is to use PropertyType.STRING, since values can always
+                // be converted to Strings (and because clients might use Property.getType() to decide how to
+                // manipulate or set values).
+                propertyType = PropertyType.STRING;
+
+                // // Or, we can choose the property type for the first value (or the default value, if there is one) ...
+                // Object value = dnaProp.getFirstValue();
+                // org.jboss.dna.graph.property.PropertyType dnaType =
+                // org.jboss.dna.graph.property.PropertyType.discoverType(value);
+                // propertyType = jcrPropertyTypeFor(dnaType);
+            }
+
             // Create the appropriate JCR property wrapper ...
             if (isMultiple) {
-                properties.add(new JcrMultiValueProperty(node, executionContext, propertyDefinition, dnaProp));
+                properties.add(new JcrMultiValueProperty(node, executionContext, propertyDefinition, propertyType, dnaProp));
             } else {
-                properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, dnaProp));
+                properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, propertyType, dnaProp));
             }
         }
 

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -43,10 +43,11 @@
 final class JcrSingleValueProperty extends AbstractJcrProperty {
 
     JcrSingleValueProperty( Node node,
-                 ExecutionContext executionContext,
-                 PropertyDefinition definition,
-                 Property dnaProperty ) {
-        super(node, executionContext, definition, dnaProperty);
+                            ExecutionContext executionContext,
+                            PropertyDefinition definition,
+                            int propertyType,
+                            Property dnaProperty ) {
+        super(node, executionContext, definition, propertyType, dnaProperty);
     }
 
     /**

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -49,8 +49,7 @@
         assert valueFactories != null;
         assert type == PropertyType.BINARY || type == PropertyType.BOOLEAN || type == PropertyType.DATE
                || type == PropertyType.DOUBLE || type == PropertyType.LONG || type == PropertyType.NAME
-               || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING
-               || type == PropertyType.UNDEFINED;
+               || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING;
         assert value != null;
         this.valueFactories = valueFactories;
         this.type = type;

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-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -234,7 +234,7 @@
                                  ExecutionContext executionContext,
                                  PropertyDefinition propertyDefinition,
                                  org.jboss.dna.graph.property.Property dnaProperty ) {
-            super(node, executionContext, propertyDefinition, dnaProperty);
+            super(node, executionContext, propertyDefinition, propertyDefinition.getRequiredType(), dnaProperty);
         }
 
         /**

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -85,7 +85,7 @@
             addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -59,7 +59,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
         stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
     }
 
     @Test
@@ -138,7 +138,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -149,7 +149,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -159,7 +159,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object[])values);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(values.length));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-04 19:40:01 UTC (rev 754)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-04 21:58:43 UTC (rev 755)
@@ -65,16 +65,14 @@
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(false);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
     }
 
     @Test
     public void shouldProvideBoolean() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
-        stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BOOLEAN, dnaProperty);
         assertThat(prop.getBoolean(), is(true));
         assertThat(prop.getType(), is(PropertyType.BOOLEAN));
     }
@@ -90,8 +88,7 @@
     public void shouldProvideDate() throws Exception {
         DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DATE);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DATE, dnaProperty);
         assertThat(prop.getDate(), is(dnaDate.toCalendar()));
         assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
         assertThat(prop.getType(), is(PropertyType.DATE));
@@ -102,12 +99,11 @@
     public void shouldProvideNode() throws Exception {
         UUID referencedUuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
-        stub(definition.getRequiredType()).toReturn(PropertyType.REFERENCE);
         Node referencedNode = mock(Node.class);
         JcrSession session = mock(JcrSession.class);
         stub(node.getSession()).toReturn(session);
         stub(session.getNode(referencedUuid)).toReturn(referencedNode);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.REFERENCE, dnaProperty);
         assertThat(prop.getNode(), is(referencedNode));
         assertThat(prop.getType(), is(PropertyType.REFERENCE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -116,13 +112,12 @@
     @Test
     public void shouldProvideDouble() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -131,15 +126,13 @@
     @Test
     public void shouldProvideLong() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getLong(), is(1L));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
 
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
-        stub(definition.getRequiredType()).toReturn(PropertyType.DOUBLE);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getLong(), is(1L));
         assertThat(prop.getString(), is("1"));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
@@ -149,8 +142,7 @@
     @Test
     public void shouldProvideStream() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
-        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BINARY, dnaProperty);
         assertThat(prop.getType(), is(PropertyType.BINARY));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         InputStream stream = prop.getStream();
@@ -166,8 +158,7 @@
     @Test
     public void shouldProvideString() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getString(), is("value"));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -177,8 +168,7 @@
     public void shouldAllowReferenceValue() throws Exception {
         UUID uuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getString(), is(uuid.toString()));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -189,8 +179,7 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
-        stub(definition.getRequiredType()).toReturn(PropertyType.NAME);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.NAME, dnaProperty);
         assertThat(prop.getString(), is("acme:something"));
         assertThat(prop.getType(), is(PropertyType.NAME));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -204,8 +193,7 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
-        stub(definition.getRequiredType()).toReturn(PropertyType.PATH);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.PATH, dnaProperty);
         assertThat(prop.getString(), is("/a/b/acme:c"));
         assertThat(prop.getType(), is(PropertyType.PATH));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -232,15 +220,13 @@
 
         dnaValue = "some other value";
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
-        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getLength(), is((long)dnaValue.length()));
 
         Object obj = new Object();
         long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
-        stub(definition.getRequiredType()).toReturn(PropertyType.BINARY);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, dnaProperty);
+        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BINARY, dnaProperty);
         assertThat(prop.getLength(), is(binaryLength));
     }
 



From dna-commits at lists.jboss.org  Wed Mar  4 17:18:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 04 Mar 2009 17:18:46 -0500
Subject: [dna-commits] DNA SVN: r756 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LezQM-0001vy-Dj@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-04 17:18:45 -0500 (Wed, 04 Mar 2009)
New Revision: 756

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
Log:
DNA-293 JcrSession creates Property instances that use the PropertyType.UNDEFINED, which is not allowed

Changed how JcrSession.populateNode(...) determines the PropertyType for an existing DNA Property instance.  It is now based upon the first value, since the Java type for that value should be based upon the PropertyType specified when creating or updating the JCR property.

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-03-04 21:58:43 UTC (rev 755)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-04 22:18:45 UTC (rev 756)
@@ -609,13 +609,20 @@
 
     /**
      * Compute the JCR {@link PropertyType} for the given DNA {@link org.jboss.dna.graph.property.PropertyType}.
+     * <p>
+     * See DNA-293 for complete discussion on why this method works the way it does. The best option appears to be basing the
+     * PropertyType on the first value, since that should be compatible with the PropertyType that was used when the values were
+     * set on the property in the first place.
+     * </p>
      * 
-     * @param dnaPropertyType the DNA property type; never null
-     * @return the JCR property type
+     * @param property the DNA property for which the {@link PropertyType} is to be determined; never null
+     * @return the JCR property type; always a valid value and never {@link PropertyType#UNDEFINED}.
      */
-    static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.PropertyType dnaPropertyType ) {
+    static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.Property property ) {
+        Object value = property.getFirstValue();
+
         // Get the DNA property type for this ...
-        switch (dnaPropertyType) {
+        switch (org.jboss.dna.graph.property.PropertyType.discoverType(value)) {
             case STRING:
                 return PropertyType.STRING;
             case NAME:
@@ -809,16 +816,7 @@
             // Figure out the property type ...
             int propertyType = propertyDefinition.getRequiredType();
             if (propertyType == PropertyType.UNDEFINED) {
-                // See DNA-293 for discussion. Best option is to use PropertyType.STRING, since values can always
-                // be converted to Strings (and because clients might use Property.getType() to decide how to
-                // manipulate or set values).
-                propertyType = PropertyType.STRING;
-
-                // // Or, we can choose the property type for the first value (or the default value, if there is one) ...
-                // Object value = dnaProp.getFirstValue();
-                // org.jboss.dna.graph.property.PropertyType dnaType =
-                // org.jboss.dna.graph.property.PropertyType.discoverType(value);
-                // propertyType = jcrPropertyTypeFor(dnaType);
+                propertyType = jcrPropertyTypeFor(dnaProp);
             }
 
             // Create the appropriate JCR property wrapper ...



From dna-commits at lists.jboss.org  Thu Mar  5 09:55:09 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 05 Mar 2009 09:55:09 -0500
Subject: [dna-commits] DNA SVN: r757 - in trunk/dna-jcr/src/test: resources
	and 1 other directory.
Message-ID: <E1LfEyb-00023s-Sd@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-05 09:55:09 -0500 (Thu, 05 Mar 2009)
New Revision: 757

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Removed:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
Modified:
   trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-194 Implement update JCR capability

Refactored the TCK unit tests to make it easy to add Level 2 and Optional unit tests from the Jackrabbit TCK unit tests.

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-04 22:18:45 UTC (rev 756)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java	2009-03-05 14:55:09 UTC (rev 757)
@@ -1,229 +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.Properties;
-import javax.jcr.Credentials;
-import javax.jcr.Repository;
-import junit.framework.Test;
-import junit.framework.TestSuite;
-import org.apache.jackrabbit.test.JCRTestSuite;
-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.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.property.Path;
-
-/**
- * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) tests.
- */
-public class JackrabbitJcrTckTest {
-
-    /**
-     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
-     * 
-     * @return a new instance of {@link JCRTestSuite}.
-     */
-    public static Test suite() {
-        // Uncomment this to execute all tests
-        // return new JCRTestSuite();
-
-        // Uncomment this to execute level one tests only
-        return new JcrLevelOneTestSuite();
-    }
-
-    /** Test suite that includes all of the Jackrabbit TCK API tests <b>except</b> the level two tests. */
-    private static class JcrLevelOneTestSuite extends TestSuite {
-        public JcrLevelOneTestSuite() {
-            super("JCR 1.0 Level 1 API tests");
-
-            // We currently don't pass the tests in those suites that are commented out
-            // See https://jira.jboss.org/jira/browse/DNA-285
-
-            addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
-            addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
-
-            // These might not all be level one tests
-            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
-
-            // The tests in this suite are level one
-            // addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
-        }
-
-    }
-
-    /**
-     * Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
-     */
-    public static 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
-            connection = source.getConnection();
-            repository = new JcrRepository(Collections.<String, String>emptyMap(), executionContext.create(accessControlContext),
-                                           connectionFactory, source.getName());
-
-            // 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("sv", "http://www.jcp.org/jcr/sv/1.0");
-
-                Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
-                GraphImporter importer = new GraphImporter(graph);
-
-                URI xmlContent = new File("src/test/resources/repositoryJackrabbitTck.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;
-        }
-
-    }
-
-}

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
@@ -0,0 +1,95 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes the Level 1 JCR TCK API tests from the Jackrabbit project.
+ * 
+ * @see JcrTckLevelTwoTestSuite
+ * @see JcrTckOptionalTestSuite
+ * @see JcrTckTest
+ */
+public class JcrTckLevelOneTestSuite extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this package.
+     * 
+     * @return a <code>Test</code> suite that executes all tests inside this package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Level 1 API tests");
+
+        // We currently don't pass the tests in those suites that are commented out
+        // See https://jira.jboss.org/jira/browse/DNA-285
+
+        suite.addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
+        suite.addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
+
+        // These might not all be level one tests
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
+        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
+
+        // The tests in this suite are level one
+        // suite.addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
+
+        return suite;
+    }
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
@@ -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.
+ *
+ * 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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.CheckPermissionTest;
+import org.apache.jackrabbit.test.api.DocumentViewImportTest;
+import org.apache.jackrabbit.test.api.ImpersonateTest;
+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.NodeOrderableChildNodesTest;
+import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.NodeTest;
+import org.apache.jackrabbit.test.api.NodeUUIDTest;
+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.ReferencesTest;
+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.SetPropertyAssumeTypeTest;
+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.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;
+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.SetValueStringTest;
+import org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest;
+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.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
+
+/**
+ * Test suite that includes the Level 2 JCR TCK API tests from the Jackrabbit project.
+ * 
+ * @see JcrTckLevelOneTestSuite
+ * @see JcrTckOptionalTestSuite
+ * @see JcrTckTest
+ */
+public class JcrTckLevelTwoTestSuite extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this package.
+     * 
+     * @return a <code>Test</code> suite that executes all tests inside this package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Level 2 API tests");
+
+        // We currently don't pass the tests in those suites that are commented out
+        // See https://jira.jboss.org/jira/browse/DNA-285
+
+        // level 2 tests
+        suite.addTestSuite(AddNodeTest.class);
+        suite.addTestSuite(NamespaceRegistryTest.class);
+        suite.addTestSuite(ReferencesTest.class);
+        suite.addTestSuite(SessionTest.class);
+        suite.addTestSuite(SessionUUIDTest.class);
+        suite.addTestSuite(NodeTest.class);
+        suite.addTestSuite(NodeUUIDTest.class);
+        suite.addTestSuite(NodeOrderableChildNodesTest.class);
+        suite.addTestSuite(PropertyTest.class);
+
+        suite.addTestSuite(SetValueBinaryTest.class);
+        suite.addTestSuite(SetValueBooleanTest.class);
+        suite.addTestSuite(SetValueDateTest.class);
+        suite.addTestSuite(SetValueDoubleTest.class);
+        suite.addTestSuite(SetValueLongTest.class);
+        suite.addTestSuite(SetValueReferenceTest.class);
+        suite.addTestSuite(SetValueStringTest.class);
+        suite.addTestSuite(SetValueConstraintViolationExceptionTest.class);
+        suite.addTestSuite(SetValueValueFormatExceptionTest.class);
+        suite.addTestSuite(SetValueVersionExceptionTest.class);
+
+        suite.addTestSuite(SetPropertyBooleanTest.class);
+        suite.addTestSuite(SetPropertyCalendarTest.class);
+        suite.addTestSuite(SetPropertyDoubleTest.class);
+        suite.addTestSuite(SetPropertyInputStreamTest.class);
+        suite.addTestSuite(SetPropertyLongTest.class);
+        suite.addTestSuite(SetPropertyNodeTest.class);
+        suite.addTestSuite(SetPropertyStringTest.class);
+        suite.addTestSuite(SetPropertyValueTest.class);
+        suite.addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
+        suite.addTestSuite(SetPropertyAssumeTypeTest.class);
+
+        suite.addTestSuite(NodeItemIsModifiedTest.class);
+        suite.addTestSuite(NodeItemIsNewTest.class);
+        suite.addTestSuite(PropertyItemIsModifiedTest.class);
+        suite.addTestSuite(PropertyItemIsNewTest.class);
+
+        suite.addTestSuite(NodeAddMixinTest.class);
+        suite.addTestSuite(NodeCanAddMixinTest.class);
+        suite.addTestSuite(NodeRemoveMixinTest.class);
+
+        suite.addTestSuite(WorkspaceCloneReferenceableTest.class);
+        suite.addTestSuite(WorkspaceCloneSameNameSibsTest.class);
+        suite.addTestSuite(WorkspaceCloneTest.class);
+        suite.addTestSuite(WorkspaceCloneVersionableTest.class);
+        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
+        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
+        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
+        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
+        suite.addTestSuite(WorkspaceCopyReferenceableTest.class);
+        suite.addTestSuite(WorkspaceCopySameNameSibsTest.class);
+        suite.addTestSuite(WorkspaceCopyTest.class);
+        suite.addTestSuite(WorkspaceCopyVersionableTest.class);
+        suite.addTestSuite(WorkspaceMoveReferenceableTest.class);
+        suite.addTestSuite(WorkspaceMoveSameNameSibsTest.class);
+        suite.addTestSuite(WorkspaceMoveTest.class);
+        suite.addTestSuite(WorkspaceMoveVersionableTest.class);
+
+        suite.addTestSuite(RepositoryLoginTest.class);
+        suite.addTestSuite(ImpersonateTest.class);
+        suite.addTestSuite(CheckPermissionTest.class);
+
+        suite.addTestSuite(DocumentViewImportTest.class);
+        suite.addTestSuite(SerializationTest.class);
+
+        suite.addTestSuite(ValueFactoryTest.class);
+
+        return suite;
+    }
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
@@ -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.
+ *
+ * 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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes the Optional JCR TCK API tests from the Jackrabbit project.
+ * 
+ * @see JcrTckLevelOneTestSuite
+ * @see JcrTckLevelTwoTestSuite
+ * @see JcrTckTest
+ */
+public class JcrTckOptionalTestSuite extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this package.
+     * 
+     * @return a <code>Test</code> suite that executes all tests inside this package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Optional API tests");
+
+        // We currently don't pass the tests in those suites that are commented out
+        // See https://jira.jboss.org/jira/browse/DNA-285
+
+        suite.addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
+        suite.addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
+        suite.addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
+        suite.addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
+
+        return suite;
+    }
+}


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

Copied: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java (from rev 756, trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java)
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-05 14:55:09 UTC (rev 757)
@@ -0,0 +1,184 @@
+/*
+ * 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.Properties;
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+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.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.property.Path;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests.
+ * 
+ * @see JcrTckLevelOneTestSuite
+ * @see JcrTckLevelTwoTestSuite
+ * @see JcrTckOptionalTestSuite
+ */
+public class JcrTckTest {
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        // Uncomment this to execute all tests
+        // return new JCRTestSuite();
+
+        // Or uncomment the following lines to execute the different sets/suites of tests ...
+        TestSuite suite = new TestSuite("JCR 1.0 API tests");
+        suite.addTest(JcrTckLevelOneTestSuite.suite());
+        // suite.addTest(JcrTckLevelTwoTestSuite.suite());
+        // suite.addTest(JcrTckOptionalTestSuite.suite());
+
+        return suite;
+    }
+
+    /**
+     * Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
+     */
+    public static 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
+            connection = source.getConnection();
+            repository = new JcrRepository(Collections.<String, String>emptyMap(), executionContext.create(accessControlContext),
+                                           connectionFactory, source.getName());
+
+            // 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("sv", "http://www.jcp.org/jcr/sv/1.0");
+
+                Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
+                GraphImporter importer = new GraphImporter(graph);
+
+                URI xmlContent = new File("src/test/resources/repositoryJackrabbitTck.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;
+        }
+
+    }
+
+}


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

Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-03-04 22:18:45 UTC (rev 756)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties	2009-03-05 14:55:09 UTC (rev 757)
@@ -1,4 +1,4 @@
-javax.jcr.tck.repository_stub_impl=org.jboss.dna.jcr.JackrabbitJcrTckTest$InMemoryRepositoryStub
+javax.jcr.tck.repository_stub_impl=org.jboss.dna.jcr.JcrTckTest$InMemoryRepositoryStub
 javax.jcr.tck.testroot=/testroot
 javax.jcr.tck.nodename1=node1
 javax.jcr.tck.nodename2=node2



From dna-commits at lists.jboss.org  Thu Mar  5 13:02:40 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 05 Mar 2009 13:02:40 -0500
Subject: [dna-commits] DNA SVN: r758 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LfHu4-0001gl-Br@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-05 13:02:40 -0500 (Thu, 05 Mar 2009)
New Revision: 758

Removed:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.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/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-194 Implement update JCR capability

Refactored the TCK unit tests again to make them easier to run/understand.  Also improved the implementation of some JCR API methods, and reorganized the methods in JcrAbstractNode.


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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -24,11 +24,11 @@
 package org.jboss.dna.jcr;
 
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Calendar;
 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.regex.Pattern;
@@ -48,11 +48,11 @@
 import javax.jcr.lock.Lock;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionHistory;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
@@ -65,8 +65,10 @@
 @NotThreadSafe
 abstract class AbstractJcrNode extends AbstractJcrItem implements Node {
 
+    private static final NodeType[] EMPTY_NODE_TYPES = new NodeType[] {};
+
     private final JcrSession session;
-    Set<Property> properties;
+    Map<Name, Property> properties;
     List<Path.Segment> children;
     private UUID uuid;
 
@@ -78,137 +80,113 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws IllegalArgumentException if <code>visitor</code> is <code>null</code>.
-     * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor)
+     * @see javax.jcr.Item#getSession()
      */
-    public final void accept( ItemVisitor visitor ) throws RepositoryException {
-        CheckArg.isNotNull(visitor, "visitor");
-        visitor.visit(this);
+    public final Session getSession() {
+        return session;
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#addMixin(java.lang.String)
-     */
-    public final void addMixin( String mixinName ) {
-        throw new UnsupportedOperationException();
+    final UUID internalUuid() {
+        return uuid;
     }
 
     /**
-     * {@inheritDoc}
+     * Change the internal UUID. Technically, it is possible for this to change, but only because a new node may be assigned a
+     * UUID when it is created within the session and (according to the spec) this UUID can change when the new node is persisted
+     * upon {@link #save()}.
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#addNode(java.lang.String)
+     * @param uuid the new UUID; may not be null
      */
-    public final Node addNode( String relPath ) {
-        throw new UnsupportedOperationException();
+    final void setInternalUuid( UUID uuid ) {
+        assert uuid != null;
+        this.uuid = uuid;
     }
 
-    /**
-     * {@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();
+    final void setChildren( List<Segment> children ) {
+        assert children != null;
+        this.children = children;
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#canAddMixin(java.lang.String)
-     */
-    public final boolean canAddMixin( String mixinName ) {
-        return false;
+    final void setProperties( Map<Name, Property> properties ) {
+        assert properties != null;
+        this.properties = properties;
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#cancelMerge(javax.jcr.version.Version)
-     */
-    public final void cancelMerge( Version version ) {
-        throw new UnsupportedOperationException();
+    final Name nameFrom( String name ) {
+        return session.getExecutionContext().getValueFactories().getNameFactory().create(name);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#checkin()
+     * @see javax.jcr.Node#getUUID()
      */
-    public final Version checkin() {
-        throw new UnsupportedOperationException();
+    public final String getUUID() throws RepositoryException {
+        // Return JCR UUID only if node is referenceable
+        try {
+            Property mixinsProp = getProperty("jcr:mixinTypes");
+            if (mixinsProp != null) {
+                for (Value value : mixinsProp.getValues()) {
+                    if ("mix:referenceable".equals(value.getString())) return getProperty("jcr:uuid").getString();
+                }
+            }
+        } catch (PathNotFoundException error) {
+            throw new UnsupportedRepositoryOperationException(error);
+        }
+        throw new UnsupportedRepositoryOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#checkout()
+     * @return <code>true</code>
+     * @see javax.jcr.Item#isNode()
      */
-    public final void checkout() {
-        throw new UnsupportedOperationException();
+    public final boolean isNode() {
+        return true;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#doneMerge(javax.jcr.version.Version)
+     * @return <code>false</code>
+     * @see javax.jcr.Node#isNodeType(java.lang.String)
      */
-    public final void doneMerge( Version version ) {
-        throw new UnsupportedOperationException();
-    }
+    public boolean isNodeType( String nodeTypeName ) throws RepositoryException {
+        NodeType nodeType = getPrimaryNodeType();
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#getBaseVersion()
-     */
-    public final Version getBaseVersion() throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
+        if (nodeType.isNodeType(nodeTypeName)) {
+            return true;
+        }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#getCorrespondingNodePath(java.lang.String)
-     */
-    public final String getCorrespondingNodePath( String workspaceName ) {
-        throw new UnsupportedOperationException();
+        NodeType[] mixinNodeTypes = getMixinNodeTypes();
+        for (int i = 0; i < mixinNodeTypes.length; i++) {
+            if (mixinNodeTypes[i].isNodeType(nodeTypeName)) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#getDefinition()
      */
     public NodeDefinition getDefinition() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
-    final UUID getInternalUuid() {
-        return uuid;
-    }
-
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#getLock()
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#getPrimaryNodeType()
      */
-    public final Lock getLock() throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+    public NodeType getPrimaryNodeType() throws RepositoryException {
+        String nodeTypeName = getProperty(JcrLexicon.PRIMARY_TYPE).getString();
+        return session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
     }
 
     /**
@@ -218,85 +196,36 @@
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
     public NodeType[] getMixinNodeTypes() throws RepositoryException {
-        PropertyIterator mixinProperties = getProperties(JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext()
-                                                                                                 .getNamespaceRegistry()));
-        List<NodeType> mixinNodeTypes = new ArrayList<NodeType>((int)mixinProperties.getSize());
-
-        while (mixinProperties.hasNext()) {
-            Property property = mixinProperties.nextProperty();
-
-            String nodeTypeName = property.getValue().getString();
-            NodeType nodeType = session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
-
-            mixinNodeTypes.add(nodeType);
+        NodeTypeManager nodeTypeManager = session.getWorkspace().getNodeTypeManager();
+        Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
+        if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
+        List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();
+        for (Value value : mixinTypesProperty.getValues()) {
+            String nodeTypeName = value.getString();
+            NodeType nodeType = nodeTypeManager.getNodeType(nodeTypeName);
+            if (nodeType != null) mixinNodeTypes.add(nodeType);
         }
-
         return mixinNodeTypes.toArray(new NodeType[mixinNodeTypes.size()]);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
-     * @see javax.jcr.Node#getNode(java.lang.String)
+     * @return <code>false</code>
+     * @see javax.jcr.Node#canAddMixin(java.lang.String)
      */
-    public final Node getNode( String relativePath ) throws RepositoryException {
-        CheckArg.isNotEmpty(relativePath, "relativePath");
-        String path = getPath(getPath(), relativePath);
-        Item item = getSession().getItem(path);
-        if (item instanceof Node) {
-            return (Node)item;
-        }
-        throw new PathNotFoundException();
+    public final boolean canAddMixin( String mixinName ) {
+        return false;
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Node#getNodes()
-     */
-    public final NodeIterator getNodes() {
-        if (children == null) {
-            return new JcrEmptyNodeIterator();
-        }
-        return new JcrChildNodeIterator(this, this.session.getExecutionContext().getNamespaceRegistry(), children);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#getNodes(java.lang.String)
+     * @see javax.jcr.Node#removeMixin(java.lang.String)
      */
-    public NodeIterator getNodes( String namePattern ) throws RepositoryException {
-        CheckArg.isNotNull(namePattern, "namePattern");
-        namePattern = namePattern.trim();
-        if (namePattern.length() == 0) return new JcrEmptyNodeIterator();
-        if ("*".equals(namePattern)) return getNodes();
-        List<Object> patterns = createPatternsFor(namePattern);
-
-        // Implementing exact-matching only for now to prototype types as properties
-        List<Path.Segment> matchingChildren = new LinkedList<Path.Segment>();
-        NamespaceRegistry registry = session.executionContext.getNamespaceRegistry();
-        boolean foundMatch = false;
-        for (Path.Segment child : children) {
-            String childName = child.getName().getString(registry);
-            for (Object patternOrMatch : patterns) {
-                if (patternOrMatch instanceof Pattern) {
-                    Pattern pattern = (Pattern)patternOrMatch;
-                    if (pattern.matcher(childName).matches()) foundMatch = true;
-                } else {
-                    String match = (String)patternOrMatch;
-                    if (childName.equals(match)) foundMatch = true;
-                }
-                if (foundMatch) {
-                    foundMatch = false;
-                    matchingChildren.add(child);
-                    break;
-                }
-            }
-        }
-        return new JcrChildNodeIterator(this, this.session.executionContext.getNamespaceRegistry(), matchingChildren);
+    public final void removeMixin( String mixinName ) {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -317,19 +246,45 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#getPrimaryNodeType()
+     * @throws IllegalArgumentException if <code>otherItem</code> is <code>null</code>.
+     * @see javax.jcr.Item#isSame(javax.jcr.Item)
      */
-    public NodeType getPrimaryNodeType() throws RepositoryException {
-        String primaryTypeName = JcrLexicon.PRIMARY_TYPE.getString(session.getExecutionContext().getNamespaceRegistry());
-        Property primaryNodeTypeProperty = getProperty(primaryTypeName);
-        Value nodeValue = primaryNodeTypeProperty.getValue();
+    @Override
+    public final boolean isSame( Item otherItem ) throws RepositoryException {
+        CheckArg.isNotNull(otherItem, "otherItem");
+        if (super.isSame(otherItem) && otherItem instanceof Node) {
+            if (otherItem instanceof AbstractJcrNode) {
+                return internalUuid().equals(((AbstractJcrNode)otherItem).internalUuid());
+            }
+            // If not our implementation, let the other item figure out whether we are the same.
+            return otherItem.isSame(this);
+        }
+        return false;
+    }
 
-        ExecutionContext context = session.getExecutionContext();
-        Name nodeValueAsName = context.getValueFactories().getNameFactory().create(nodeValue.getString());
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Node#hasProperties()
+     */
+    public final boolean hasProperties() {
+        assert properties != null;
+        return !properties.isEmpty();
+    }
 
-        String nodeTypeName = nodeValueAsName.getString(context.getNamespaceRegistry());
-        return session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
+     * @see javax.jcr.Node#hasProperty(java.lang.String)
+     */
+    public final boolean hasProperty( String relativePath ) throws RepositoryException {
+        CheckArg.isNotEmpty(relativePath, "relativePath");
+        if (relativePath.indexOf('/') >= 0) {
+            return (getProperty(relativePath) != null);
+        }
+        assert properties != null;
+        return properties.containsKey(nameFrom(relativePath));
     }
 
     /**
@@ -338,7 +293,7 @@
      * @see javax.jcr.Node#getProperties()
      */
     public final PropertyIterator getProperties() {
-        return new JcrPropertyIterator(properties);
+        return new JcrPropertyIterator(properties.values());
     }
 
     /**
@@ -350,84 +305,40 @@
         CheckArg.isNotNull(namePattern, "namePattern");
         namePattern = namePattern.trim();
         if (namePattern.length() == 0) return new JcrEmptyPropertyIterator();
-        if ("*".equals(namePattern)) return new JcrPropertyIterator(properties);
+        if ("*".equals(namePattern)) return new JcrPropertyIterator(properties.values());
         List<Object> patterns = createPatternsFor(namePattern);
 
         // Implementing exact-matching only for now to prototype types as properties
         Set<Property> matchingProps = new HashSet<Property>();
-        for (Object patternOrMatch : patterns) {
-            if (patternOrMatch instanceof Pattern) {
-                Pattern pattern = (Pattern)patternOrMatch;
-                for (Property prop : properties) {
-                    String propName = prop.getName();
-                    if (pattern.matcher(propName).matches()) matchingProps.add(prop);
+        boolean foundMatch = false;
+        for (Property property : properties.values()) {
+            String propName = property.getName();
+            for (Object patternOrMatch : patterns) {
+                if (patternOrMatch instanceof Pattern) {
+                    Pattern pattern = (Pattern)patternOrMatch;
+                    if (pattern.matcher(propName).matches()) foundMatch = true;
+                } else {
+                    String match = (String)patternOrMatch;
+                    if (propName.equals(match)) foundMatch = true;
                 }
-            } else {
-                String match = (String)patternOrMatch;
-                for (Property prop : properties) {
-                    String propName = prop.getName();
-                    if (propName.equals(match)) matchingProps.add(prop);
+                if (foundMatch) {
+                    foundMatch = false;
+                    matchingProps.add(property);
+                    break;
                 }
             }
         }
         return new JcrPropertyIterator(matchingProps);
     }
 
-    protected static List<Object> createPatternsFor( String namePattern ) throws RepositoryException {
-        List<Object> patterns = new LinkedList<Object>();
-        for (String stringPattern : namePattern.split("[|]")) {
-            stringPattern = stringPattern.trim();
-            int length = stringPattern.length();
-            if (length == 0) continue;
-            if (stringPattern.indexOf("*") == -1) {
-                // Doesn't use wildcard, so use String not Pattern
-                patterns.add(stringPattern);
-            } else {
-                // We need to escape the regular expression characters ...
-                StringBuilder sb = new StringBuilder(length);
-                for (int i = 0; i != length; i++) {
-                    char c = stringPattern.charAt(i);
-                    switch (c) {
-                        // Per the spec, the the following characters are not allowed in patterns:
-                        case '/':
-                        case '[':
-                        case ']':
-                        case '\'':
-                        case '"':
-                        case '|':
-                        case '\t':
-                        case '\n':
-                        case '\r':
-                            String msg = JcrI18n.invalidNamePattern.text(c, namePattern);
-                            throw new RepositoryException(msg);
-                            // The following characters must be escaped when used in regular expressions ...
-                        case '?':
-                        case '(':
-                        case ')':
-                        case '$':
-                        case '^':
-                        case '.':
-                        case '{':
-                        case '}':
-                        case '\\':
-                            sb.append("\\");
-                            sb.append(c);
-                            break;
-                        case '*':
-                            // replace with the regular expression wildcard
-                            sb.append(".*");
-                            break;
-                        default:
-                            sb.append(c);
-                            break;
-                    }
-                }
-                String escapedString = sb.toString();
-                Pattern pattern = Pattern.compile(escapedString);
-                patterns.add(pattern);
-            }
-        }
-        return patterns;
+    /**
+     * A non-standard method to obtain a property given the {@link Name DNA Name} object. This method is faster
+     * 
+     * @param propertyName the property name
+     * @return the JCR property with the supplied name, or null if the property doesn't exist
+     */
+    public final Property getProperty( Name propertyName ) {
+        return properties.get(propertyName);
     }
 
     /**
@@ -450,9 +361,8 @@
             return ((Node)item).getParent().getProperty(item.getName());
         }
         assert properties != null;
-        for (Property property : properties) {
-            if (relativePath.equals(property.getName())) return property;
-        }
+        Property property = properties.get(nameFrom(relativePath));
+        if (property != null) return property;
         throw new PathNotFoundException();
     }
 
@@ -465,7 +375,7 @@
     public final PropertyIterator getReferences() throws RepositoryException {
         // Iterate through the properties to see which ones have a REFERENCE type ...
         Set<Property> references = new HashSet<Property>();
-        for (Property property : properties) {
+        for (Property property : properties.values()) {
             if (property.getType() == PropertyType.REFERENCE) references.add(property);
         }
         if (references.isEmpty()) return new JcrEmptyPropertyIterator();
@@ -475,45 +385,6 @@
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Item#getSession()
-     */
-    public final Session getSession() {
-        return session;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Node#getUUID()
-     */
-    public final String getUUID() throws RepositoryException {
-        // Return JCR UUID only if node is referenceable
-        try {
-            Property mixinsProp = getProperty("jcr:mixinTypes");
-            if (mixinsProp != null) {
-                for (Value value : mixinsProp.getValues()) {
-                    if ("mix:referenceable".equals(value.getString())) return getProperty("jcr:uuid").getString();
-                }
-            }
-        } catch (PathNotFoundException error) {
-            throw new UnsupportedRepositoryOperationException(error);
-        }
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#getVersionHistory()
-     */
-    public final VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
      * @see javax.jcr.Node#hasNode(java.lang.String)
      */
@@ -550,221 +421,123 @@
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Node#hasProperties()
-     */
-    public final boolean hasProperties() {
-        assert properties != null;
-        return !properties.isEmpty();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
-     * @see javax.jcr.Node#hasProperty(java.lang.String)
+     * @see javax.jcr.Node#getNode(java.lang.String)
      */
-    public final boolean hasProperty( String relativePath ) throws RepositoryException {
+    public final Node getNode( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
-        if (relativePath.indexOf('/') >= 0) {
-            return (getProperty(relativePath) != null);
+        String path = getPath(getPath(), relativePath);
+        Item item = getSession().getItem(path);
+        if (item instanceof Node) {
+            return (Node)item;
         }
-        assert properties != null;
-        for (Property property : properties) {
-            if (relativePath.equals(property.getName())) {
-                return true;
-            }
-        }
-        return false;
+        throw new PathNotFoundException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#holdsLock()
+     * @see javax.jcr.Node#getNodes()
      */
-    public final boolean holdsLock() {
-        return false;
+    public final NodeIterator getNodes() {
+        if (children == null) {
+            return new JcrEmptyNodeIterator();
+        }
+        return new JcrChildNodeIterator(this, this.session.getExecutionContext().getNamespaceRegistry(), children);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#isCheckedOut()
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#getNodes(java.lang.String)
      */
-    public final boolean isCheckedOut() {
-        return false;
-    }
+    public NodeIterator getNodes( String namePattern ) throws RepositoryException {
+        CheckArg.isNotNull(namePattern, "namePattern");
+        namePattern = namePattern.trim();
+        if (namePattern.length() == 0) return new JcrEmptyNodeIterator();
+        if ("*".equals(namePattern)) return getNodes();
+        List<Object> patterns = createPatternsFor(namePattern);
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#isLocked()
-     */
-    public final boolean isLocked() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @return <code>true</code>
-     * @see javax.jcr.Item#isNode()
-     */
-    public final boolean isNode() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @return <code>false</code>
-     * @see javax.jcr.Node#isNodeType(java.lang.String)
-     */
-    public boolean isNodeType( String nodeTypeName ) throws RepositoryException {
-        NodeType nodeType = getPrimaryNodeType();
-
-        if (nodeType.isNodeType(nodeTypeName)) {
-            return true;
-        }
-
-        NodeType[] mixinNodeTypes = getMixinNodeTypes();
-        for (int i = 0; i < mixinNodeTypes.length; i++) {
-            if (mixinNodeTypes[i].isNodeType(nodeTypeName)) {
-                return true;
+        // Implementing exact-matching only for now to prototype types as properties
+        List<Path.Segment> matchingChildren = new LinkedList<Path.Segment>();
+        NamespaceRegistry registry = session.executionContext.getNamespaceRegistry();
+        boolean foundMatch = false;
+        for (Path.Segment child : children) {
+            String childName = child.getName().getString(registry);
+            for (Object patternOrMatch : patterns) {
+                if (patternOrMatch instanceof Pattern) {
+                    Pattern pattern = (Pattern)patternOrMatch;
+                    if (pattern.matcher(childName).matches()) foundMatch = true;
+                } else {
+                    String match = (String)patternOrMatch;
+                    if (childName.equals(match)) foundMatch = true;
+                }
+                if (foundMatch) {
+                    foundMatch = false;
+                    matchingChildren.add(child);
+                    break;
+                }
             }
         }
-
-        return false;
+        return new JcrChildNodeIterator(this, this.session.executionContext.getNamespaceRegistry(), matchingChildren);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws IllegalArgumentException if <code>otherItem</code> is <code>null</code>.
-     * @see javax.jcr.Item#isSame(javax.jcr.Item)
+     * @throws IllegalArgumentException if <code>visitor</code> is <code>null</code>.
+     * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor)
      */
-    @Override
-    public final boolean isSame( Item otherItem ) throws RepositoryException {
-        CheckArg.isNotNull(otherItem, "otherItem");
-        if (super.isSame(otherItem) && otherItem instanceof Node) {
-            if (otherItem instanceof AbstractJcrNode) {
-                return getInternalUuid().equals(((AbstractJcrNode)otherItem).getInternalUuid());
-            }
-            // If not our implementation, let the other item figure out whether we are the same.
-            return otherItem.isSame(this);
-        }
-        return false;
+    public final void accept( ItemVisitor visitor ) throws RepositoryException {
+        CheckArg.isNotNull(visitor, "visitor");
+        visitor.visit(this);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#lock(boolean, boolean)
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#addMixin(java.lang.String)
      */
-    public final Lock lock( boolean isDeep,
-                            boolean isSessionScoped ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+    public final void addMixin( String mixinName ) {
+        throw new UnsupportedOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
      * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#merge(java.lang.String, boolean)
+     * @see javax.jcr.Node#addNode(java.lang.String)
      */
-    public final NodeIterator merge( String srcWorkspace,
-                                     boolean bestEffort ) {
+    public final Node addNode( String relPath ) {
         throw new UnsupportedOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#orderBefore(java.lang.String, java.lang.String)
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#addNode(java.lang.String, java.lang.String)
      */
-    public final void orderBefore( String srcChildRelPath,
-                                   String destChildRelPath ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+    public final Node addNode( String relPath,
+                               String primaryNodeTypeName ) {
+        throw new UnsupportedOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
      * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#removeMixin(java.lang.String)
+     * @see javax.jcr.Node#update(java.lang.String)
      */
-    public final void removeMixin( String mixinName ) {
+    public final void update( String srcWorkspaceName ) {
         throw new UnsupportedOperationException();
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#restore(java.lang.String, boolean)
-     */
-    public final void restore( String versionName,
-                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#restore(javax.jcr.version.Version, boolean)
-     */
-    public final void restore( Version version,
-                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#restore(javax.jcr.version.Version, java.lang.String, boolean)
-     */
-    public final void restore( Version version,
-                               String relPath,
-                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedRepositoryOperationException always
-     * @see javax.jcr.Node#restoreByLabel(java.lang.String, boolean)
-     */
-    public final void restoreByLabel( String versionLabel,
-                                      boolean removeExisting ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
-    }
-
-    final void setChildren( List<Segment> children ) {
-        assert children != null;
-        this.children = children;
-    }
-
-    final void setInternalUuid( UUID uuid ) {
-        assert uuid != null;
-        this.uuid = uuid;
-    }
-
-    final void setProperties( Set<Property> properties ) {
-        assert properties != null;
-        this.properties = properties;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws UnsupportedOperationException always
      * @see javax.jcr.Node#setProperty(java.lang.String, boolean)
      */
@@ -923,7 +696,68 @@
     /**
      * {@inheritDoc}
      * 
+     * @return <code>false</code>
+     * @see javax.jcr.Node#isCheckedOut()
+     */
+    public final boolean isCheckedOut() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#checkin()
+     */
+    public final Version checkin() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#checkout()
+     */
+    public final void checkout() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return <code>false</code>
+     * @see javax.jcr.Node#holdsLock()
+     */
+    public final boolean holdsLock() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return <code>false</code>
+     * @see javax.jcr.Node#isLocked()
+     */
+    public final boolean isLocked() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#lock(boolean, boolean)
+     */
+    public final Lock lock( boolean isDeep,
+                            boolean isSessionScoped ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#unlock()
      */
     public final void unlock() throws UnsupportedRepositoryOperationException {
@@ -933,10 +767,184 @@
     /**
      * {@inheritDoc}
      * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#getLock()
+     */
+    public final Lock getLock() throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#update(java.lang.String)
+     * @see javax.jcr.Node#merge(java.lang.String, boolean)
      */
-    public final void update( String srcWorkspaceName ) {
+    public final NodeIterator merge( String srcWorkspace,
+                                     boolean bestEffort ) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#cancelMerge(javax.jcr.version.Version)
+     */
+    public final void cancelMerge( Version version ) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#doneMerge(javax.jcr.version.Version)
+     */
+    public final void doneMerge( Version version ) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#getCorrespondingNodePath(java.lang.String)
+     */
+    public final String getCorrespondingNodePath( String workspaceName ) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#getVersionHistory()
+     */
+    public final VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#getBaseVersion()
+     */
+    public final Version getBaseVersion() throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#restore(java.lang.String, boolean)
+     */
+    public final void restore( String versionName,
+                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#restore(javax.jcr.version.Version, boolean)
+     */
+    public final void restore( Version version,
+                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#restore(javax.jcr.version.Version, java.lang.String, boolean)
+     */
+    public final void restore( Version version,
+                               String relPath,
+                               boolean removeExisting ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#restoreByLabel(java.lang.String, boolean)
+     */
+    public final void restoreByLabel( String versionLabel,
+                                      boolean removeExisting ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedRepositoryOperationException always
+     * @see javax.jcr.Node#orderBefore(java.lang.String, java.lang.String)
+     */
+    public final void orderBefore( String srcChildRelPath,
+                                   String destChildRelPath ) throws UnsupportedRepositoryOperationException {
+        throw new UnsupportedRepositoryOperationException();
+    }
+
+    protected static List<Object> createPatternsFor( String namePattern ) throws RepositoryException {
+        List<Object> patterns = new LinkedList<Object>();
+        for (String stringPattern : namePattern.split("[|]")) {
+            stringPattern = stringPattern.trim();
+            int length = stringPattern.length();
+            if (length == 0) continue;
+            if (stringPattern.indexOf("*") == -1) {
+                // Doesn't use wildcard, so use String not Pattern
+                patterns.add(stringPattern);
+            } else {
+                // We need to escape the regular expression characters ...
+                StringBuilder sb = new StringBuilder(length);
+                for (int i = 0; i != length; i++) {
+                    char c = stringPattern.charAt(i);
+                    switch (c) {
+                        // Per the spec, the the following characters are not allowed in patterns:
+                        case '/':
+                        case '[':
+                        case ']':
+                        case '\'':
+                        case '"':
+                        case '|':
+                        case '\t':
+                        case '\n':
+                        case '\r':
+                            String msg = JcrI18n.invalidNamePattern.text(c, namePattern);
+                            throw new RepositoryException(msg);
+                            // The following characters must be escaped when used in regular expressions ...
+                        case '?':
+                        case '(':
+                        case ')':
+                        case '$':
+                        case '^':
+                        case '.':
+                        case '{':
+                        case '}':
+                        case '\\':
+                            sb.append("\\");
+                            sb.append(c);
+                            break;
+                        case '*':
+                            // replace with the regular expression wildcard
+                            sb.append(".*");
+                            break;
+                        default:
+                            sb.append(c);
+                            break;
+                    }
+                }
+                String escapedString = sb.toString();
+                Pattern pattern = Pattern.compile(escapedString);
+                patterns.add(pattern);
+            }
+        }
+        return patterns;
+    }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -23,8 +23,8 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.Collection;
 import java.util.Iterator;
-import java.util.Set;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
 import net.jcip.annotations.Immutable;
@@ -40,7 +40,7 @@
     private int ndx;
     private int size;
 
-    JcrPropertyIterator( Set<Property> properties ) {
+    JcrPropertyIterator( Collection<Property> properties ) {
         assert properties != null;
         iterator = properties.iterator();
         size = properties.size();

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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -121,7 +121,7 @@
         }
         // Initialize required JCR descriptors.
         modifiableDescriptors.put(Repository.LEVEL_1_SUPPORTED, "true");
-        modifiableDescriptors.put(Repository.LEVEL_2_SUPPORTED, "false");
+        modifiableDescriptors.put(Repository.LEVEL_2_SUPPORTED, "true");
         modifiableDescriptors.put(Repository.OPTION_LOCKING_SUPPORTED, "false");
         modifiableDescriptors.put(Repository.OPTION_OBSERVATION_SUPPORTED, "false");
         modifiableDescriptors.put(Repository.OPTION_QUERY_SQL_SUPPORTED, "false");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -30,7 +30,6 @@
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -404,8 +403,8 @@
         // If not create a new one & populate it
         JcrNode node;
         Path parentPath = path.getParent();
-        if (parentPath.isRoot()) node = new JcrNode(this, ((JcrRootNode)getRootNode()).getInternalUuid(), path.getLastSegment());
-        else node = new JcrNode(this, ((JcrNode)getNode(parentPath)).getInternalUuid(), path.getLastSegment());
+        if (parentPath.isRoot()) node = new JcrNode(this, ((JcrRootNode)getRootNode()).internalUuid(), path.getLastSegment());
+        else node = new JcrNode(this, ((JcrNode)getNode(parentPath)).internalUuid(), path.getLastSegment());
         populateNode(node, graphNode);
         return node;
     }
@@ -747,13 +746,13 @@
         }
 
         // Now create the JCR property object wrapper around the "jcr:uuid" property ...
-        Set<Property> properties = new HashSet<Property>();
+        Map<Name, Property> properties = new HashMap<Name, Property>();
         if (uuid == null) uuid = UUID.randomUUID();
         if (referenceable) {
             if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
-            properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, PropertyType.STRING,
-                                                      uuidProperty));
+            properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, executionContext, propertyDefinition,
+                                                                       PropertyType.STRING, uuidProperty));
         }
 
         // Now create the JCR property object wrappers around the other properties ...
@@ -814,16 +813,16 @@
             }
 
             // Figure out the property type ...
-            int propertyType = propertyDefinition.getRequiredType();
-            if (propertyType == PropertyType.UNDEFINED) {
-                propertyType = jcrPropertyTypeFor(dnaProp);
+            int type = propertyDefinition.getRequiredType();
+            if (type == PropertyType.UNDEFINED) {
+                type = jcrPropertyTypeFor(dnaProp);
             }
 
             // Create the appropriate JCR property wrapper ...
             if (isMultiple) {
-                properties.add(new JcrMultiValueProperty(node, executionContext, propertyDefinition, propertyType, dnaProp));
+                properties.put(name, new JcrMultiValueProperty(node, executionContext, propertyDefinition, type, dnaProp));
             } else {
-                properties.add(new JcrSingleValueProperty(node, executionContext, propertyDefinition, propertyType, dnaProp));
+                properties.put(name, new JcrSingleValueProperty(node, executionContext, propertyDefinition, type, dnaProp));
             }
         }
 
@@ -844,11 +843,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#refresh(boolean)
      */
     public void refresh( boolean keepChanges ) {
-        throw new UnsupportedOperationException();
     }
 
     /**

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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -30,9 +30,9 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import java.util.UUID;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
@@ -48,6 +48,7 @@
 import javax.jcr.Workspace;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path.Segment;
 import org.junit.Before;
 import org.junit.Test;
@@ -117,19 +118,24 @@
     @Mock
     private Repository repository;
     private List<Segment> children;
-    private Set<Property> properties;
+    private Map<Name, Property> properties;
+    private ExecutionContext context;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        ExecutionContext context = new ExecutionContext();
+        context = new ExecutionContext();
         stub(session.getExecutionContext()).toReturn(context);
         children = new ArrayList<Segment>();
-        properties = new HashSet<Property>();
+        properties = new HashMap<Name, Property>();
         node = new MockAbstractJcrNode(session, "node", null);
         node.setProperties(properties);
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
     @Test
     public void shouldAllowVisitation() throws Exception {
         ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -161,14 +167,14 @@
     public void shouldProvideInternalUuid() throws Exception {
         UUID uuid = UUID.randomUUID();
         node.setInternalUuid(uuid);
-        assertThat(node.getInternalUuid(), is(uuid));
+        assertThat(node.internalUuid(), is(uuid));
     }
 
     @Test
     public void shouldProvideNamedProperty() throws Exception {
         Property property = Mockito.mock(Property.class);
         stub(property.getName()).toReturn("test");
-        properties.add(property);
+        properties.put(name(property.getName()), property);
         assertThat(node.getProperty("test"), is(property));
     }
 
@@ -262,7 +268,7 @@
         Property property = Mockito.mock(Property.class);
         stub(property.getName()).toReturn("jcr:primaryItemName");
         stub(property.getString()).toReturn("primaryItem");
-        properties.add(property);
+        properties.put(name(property.getName()), property);
         Item primaryItem = Mockito.mock(Item.class);
         stub(session.getItem("/node/primaryItem")).toReturn(primaryItem);
         assertThat(node.getPrimaryItem(), is(primaryItem));
@@ -277,29 +283,29 @@
     public void shouldProvideProperty() throws Exception {
         Property prop1 = Mockito.mock(Property.class);
         stub(prop1.getName()).toReturn("prop1");
-        properties.add(prop1);
+        properties.put(name(prop1.getName()), prop1);
         assertThat(node.getProperty("prop1"), is(prop1));
         MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
-        Set<Property> properties = new HashSet<Property>();
+        Map<Name, Property> properties = new HashMap<Name, Property>();
         child.setProperties(properties);
         Property prop2 = Mockito.mock(Property.class);
         stub(prop2.getName()).toReturn("prop2");
         stub(session.getItem("/node/child/prop2")).toReturn(prop2);
-        properties.add(prop2);
+        properties.put(name(prop2.getName()), prop2);
         MockAbstractJcrNode prop3Node = createChild(session, "prop3", 1, children, child);
         node.setChildren(children);
         assertThat(node.getProperty("child/prop2"), is(prop2));
         // Ensure we return a property even when a child exists with the same name
         Property prop3 = Mockito.mock(Property.class);
         stub(prop3.getName()).toReturn("prop3");
-        properties.add(prop3);
+        properties.put(name(prop3.getName()), prop3);
         stub(session.getItem("/node/child/prop3")).toReturn(prop3Node);
         assertThat(node.getProperty("child/prop3"), is(prop3));
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowGetPropertyWithNullPath() throws Exception {
-        node.getProperty(null);
+        node.getProperty((String)null);
     }
 
     @Test( expected = IllegalArgumentException.class )
@@ -315,7 +321,7 @@
     @Test( expected = PathNotFoundException.class )
     public void shouldNotProvideDescendentPropertyIfNotAvailable() throws Exception {
         MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
-        Set<Property> properties = new HashSet<Property>();
+        Map<Name, Property> properties = new HashMap<Name, Property>();
         child.setProperties(properties);
         MockAbstractJcrNode propNode = createChild(session, "prop", 1, children, child);
         node.setChildren(children);
@@ -343,11 +349,11 @@
         Value value = Mockito.mock(Value.class);
         stub(value.getString()).toReturn("mix:referenceable");
         stub(mixinProp.getValues()).toReturn(new Value[] {value});
-        properties.add(mixinProp);
+        properties.put(name(mixinProp.getName()), mixinProp);
         Property uuidProp = Mockito.mock(Property.class);
         stub(uuidProp.getName()).toReturn("jcr:uuid");
         stub(uuidProp.getString()).toReturn(uuid);
-        properties.add(uuidProp);
+        properties.put(name(uuidProp.getName()), uuidProp);
         assertThat(node.getUUID(), is(uuid));
     }
 
@@ -358,11 +364,11 @@
         stub(mixinProp.getName()).toReturn("jcr:mixinTypes");
         Value value = Mockito.mock(Value.class);
         stub(mixinProp.getValues()).toReturn(new Value[] {value});
-        properties.add(mixinProp);
+        properties.put(name(mixinProp.getName()), mixinProp);
         Property uuidProp = Mockito.mock(Property.class);
         stub(uuidProp.getName()).toReturn("jcr:uuid");
         stub(uuidProp.getString()).toReturn(uuid);
-        properties.add(uuidProp);
+        properties.put(name(uuidProp.getName()), uuidProp);
         node.getUUID();
     }
 
@@ -372,7 +378,7 @@
         Property uuidProp = Mockito.mock(Property.class);
         stub(uuidProp.getName()).toReturn("jcr:uuid");
         stub(uuidProp.getString()).toReturn(uuid);
-        properties.add(uuidProp);
+        properties.put(name(uuidProp.getName()), uuidProp);
         node.getUUID();
     }
 
@@ -386,7 +392,7 @@
         assertThat(node.hasNode("{}child"), is(false));
         Property prop = Mockito.mock(Property.class);
         stub(prop.getName()).toReturn("prop");
-        properties.add(prop);
+        properties.put(name(prop.getName()), prop);
         assertThat(node.hasNode("prop"), is(false));
         Node child = createChild(session, "child", 1, children, node);
         Node child2 = createChild(session, "child2", 1, children, child);
@@ -417,7 +423,7 @@
     @Test
     public void shouldProvideHasProperties() throws Exception {
         assertThat(node.hasProperties(), is(false));
-        properties.add(Mockito.mock(Property.class));
+        properties.put(name("something"), Mockito.mock(Property.class));
         assertThat(node.hasProperties(), is(true));
     }
 
@@ -429,13 +435,13 @@
         assertThat(node.hasProperty("child"), is(false));
         Property prop = Mockito.mock(Property.class);
         stub(prop.getName()).toReturn("prop");
-        properties.add(prop);
+        properties.put(name(prop.getName()), prop);
         assertThat(node.hasProperty("prop"), is(true));
-        Set<Property> properties = new HashSet<Property>();
+        Map<Name, Property> properties = new HashMap<Name, Property>();
         child.setProperties(properties);
         Property prop2 = Mockito.mock(Property.class);
         stub(prop2.getName()).toReturn("prop2");
-        properties.add(prop2);
+        properties.put(name(prop2.getName()), prop2);
         stub(session.getItem("/node/child/prop2")).toReturn(prop2);
         assertThat(node.hasProperty("child/prop2"), is(true));
     }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -28,7 +28,7 @@
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.stub;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.UUID;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
@@ -67,7 +67,7 @@
         ExecutionContext context = Mockito.mock(ExecutionContext.class);
         stub(session.getExecutionContext()).toReturn(context);
         stub(session.getNode(uuid)).toReturn(root);
-        node.setProperties(new HashSet<Property>());
+        node.setProperties(new HashMap<Name, Property>());
         node.setChildren(new ArrayList<Segment>());
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -26,10 +26,12 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
 import org.junit.Before;
 import org.junit.Test;
@@ -45,22 +47,28 @@
     private AbstractJcrNode node;
     @Mock
     private JcrSession session;
-    private Set<Property> properties;
+    private Map<Name, Property> properties;
+    private ExecutionContext context;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        properties = new HashSet<Property>();
+        context = new ExecutionContext();
+        properties = new HashMap<Name, Property>();
         node = new MockAbstractJcrNode(session, "node", null);
         node.setProperties(properties);
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
     @Test
     public void shouldProvidePropertyIterator() throws Exception {
-        properties.add(Mockito.mock(Property.class));
-        properties.add(Mockito.mock(Property.class));
-        properties.add(Mockito.mock(Property.class));
-        properties.add(Mockito.mock(Property.class));
+        properties.put(name("prop1"), Mockito.mock(Property.class));
+        properties.put(name("prop2"), Mockito.mock(Property.class));
+        properties.put(name("prop3"), Mockito.mock(Property.class));
+        properties.put(name("prop4"), Mockito.mock(Property.class));
         PropertyIterator iter = node.getProperties();
         assertThat(iter, notNullValue());
         assertThat(iter.getSize(), is(4L));

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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -253,7 +253,7 @@
 
     private void testDescriptorValues( Repository repository ) {
         assertThat(repository.getDescriptor(Repository.LEVEL_1_SUPPORTED), is("true"));
-        assertThat(repository.getDescriptor(Repository.LEVEL_2_SUPPORTED), is("false"));
+        assertThat(repository.getDescriptor(Repository.LEVEL_2_SUPPORTED), is("true"));
         assertThat(repository.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED), is("false"));
         assertThat(repository.getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED), is("false"));
         assertThat(repository.getDescriptor(Repository.OPTION_QUERY_SQL_SUPPORTED), is("false"));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -26,10 +26,11 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Property;
+import org.jboss.dna.graph.property.Name;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -43,12 +44,12 @@
     private JcrRootNode root;
     @Mock
     private JcrSession session;
-    private Set<Property> properties;
+    private Map<Name, Property> properties;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        properties = new HashSet<Property>();
+        properties = new HashMap<Name, Property>();
         root = new JcrRootNode(session);
         root.setProperties(properties);
     }

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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -285,7 +285,7 @@
         assertThat(nodesByUuid.isEmpty(), is(true));
         Node root = session.getRootNode();
         assertThat(root, notNullValue());
-        UUID uuid = ((JcrRootNode)root).getInternalUuid();
+        UUID uuid = ((JcrRootNode)root).internalUuid();
         assertThat(uuid, notNullValue());
         assertThat(nodesByUuid.get(uuid), is(root));
     }

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelOneTestSuite.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -1,95 +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 junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Test suite that includes the Level 1 JCR TCK API tests from the Jackrabbit project.
- * 
- * @see JcrTckLevelTwoTestSuite
- * @see JcrTckOptionalTestSuite
- * @see JcrTckTest
- */
-public class JcrTckLevelOneTestSuite extends TestCase {
-
-    /**
-     * Returns a <code>Test</code> suite that executes all tests inside this package.
-     * 
-     * @return a <code>Test</code> suite that executes all tests inside this package.
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite("JCR 1.0 Level 1 API tests");
-
-        // We currently don't pass the tests in those suites that are commented out
-        // See https://jira.jboss.org/jira/browse/DNA-285
-
-        suite.addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
-        suite.addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
-
-        // These might not all be level one tests
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
-        // suite.addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
-
-        // The tests in this suite are level one
-        // suite.addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
-
-        return suite;
-    }
-}

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckLevelTwoTestSuite.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -1,178 +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 junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.apache.jackrabbit.test.api.AddNodeTest;
-import org.apache.jackrabbit.test.api.CheckPermissionTest;
-import org.apache.jackrabbit.test.api.DocumentViewImportTest;
-import org.apache.jackrabbit.test.api.ImpersonateTest;
-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.NodeOrderableChildNodesTest;
-import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
-import org.apache.jackrabbit.test.api.NodeTest;
-import org.apache.jackrabbit.test.api.NodeUUIDTest;
-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.ReferencesTest;
-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.SetPropertyAssumeTypeTest;
-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.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;
-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.SetValueStringTest;
-import org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest;
-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.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
-import org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest;
-import org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest;
-import org.apache.jackrabbit.test.api.WorkspaceCopyTest;
-import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
-import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
-import org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest;
-import org.apache.jackrabbit.test.api.WorkspaceMoveTest;
-import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
-
-/**
- * Test suite that includes the Level 2 JCR TCK API tests from the Jackrabbit project.
- * 
- * @see JcrTckLevelOneTestSuite
- * @see JcrTckOptionalTestSuite
- * @see JcrTckTest
- */
-public class JcrTckLevelTwoTestSuite extends TestCase {
-
-    /**
-     * Returns a <code>Test</code> suite that executes all tests inside this package.
-     * 
-     * @return a <code>Test</code> suite that executes all tests inside this package.
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite("JCR 1.0 Level 2 API tests");
-
-        // We currently don't pass the tests in those suites that are commented out
-        // See https://jira.jboss.org/jira/browse/DNA-285
-
-        // level 2 tests
-        suite.addTestSuite(AddNodeTest.class);
-        suite.addTestSuite(NamespaceRegistryTest.class);
-        suite.addTestSuite(ReferencesTest.class);
-        suite.addTestSuite(SessionTest.class);
-        suite.addTestSuite(SessionUUIDTest.class);
-        suite.addTestSuite(NodeTest.class);
-        suite.addTestSuite(NodeUUIDTest.class);
-        suite.addTestSuite(NodeOrderableChildNodesTest.class);
-        suite.addTestSuite(PropertyTest.class);
-
-        suite.addTestSuite(SetValueBinaryTest.class);
-        suite.addTestSuite(SetValueBooleanTest.class);
-        suite.addTestSuite(SetValueDateTest.class);
-        suite.addTestSuite(SetValueDoubleTest.class);
-        suite.addTestSuite(SetValueLongTest.class);
-        suite.addTestSuite(SetValueReferenceTest.class);
-        suite.addTestSuite(SetValueStringTest.class);
-        suite.addTestSuite(SetValueConstraintViolationExceptionTest.class);
-        suite.addTestSuite(SetValueValueFormatExceptionTest.class);
-        suite.addTestSuite(SetValueVersionExceptionTest.class);
-
-        suite.addTestSuite(SetPropertyBooleanTest.class);
-        suite.addTestSuite(SetPropertyCalendarTest.class);
-        suite.addTestSuite(SetPropertyDoubleTest.class);
-        suite.addTestSuite(SetPropertyInputStreamTest.class);
-        suite.addTestSuite(SetPropertyLongTest.class);
-        suite.addTestSuite(SetPropertyNodeTest.class);
-        suite.addTestSuite(SetPropertyStringTest.class);
-        suite.addTestSuite(SetPropertyValueTest.class);
-        suite.addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
-        suite.addTestSuite(SetPropertyAssumeTypeTest.class);
-
-        suite.addTestSuite(NodeItemIsModifiedTest.class);
-        suite.addTestSuite(NodeItemIsNewTest.class);
-        suite.addTestSuite(PropertyItemIsModifiedTest.class);
-        suite.addTestSuite(PropertyItemIsNewTest.class);
-
-        suite.addTestSuite(NodeAddMixinTest.class);
-        suite.addTestSuite(NodeCanAddMixinTest.class);
-        suite.addTestSuite(NodeRemoveMixinTest.class);
-
-        suite.addTestSuite(WorkspaceCloneReferenceableTest.class);
-        suite.addTestSuite(WorkspaceCloneSameNameSibsTest.class);
-        suite.addTestSuite(WorkspaceCloneTest.class);
-        suite.addTestSuite(WorkspaceCloneVersionableTest.class);
-        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
-        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
-        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
-        suite.addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
-        suite.addTestSuite(WorkspaceCopyReferenceableTest.class);
-        suite.addTestSuite(WorkspaceCopySameNameSibsTest.class);
-        suite.addTestSuite(WorkspaceCopyTest.class);
-        suite.addTestSuite(WorkspaceCopyVersionableTest.class);
-        suite.addTestSuite(WorkspaceMoveReferenceableTest.class);
-        suite.addTestSuite(WorkspaceMoveSameNameSibsTest.class);
-        suite.addTestSuite(WorkspaceMoveTest.class);
-        suite.addTestSuite(WorkspaceMoveVersionableTest.class);
-
-        suite.addTestSuite(RepositoryLoginTest.class);
-        suite.addTestSuite(ImpersonateTest.class);
-        suite.addTestSuite(CheckPermissionTest.class);
-
-        suite.addTestSuite(DocumentViewImportTest.class);
-        suite.addTestSuite(SerializationTest.class);
-
-        suite.addTestSuite(ValueFactoryTest.class);
-
-        return suite;
-    }
-}

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java	2009-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckOptionalTestSuite.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -1,57 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * 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 junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Test suite that includes the Optional JCR TCK API tests from the Jackrabbit project.
- * 
- * @see JcrTckLevelOneTestSuite
- * @see JcrTckLevelTwoTestSuite
- * @see JcrTckTest
- */
-public class JcrTckOptionalTestSuite extends TestCase {
-
-    /**
-     * Returns a <code>Test</code> suite that executes all tests inside this package.
-     * 
-     * @return a <code>Test</code> suite that executes all tests inside this package.
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite("JCR 1.0 Optional API tests");
-
-        // We currently don't pass the tests in those suites that are commented out
-        // See https://jira.jboss.org/jira/browse/DNA-285
-
-        suite.addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
-        suite.addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
-        suite.addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
-        suite.addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
-
-        return suite;
-    }
-}

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-03-05 14:55:09 UTC (rev 757)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-05 18:02:40 UTC (rev 758)
@@ -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.RepositoryLoginTest;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -48,14 +49,16 @@
 
 /**
  * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests.
- * 
- * @see JcrTckLevelOneTestSuite
- * @see JcrTckLevelTwoTestSuite
- * @see JcrTckOptionalTestSuite
  */
 public class JcrTckTest {
 
     /**
+     * 
+     */
+    public JcrTckTest() {
+    }
+
+    /**
      * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
      * 
      * @return a new instance of {@link JCRTestSuite}.
@@ -66,14 +69,164 @@
 
         // Or uncomment the following lines to execute the different sets/suites of tests ...
         TestSuite suite = new TestSuite("JCR 1.0 API tests");
-        suite.addTest(JcrTckLevelOneTestSuite.suite());
-        // suite.addTest(JcrTckLevelTwoTestSuite.suite());
-        // suite.addTest(JcrTckOptionalTestSuite.suite());
 
+        suite.addTest(new LevelOneFeatureTests());
+        suite.addTest(new LevelTwoFeatureTests());
+        suite.addTest(new OptionalFeatureTests());
+
         return suite;
     }
 
     /**
+     * Test suite that includes the Level 1 JCR TCK API tests from the Jackrabbit project.
+     */
+    private static class LevelOneFeatureTests extends TestSuite {
+        protected LevelOneFeatureTests() {
+            super("JCR Level 1 API Tests");
+            // We currently don't pass the tests in those suites that are commented out
+            // See https://jira.jboss.org/jira/browse/DNA-285
+
+            addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
+
+            // These might not all be level one tests
+            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
+            // addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
+
+            // The tests in this suite are level one
+            // addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
+        }
+    }
+
+    /**
+     * Test suite that includes the Level 2 JCR TCK API tests from the Jackrabbit project.
+     */
+    private static class LevelTwoFeatureTests extends TestSuite {
+        protected LevelTwoFeatureTests() {
+            super("JCR Level 2 API Tests");
+            // We currently don't pass the tests in those suites that are commented out
+            // See https://jira.jboss.org/jira/browse/DNA-285
+
+            // level 2 tests
+            // addTestSuite(AddNodeTest.class);
+            // addTestSuite(NamespaceRegistryTest.class);
+            // addTestSuite(ReferencesTest.class);
+            // addTestSuite(SessionTest.class);
+            // addTestSuite(SessionUUIDTest.class);
+            // addTestSuite(NodeTest.class);
+            // addTestSuite(NodeUUIDTest.class);
+            // addTestSuite(NodeOrderableChildNodesTest.class);
+            // addTestSuite(PropertyTest.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(SetValueValueFormatExceptionTest.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(SetPropertyAssumeTypeTest.class);
+            //
+            // addTestSuite(NodeItemIsModifiedTest.class);
+            // 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);
+            // addTestSuite(WorkspaceCloneVersionableTest.class);
+            // addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
+            // addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
+            // addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
+            // addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
+            // addTestSuite(WorkspaceCopyReferenceableTest.class);
+            // addTestSuite(WorkspaceCopySameNameSibsTest.class);
+            // addTestSuite(WorkspaceCopyTest.class);
+            // addTestSuite(WorkspaceCopyVersionableTest.class);
+            // addTestSuite(WorkspaceMoveReferenceableTest.class);
+            // addTestSuite(WorkspaceMoveSameNameSibsTest.class);
+            // addTestSuite(WorkspaceMoveTest.class);
+            // addTestSuite(WorkspaceMoveVersionableTest.class);
+            //
+            addTestSuite(RepositoryLoginTest.class);
+            // addTestSuite(ImpersonateTest.class);
+            // addTestSuite(CheckPermissionTest.class);
+            //
+            // addTestSuite(DocumentViewImportTest.class);
+            // addTestSuite(SerializationTest.class);
+            //
+            // addTestSuite(ValueFactoryTest.class);
+        }
+    }
+
+    /**
+     * Test suite that includes the Optional JCR TCK API tests from the Jackrabbit project.
+     */
+    private static class OptionalFeatureTests extends TestSuite {
+        protected OptionalFeatureTests() {
+            super("JCR Optional API Tests");
+            // 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.version.TestAll.suite());
+            // addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
+            // 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 {



From dna-commits at lists.jboss.org  Thu Mar  5 13:14:57 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 05 Mar 2009 13:14:57 -0500
Subject: [dna-commits] DNA SVN: r759 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LfI5x-0004xv-4a@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-05 13:14:56 -0500 (Thu, 05 Mar 2009)
New Revision: 759

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
Log:
DNA-91 Implement node type management

Applied the latest patch to correct the behavior JcrNodeType.can* methods re considering the definitions from supertypes.  The patch also cleans up the can* methods in general.

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-03-05 18:02:40 UTC (rev 758)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-05 18:14:56 UTC (rev 759)
@@ -39,6 +39,7 @@
 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.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.Path.Segment;
 
@@ -48,6 +49,8 @@
 @Immutable
 class JcrNodeType implements NodeType {
 
+    public static final String RESIDUAL_ITEM_NAME = "*";
+
     /** The name of the node type (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code>) */
     private final Name name;
     /** The name of the node's primary item */
@@ -97,6 +100,56 @@
     }
 
     /**
+     * Returns the property definition with the given name. This method first checks the property definitions declared within this
+     * type to see if any property definitions have the given name. If no matches are found, this method initiates a recursive
+     * depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the supertypes of
+     * the supertypes).
+     * 
+     * @param propertyName the name of the property for which the definition should be retrieved. Use
+     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
+     * @return the property definition for the given name or <code>null</code> if no such definition exists.
+     * @see JcrNodeType#RESIDUAL_ITEM_NAME
+     */
+    JcrPropertyDefinition getPropertyDefinition( String propertyName ) {
+        for (JcrPropertyDefinition property : propertyDefinitions) {
+            if (propertyName.equals(property.getName())) {
+                return property;
+            }
+        }
+
+        for (NodeType nodeType : declaredSupertypes) {
+            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName);
+            if (definition != null) return definition;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the node definition for the child node with the given name. This method first checks the child node definitions
+     * declared within this type to see if any child node definitions have the given name. If no matches are found, this method
+     * initiates a recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or
+     * one the supertypes of the supertypes).
+     * 
+     * @param childNodeName the name of the child node for which the definition should be retrieved. Use
+     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual child node definition (if any).
+     * @return the child node definition with the given name or <code>null</code> if no such definition exists.
+     * @see JcrNodeType#RESIDUAL_ITEM_NAME
+     */
+    JcrNodeDefinition getChildNodeDefinition( String childNodeName ) {
+        for (JcrNodeDefinition childNode : childNodeDefinitions) {
+            if (childNodeName.equals(childNode.getName())) {
+                return childNode;
+            }
+        }
+
+        for (NodeType nodeType : declaredSupertypes) {
+            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childNodeName);
+            if (definition != null) return definition;
+        }
+        return null;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
@@ -105,39 +158,25 @@
 
         CheckArg.isNotNull(childNodeName, "childNodeName");
 
-        JcrNodeDefinition residual = null;
-
         // First, try to find a child node definition with the given name
-        for (JcrNodeDefinition childNode : childNodeDefinitions) {
-            if (childNodeName.equals(childNode.getName())) {
-                NodeType defaultType = childNode.getDefaultPrimaryType();
-                // If there's no default type, the child node can't be created
-                if (defaultType == null) {
-                    return false;
-                }
+        JcrNodeDefinition childNode = getChildNodeDefinition(childNodeName);
 
-                // Check if the node can be added with the named child node definition
-                return checkTypeAgainstDefinition(defaultType, childNode);
-                // If we run into a residual (*) definition, save it just in case
-            } else if (childNode.name == null) {
-                residual = childNode;
-            }
+        // If there are no named definitions in the type hierarchy, try to find a residual node definition
+        if (childNode == null) {
+            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
         }
 
-        // If there's no matching child node definition for the name and no residual definition, the node cannot be added
-        if (residual == null) {
-            return false;
-        }
+        if (childNode != null) {
+            NodeType defaultType = childNode.getDefaultPrimaryType();
+            // If there's no default type, the child node can't be created
+            if (defaultType == null) {
+                return false;
+            }
 
-        NodeType defaultType = residual.getDefaultPrimaryType();
-
-        // If there's no default type, the child node can't be created
-        if (defaultType == null) {
-            return false;
+            // Check if the node can be added with the named child node definition
+            return checkTypeAgainstDefinition(defaultType, childNode);
         }
-        
-        // Check if the node can be added with the default type of the residual child node definition
-        return checkTypeAgainstDefinition(defaultType, residual);
+        return false;
     }
 
     /**
@@ -159,24 +198,20 @@
             return false;
         }
 
-        JcrNodeDefinition residual = null;
+        // First, try to find a child node definition with the given name
+        JcrNodeDefinition childNode = getChildNodeDefinition(childNodeName);
 
-        // First, try to find a child node definition with the given name
-        for (JcrNodeDefinition childNode : childNodeDefinitions) {
-            if (childNodeName.equals(childNode.getName())) {
-                return checkTypeAgainstDefinition(primaryNodeType, childNode);
-                // If we run into a residual (*) definition, save it just in case
-            } else if (childNode.name == null) {
-                residual = childNode;
-            }
+        // If there are no named definitions in the type hierarchy, try to find a residual node definition
+        if (childNode == null) {
+            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
         }
 
-        // If there's no matching child node definition for the name and no residual definition, the node cannot be added
-        if (residual == null) {
-            return false;
+        // Check if the node can be added with the named child node definition
+        if (childNode != null) {
+            return checkTypeAgainstDefinition(primaryNodeType, childNode);
         }
 
-        return checkTypeAgainstDefinition(primaryNodeType, residual);
+        return false;
     }
 
     /**
@@ -209,6 +244,7 @@
     public boolean canRemoveItem( String itemName ) {
         CheckArg.isNotNull(itemName, "itemName");
 
+        // Don't know if item is a property or a node, so check both locally before moving up the type hierarchy
         for (PropertyDefinition item : propertyDefinitions) {
             if (itemName.equals(item.getName())) {
                 return !item.isMandatory() && !item.isProtected();
@@ -221,6 +257,13 @@
             }
         }
 
+        // Check if any supertypes prevent the removal of this item
+        for (NodeType type : declaredSupertypes) {
+            if (!type.canRemoveItem(itemName)) {
+                return false;
+            }
+        }
+
         return true;
     }
 
@@ -233,127 +276,117 @@
                                    Value value ) {
         CheckArg.isNotNull(propertyName, "propertyName");
 
-        JcrPropertyDefinition residual = null;
+        JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+        if (property == null) {
+            property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+        }
 
-        for (JcrPropertyDefinition property : propertyDefinitions) {
-            if (propertyName.equals(property.getName())) {
-                if (property.isMultiple()) {
-                    return false;
-                }
+        if (property == null) {
+            return false;
+        }
 
-                return canSetProperty(property, value);
-            } else if (property.name == null && !property.isMultiple()) {
-                residual = property;
-            }
+        // Can't modify a multi-property with a single value. Can't modify a protected property at all.
+        if (property.isMultiple() || property.isProtected()) {
+            return false;
         }
 
-        return canSetProperty(residual, value);
+        // Null values indicates an attempt to unset property
+        if (value == null) {
+            return !property.isMandatory();
+        }
+
+        return canCastValueToType(value, property.getRequiredType());
     }
 
     /**
-     * Internal method to validate that a value can be set on a given property. The values are set according to the following
-     * rules:
+     * Internal method to validate that a value can be cast to a given JCR property type. The values are set according to the
+     * following rules:
      * <ol>
-     * <li>If <code>value</code> is <code>null</code>, return the value of {@link JcrNodeType#canRemoveItem(String)}</li>
-     * <li>If <code>property.isProtected()</code> is <code>true</code>, return <code>false</code></li>
      * <li>If <code>property.getRequiredType()</code> is {@link PropertyType#UNDEFINED}, return <code>true</code></li>
      * <li>Compare the type of the given value to the required type and see if they are compatible based on the rules in the JCR
      * 1.0 spec.</li>
      * </ol>
      * 
-     * @param property the property to be set
-     * @param value the value to set (may be <code>null</code>)
-     * @return whether the property can be set to the value based on the described rules
+     * @param jcrPropertyType a value from the {@link PropertyType} constants to which this value MAY be able to be casted.
+     * @param value the value to set (may not be <code>null</code>)
+     * @return whether the value can be cast to the given property type
      */
-    private boolean canSetProperty( JcrPropertyDefinition property,
-                                    Value value ) {
-        assert property != null;
+    private boolean canCastValueToType( Value value,
+                                        int jcrPropertyType ) {
+        assert value != null;
 
-        if (value == null) {
-            return !property.isProtected() && !property.isMandatory();
-        }
+        int valueType = value.getType();
 
-        if (property.isProtected()) {
-            return false;
+        // Trivial case - no cast required
+        if (valueType == jcrPropertyType) {
+            return true;
         }
 
-        int valueType = value.getType();
+        try {
+            switch (jcrPropertyType) {
+                case PropertyType.BOOLEAN:
+                    if (valueType == PropertyType.STRING) {
+                        return true;
+                    }
 
-        switch (property.getRequiredType()) {
-            case PropertyType.BINARY:
-                return true;
-            case PropertyType.BOOLEAN:
-                if (valueType == PropertyType.BOOLEAN || valueType == PropertyType.STRING) {
-                    return true;
-                }
-
-                // If the binary can be converted to a UTF-8 string, it can be set onto a boolean property
-                if (valueType == PropertyType.BINARY) {
-                    try {
+                    if (valueType == PropertyType.BINARY) {
+                        // If the binary can be converted to a UTF-8 string, it can be set onto a boolean property
                         value.getString();
                         return true;
-                    } catch (RepositoryException re) {
-                        return false;
                     }
-                }
-                return false;
+                    return false;
 
-            case PropertyType.DATE:
-                if (valueType == PropertyType.DATE || valueType == PropertyType.DOUBLE || valueType == PropertyType.LONG) {
-                    return true;
-                }
+                case PropertyType.DATE:
+                    if (valueType == PropertyType.DOUBLE || valueType == PropertyType.LONG) {
+                        return true;
+                    }
 
-                if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
-                    try {
+                    if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
+                        // If the binary can be converted to a date, it can be set onto a date property
                         value.getDate();
                         return true;
-                    } catch (RepositoryException re) {
-                        return false;
                     }
-                }
-                return false;
+                    return false;
 
-            case PropertyType.DOUBLE:
-                return value.getType() == PropertyType.DOUBLE;
-            case PropertyType.LONG:
-                return value.getType() == PropertyType.LONG;
-            case PropertyType.NAME:
-                if (value.getType() == PropertyType.NAME) {
-                    return true;
-                }
-
-                try {
+                case PropertyType.NAME:
+                    ValueFactories valueFactories = session.getExecutionContext().getValueFactories();
                     if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
-                        session.getExecutionContext().getValueFactories().getNameFactory().create(value.getString());
+                        valueFactories.getNameFactory().create(value.getString());
                         return true;
                     }
 
                     if (valueType == PropertyType.PATH) {
-                        Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(value.getString());
+                        Path path = valueFactories.getPathFactory().create(value.getString());
 
                         Segment[] segments = path.getSegmentsArray();
                         return !path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex();
                     }
-                } catch (ValueFormatException re) {
+
                     return false;
-                } catch (RepositoryException re) {
-                    return false;
-                }
 
-                return false;
+                case PropertyType.PATH:
+                    return value.getType() == PropertyType.STRING;
 
-            case PropertyType.PATH:
-                return value.getType() == PropertyType.PATH || value.getType() == PropertyType.STRING;
-            case PropertyType.REFERENCE:
-                return value.getType() == PropertyType.REFERENCE;
+                    // Nothing can be converted to these types (except themselves)
+                case PropertyType.REFERENCE:
+                case PropertyType.DOUBLE:
+                case PropertyType.LONG:
+                    return false;
 
-                // Anything can be converted to these types
-            case PropertyType.STRING:
-            case PropertyType.UNDEFINED:
-                return true;
-            default:
-                throw new IllegalStateException("Invalid required property type " + property.getRequiredType() + " for property "
-                                                + property.getName());
+                    // Anything can be converted to these types
+                case PropertyType.BINARY:
+                case PropertyType.STRING:
+                case PropertyType.UNDEFINED:
+                    return true;
+                default:
+                    assert false : "Unexpected JCR property type " + jcrPropertyType;
+                    // This should still throw an exception even if assertions are turned off
+                    throw new IllegalStateException("Invalid property type " + jcrPropertyType);
+            }
+        } catch (RepositoryException re) {
+            return false;
+        } catch (ValueFormatException vfe) {
+            return false;
         }
     }
 
@@ -366,49 +399,33 @@
                                    Value[] values ) {
         CheckArg.isNotNull(propertyName, "propertyName");
 
-        JcrPropertyDefinition residual = null;
+        JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+        if (property == null) {
+            property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+        }
 
-        for (JcrPropertyDefinition property : propertyDefinitions) {
-            if (propertyName.equals(property.getName())) {
-                if (!property.isMultiple()) {
-                    return false;
-                }
+        if (property == null) {
+            return false;
+        }
 
-                return canSetProperty(property, values);
-            } else if (property.name == null && property.isMultiple()) {
-                residual = property;
-            }
+        // Can't modify a single valued property with a multiple values. Can't modify a protected property at all.
+        if (!property.isMultiple() || property.isProtected()) {
+            return false;
         }
 
-        return canSetProperty(residual, values);
-    }
+        // Null values indicates an attempt to unset property
+        if (values == null) {
+            return !property.isMandatory();
+        }
 
-    /**
-     * Internal method to validate that an array of values can be set on a given property. This method returns <code>true</code>
-     * if the following algorithm would return <code>true</code> when applied to each non-null value in <code>values</code>:
-     * <ol>
-     * <li>If the value is null, return the value of {@link JcrNodeType#canRemoveItem(String)}</li>
-     * <li>If the property definition has a no required type ({@link PropertyType#UNDEFINED}), return <code>true</code></li>
-     * <li>Compare the type of the given value to the required type and see if they are compatible</li>
-     * </ol>
-     * 
-     * @param property the property to be set
-     * @param values the array of values to set (may be <code>null</code>)
-     * @return whether the property can be set to the value based on the described rules
-     */
-    private boolean canSetProperty( JcrPropertyDefinition property,
-                                    Value[] values ) {
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] != null) {
-                    if (!canSetProperty(property, values[i])) {
-                        return false;
-                    }
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] != null) {
+                if (!canCastValueToType(values[i], property.getRequiredType())) {
+                    return false;
                 }
             }
         }
-
-        return !property.isProtected();
+        return true;
     }
 
     /**



From dna-commits at lists.jboss.org  Thu Mar  5 13:20:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 05 Mar 2009 13:20:34 -0500
Subject: [dna-commits] DNA SVN: r760 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LfIBO-0007Ly-4b@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-05 13:20:33 -0500 (Thu, 05 Mar 2009)
New Revision: 760

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.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/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.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-292 System View Creates Nodes With Primary Type That Is Not Defined in Type Load

Applied the patch that builds in the dna:namespace type and adds support for chaining of node type sources.

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -0,0 +1,161 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * Implementation of {@link JcrNodeTypeSource} that supports chaining of node type sources.
+ */
+abstract class AbstractJcrNodeTypeSource implements JcrNodeTypeSource {
+
+    // Convenience constants to help improve readability
+    protected static final Value[] NO_DEFAULT_VALUES = new Value[0];
+    protected static final String[] NO_CONSTRAINTS = new String[0];
+    protected static final List<NodeType> NO_SUPERTYPES = Collections.<NodeType>emptyList();
+    protected static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
+    protected static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
+
+    // Indicates that the node type has no primary item name - added for readability
+    protected static final Name NO_PRIMARY_ITEM_NAME = null;
+    // Indicates that the definition should apply to all property definition or child node definitions - added for readability
+    protected static final Name ALL_NODES = null;
+
+    // Indicates whether or not the node type is a mixin - added for readability
+    protected static final boolean IS_A_MIXIN = true;
+    protected static final boolean NOT_MIXIN = false;
+
+    // Indicates whether or not the node type has orderable children - added for readability
+    protected static final boolean ORDERABLE_CHILD_NODES = true;
+    protected static final boolean UNORDERABLE_CHILD_NODES = false;
+
+    /** The predecessor node type source. */
+    private final JcrNodeTypeSource predecessor;
+
+    AbstractJcrNodeTypeSource() {
+        this(null);
+    }
+
+    AbstractJcrNodeTypeSource( JcrNodeTypeSource parent ) {
+        this.predecessor = parent;
+    }
+
+    /**
+     * Returns the list of mixin node types declared in this type source.
+     * 
+     * @return the list of mixin node types declared in this type source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     */
+    public abstract Collection<JcrNodeType> getDeclaredMixinNodeTypes();
+
+    /**
+     * Returns the list of primary node types declared in this type source.
+     * 
+     * @return the list of primary node types declared in this type source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     */
+    public abstract Collection<JcrNodeType> getDeclaredPrimaryNodeTypes();
+
+    /**
+     * Returns the list of mixin node types returned by this and any predecessor source.
+     * 
+     * @return the list of mixin node types returned by this and any predecessor source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     */
+    public Collection<JcrNodeType> getMixinNodeTypes() {
+        if (predecessor == null) {
+            return getDeclaredMixinNodeTypes();
+        }
+
+        Collection<JcrNodeType> declaredMixins = getDeclaredMixinNodeTypes();
+        Collection<JcrNodeType> predecessorMixins = predecessor.getMixinNodeTypes();
+
+        List<JcrNodeType> mixins = new ArrayList<JcrNodeType>(declaredMixins.size() + predecessorMixins.size());
+        mixins.addAll(predecessorMixins);
+        mixins.addAll(declaredMixins);
+
+        return mixins;
+    }
+
+    /**
+     * Returns the list of primary node types returned by this and any predecessor source.
+     * 
+     * @return the list of primary node types returned by this and any predecessor source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
+     */
+    public Collection<JcrNodeType> getPrimaryNodeTypes() {
+        if (predecessor == null) {
+            return getDeclaredPrimaryNodeTypes();
+        }
+
+        Collection<JcrNodeType> declaredPrimaries = getDeclaredPrimaryNodeTypes();
+        Collection<JcrNodeType> predecessorPrimaries = predecessor.getPrimaryNodeTypes();
+
+        List<JcrNodeType> primaries = new ArrayList<JcrNodeType>(declaredPrimaries.size() + predecessorPrimaries.size());
+        primaries.addAll(predecessorPrimaries);
+        primaries.addAll(declaredPrimaries);
+
+        return primaries;
+    }
+
+    /**
+     * Finds the type with the given name and returns its definition.
+     * <p>
+     * This implementation delegates to the <code>predecessor</code> (if it exists) if the type is not found within the declareded
+     * primary and mixin types.
+     * </p>
+     * 
+     * @param typeName the name of the type to return
+     * @return the type named <code>typeName</code> if it exists, otherwise <code>null</code>.
+     * @throw IllegalArgumentException if <code>typeName</code> is <code>null</code>.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#findType(org.jboss.dna.graph.property.Name)
+     */
+    public JcrNodeType findType( Name typeName ) {
+        CheckArg.isNotNull(typeName, "typeName");
+        for (JcrNodeType type : getDeclaredPrimaryNodeTypes()) {
+            if (typeName.equals(type.getInternalName())) {
+                return type;
+            }
+        }
+
+        for (JcrNodeType type : getDeclaredMixinNodeTypes()) {
+            if (typeName.equals(type.getInternalName())) {
+                return type;
+            }
+        }
+
+        if (predecessor != null) {
+            return predecessor.findType(typeName);
+        }
+
+        return null;
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -0,0 +1,93 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
+
+/**
+ * {@link JcrNodeTypeSource} that provides built-in node types provided by DNA.
+ */
+ at Immutable
+class DnaBuiltinNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+    /** The list of primary node types. */
+    private final List<JcrNodeType> primaryNodeTypes;
+    /** The list of mixin node types. */
+    private final List<JcrNodeType> mixinNodeTypes;
+
+    DnaBuiltinNodeTypeSource( JcrSession session,
+                              JcrNodeTypeSource predecessor ) {
+        super(predecessor);
+
+        primaryNodeTypes = new ArrayList<JcrNodeType>();
+        mixinNodeTypes = new ArrayList<JcrNodeType>();
+
+        JcrNodeType base = findType(JcrNtLexicon.BASE);
+
+        if (base == null) {
+            String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+            throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+        }
+
+        // Stubbing in child node and property definitions for now
+        JcrNodeType namespace = new JcrNodeType(session, DnaLexicon.NAMESPACE, Arrays.asList(new NodeType[] {base}),
+                                                DnaLexicon.URI, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+                                                    new JcrPropertyDefinition(session, null, DnaLexicon.URI,
+                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                              true, true, true, NO_DEFAULT_VALUES,
+                                                                              PropertyType.STRING, NO_CONSTRAINTS, false)}),
+                                                NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {namespace}));
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     */
+    @Override
+    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+        return mixinNodeTypes;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
+     */
+    @Override
+    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+        return primaryNodeTypes;
+    }
+
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -33,5 +33,6 @@
 
     public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
+    public static final Name URI = new BasicName(Namespace.URI, "uri");
 
 }

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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -26,7 +26,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
@@ -39,34 +38,21 @@
  * {@link JcrNodeTypeSource} that provides built-in node types per the 1.0 specification.
  */
 @Immutable
-class JcrBuiltinNodeTypeSource implements JcrNodeTypeSource {
+class JcrBuiltinNodeTypeSource extends AbstractJcrNodeTypeSource {
 
-    // Convenience constants to help improve readability
-    private static final Value[] NO_DEFAULT_VALUES = new Value[0];
-    private static final String[] NO_CONSTRAINTS = new String[0];
-    private static final List<NodeType> NO_SUPERTYPES = Collections.<NodeType>emptyList();
-    private static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
-    private static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
-
-    // Indicates that the node type has no primary item name - added for readability
-    private static final Name NO_PRIMARY_ITEM_NAME = null;
-    // Indicates that the definition should apply to all property definition or child node definitions - added for readability
-    private static final Name ALL_NODES = null;
-
-    // Indicates whether or not the node type is a mixin - added for readability
-    private static final boolean IS_A_MIXIN = true;
-    private static final boolean NOT_MIXIN = false;
-
-    // Indicates whether or not the node type has orderable children - added for readability
-    private static final boolean ORDERABLE_CHILD_NODES = true;
-    private static final boolean UNORDERABLE_CHILD_NODES = false;
-
     /** The list of primary node types. */
     private final List<JcrNodeType> primaryNodeTypes;
     /** The list of mixin node types. */
     private final List<JcrNodeType> mixinNodeTypes;
 
     JcrBuiltinNodeTypeSource( JcrSession session ) {
+        this(session, null);
+    }
+
+    JcrBuiltinNodeTypeSource( JcrSession session,
+                              JcrNodeTypeSource predecessor ) {
+        super(predecessor);
+
         primaryNodeTypes = new ArrayList<JcrNodeType>();
 
         Value trueValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN, Boolean.TRUE);
@@ -620,7 +606,8 @@
      * 
      * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
      */
-    public Collection<JcrNodeType> getMixinNodeTypes() {
+    @Override
+    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
         return mixinNodeTypes;
     }
 
@@ -629,7 +616,8 @@
      * 
      * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
      */
-    public Collection<JcrNodeType> getPrimaryNodeTypes() {
+    @Override
+    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
         return primaryNodeTypes;
     }
 

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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -65,6 +65,7 @@
     public static I18n errorWhileFindingNodeWithUuid;
 
     public static I18n typeNotFound;
+    public static I18n supertypeNotFound;
 
     // Used in AbstractJcrNode#getAncestor
     public static I18n noNegativeDepth;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	2009-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -2,6 +2,7 @@
 
 import java.util.Collection;
 import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.graph.property.Name;
 
 /**
  * Interface for any potential provider of {@link JcrNodeType} definitions, the DNA implementation of {@link NodeType}. Possible
@@ -25,4 +26,10 @@
      */
     public Collection<JcrNodeType> getMixinNodeTypes();
 
+    /**
+     * Finds the type with the given name and returns its definition.
+     * @param typeName the name of the type to return
+     * @return the type named <code>typeName</code> if it exists, otherwise <code>null</code>.
+     */
+    public JcrNodeType findType(Name typeName);
 }

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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -156,7 +156,8 @@
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
 
         // This must be initialized after the session
-        this.nodeTypeManager = new JcrNodeTypeManager(new JcrBuiltinNodeTypeSource(this.session));
+        this.nodeTypeManager = new JcrNodeTypeManager(new DnaBuiltinNodeTypeSource(this.session,
+                                                                                   new JcrBuiltinNodeTypeSource(this.session)));
 
     }
 

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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-05 18:20:33 UTC (rev 760)
@@ -63,6 +63,7 @@
 workspaceNameIsInvalid = "{1}" is not a valid workspace name for the "{0}" repository
 errorVerifyingWorkspaceName =Error validating the workspace name "{1}" for the "{0}" repository\: {2}
 typeNotFound=No type exists with name "{0}"
+supertypeNotFound=Could not find type "{0}" which is a required supertype of type "{1}"
 
 noNegativeDepth=Depth parameter ({0}) cannot be negative
 tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this node

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-03-05 18:14:56 UTC (rev 759)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-05 18:20:33 UTC (rev 760)
@@ -127,7 +127,7 @@
             // addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
 
             // The tests in this suite are level one
-            // addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
+            addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
         }
     }
 



From dna-commits at lists.jboss.org  Fri Mar  6 11:51:56 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 06 Mar 2009 11:51:56 -0500
Subject: [dna-commits] DNA SVN: r761 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LfdHA-0004Jc-2S@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-06 11:51:55 -0500 (Fri, 06 Mar 2009)
New Revision: 761

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.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/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.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/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
Log:
DNA-194 Implement update JCR capability

None of the nodes had a Node.getDefinition() implementation.  To properly implement this, I refactored how JcrSession was creating and populating nodes, and I added some utility methods to the JcrNodeTypeManager (which is now a separate package-protected class rather than an inner class).

This allows all of the Level 2 TCK unit tests to get further, but none of the other update functionality is implemented, so none of the Level 2 TCK tests run successfully.

I also made a number of utility methods final, in the hopes that they can be inlined since they are called very frequently.

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -71,10 +71,14 @@
     Map<Name, Property> properties;
     List<Path.Segment> children;
     private UUID uuid;
+    private final NodeDefinition definition;
 
-    AbstractJcrNode( JcrSession session ) {
+    AbstractJcrNode( JcrSession session,
+                     NodeDefinition definition ) {
         assert session != null;
+        assert definition != null;
         this.session = session;
+        this.definition = definition;
     }
 
     /**
@@ -112,26 +116,33 @@
         this.properties = properties;
     }
 
+    /**
+     * Utility mehtod to create a {@link Name} from the supplied string.
+     * 
+     * @param name the string
+     * @return the name, or null if the supplied string is null
+     */
     final Name nameFrom( String name ) {
         return session.getExecutionContext().getValueFactories().getNameFactory().create(name);
     }
 
+    final NamespaceRegistry namespaces() {
+        return session.getExecutionContext().getNamespaceRegistry();
+    }
+
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Node#getUUID()
      */
     public final String getUUID() throws RepositoryException {
-        // Return JCR UUID only if node is referenceable
-        try {
-            Property mixinsProp = getProperty("jcr:mixinTypes");
-            if (mixinsProp != null) {
-                for (Value value : mixinsProp.getValues()) {
-                    if ("mix:referenceable".equals(value.getString())) return getProperty("jcr:uuid").getString();
-                }
+        // Return "jcr:uuid" only if node is referenceable
+        Property mixinsProp = getProperty(JcrLexicon.MIXIN_TYPES);
+        if (mixinsProp != null) {
+            String referenceableMixinName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
+            for (Value value : mixinsProp.getValues()) {
+                if (referenceableMixinName.equals(value.getString())) return getProperty(JcrLexicon.UUID).getString();
             }
-        } catch (PathNotFoundException error) {
-            throw new UnsupportedRepositoryOperationException(error);
         }
         throw new UnsupportedRepositoryOperationException();
     }
@@ -175,7 +186,7 @@
      * @see javax.jcr.Node#getDefinition()
      */
     public NodeDefinition getDefinition() {
-        return null;
+        return definition;
     }
 
     /**
@@ -443,7 +454,7 @@
         if (children == null) {
             return new JcrEmptyNodeIterator();
         }
-        return new JcrChildNodeIterator(this, this.session.getExecutionContext().getNamespaceRegistry(), children);
+        return new JcrChildNodeIterator(this, namespaces(), children);
     }
 
     /**
@@ -461,7 +472,7 @@
 
         // Implementing exact-matching only for now to prototype types as properties
         List<Path.Segment> matchingChildren = new LinkedList<Path.Segment>();
-        NamespaceRegistry registry = session.executionContext.getNamespaceRegistry();
+        NamespaceRegistry registry = namespaces();
         boolean foundMatch = false;
         for (Path.Segment child : children) {
             String childName = child.getName().getString(registry);
@@ -480,7 +491,7 @@
                 }
             }
         }
-        return new JcrChildNodeIterator(this, this.session.executionContext.getNamespaceRegistry(), matchingChildren);
+        return new JcrChildNodeIterator(this, registry, matchingChildren);
     }
 
     /**

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-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -34,5 +34,6 @@
     public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name URI = new BasicName(Namespace.URI, "uri");
+    public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
 
 }

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -63,6 +63,7 @@
     public static I18n invalidNamePattern;
     public static I18n itemNotFoundWithUuid;
     public static I18n errorWhileFindingNodeWithUuid;
+    public static I18n nodeDefinitionCouldBeDeterminedForNode;
 
     public static I18n typeNotFound;
     public static I18n supertypeNotFound;

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -27,6 +27,7 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.property.Path.Segment;
 
@@ -41,8 +42,9 @@
 
     JcrNode( JcrSession session,
              UUID parentUuid,
-             Segment segment ) {
-        super(session);
+             Segment segment,
+             NodeDefinition nodeDefinition ) {
+        super(session, nodeDefinition);
         assert parentUuid != null;
         assert segment != null;
         this.parentUuid = parentUuid;

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -150,6 +150,33 @@
     }
 
     /**
+     * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
+     * primary type is not supplied, then only the name is considered when finding a best match.
+     * 
+     * @param childName the name of the child
+     * @param primaryNodeTypeName the name of the primary node type for the child
+     * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
+     *         are not allowed given this node type
+     */
+    JcrNodeDefinition findBestNodeDefinitionForChild( String childName,
+                                                      String primaryNodeTypeName ) {
+        // First, try to find a child node definition with the given name
+        JcrNodeDefinition childNode = getChildNodeDefinition(childName);
+
+        // If there are no named definitions in the type hierarchy, try to find a residual node definition
+        if (childNode == null) {
+            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
+        }
+
+        // Check if the node can be added with the named child node definition
+        if (childNode != null && primaryNodeTypeName != null) {
+            NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+            if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) return null;
+        }
+        return childNode;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
@@ -179,6 +206,15 @@
         return false;
     }
 
+    protected final NodeType getPrimaryNodeType( String primaryNodeTypeName ) {
+        try {
+            return session.getWorkspace().getNodeTypeManager().getNodeType(primaryNodeTypeName);
+        } catch (RepositoryException re) {
+            // If the node type doesn't exist, you can't add a child node with that type
+            return null;
+        }
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -190,11 +226,9 @@
         CheckArg.isNotNull(childNodeName, "childNodeName");
         CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
 
-        NodeType primaryNodeType;
-        try {
-            primaryNodeType = session.getWorkspace().getNodeTypeManager().getNodeType(primaryNodeTypeName);
-        } catch (RepositoryException re) {
-            // If the node type doesn't exist, you can't add a child node with that type
+        NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+        if (primaryNodeType == null) {
+            // The node type doesn't exist, so you can't add a child node with that type
             return false;
         }
 

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -0,0 +1,145 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.NodeTypeManager;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * Local implementation of @{link NodeTypeManager}. Initialized with {@link NodeType} source data when it is created (in the
+ * {@link JcrWorkspace} constructor.
+ */
+ at NotThreadSafe
+class JcrNodeTypeManager implements NodeTypeManager {
+
+    private final Map<Name, JcrNodeType> primaryNodeTypes;
+    private final Map<Name, JcrNodeType> mixinNodeTypes;
+    private final JcrSession session;
+
+    JcrNodeTypeManager( JcrSession session,
+                        JcrNodeTypeSource source ) {
+        this.session = session;
+        Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
+        Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+
+        primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
+        for (JcrNodeType nodeType : primary) {
+            primaryNodeTypes.put(nodeType.getInternalName(), nodeType);
+        }
+
+        mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
+        for (JcrNodeType nodeType : mixins) {
+            mixinNodeTypes.put(nodeType.getInternalName(), nodeType);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes()
+     */
+    public NodeTypeIterator getAllNodeTypes() {
+
+        // TODO: Can revisit this approach later if it becomes a performance issue
+        /*
+         * Note also that this creates a subtle difference in behavior for concurrent modification
+         * between this method and the specific get*NodeTypes methods.  That is, if a type is added
+         * while an iterator from the corresponding specific get*NodeType method is being traversed,
+         * a ConcurrentModificationException will be thrown.  Because this iterator is based on a copy
+         * of the underlying maps, no exception would be thrown in the same case.
+         */
+
+        List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
+        allTypes.addAll(primaryNodeTypes.values());
+        allTypes.addAll(mixinNodeTypes.values());
+        return new JcrNodeTypeIterator(allTypes);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
+     */
+    public NodeTypeIterator getMixinNodeTypes() {
+        return new JcrNodeTypeIterator(mixinNodeTypes.values());
+    }
+
+    final JcrNodeType getNodeType( Name nodeTypeName ) {
+
+        JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
+        if (nodeType == null) {
+            nodeType = mixinNodeTypes.get(nodeTypeName);
+        }
+        return nodeType;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
+     */
+    public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
+        Name ntName = session.getExecutionContext().getValueFactories().getNameFactory().create(nodeTypeName);
+        NodeType type = getNodeType(ntName);
+        if (type != null) return type;
+        throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
+     */
+    public NodeTypeIterator getPrimaryNodeTypes() {
+        return new JcrNodeTypeIterator(primaryNodeTypes.values());
+    }
+
+    /**
+     * Get the {@link NodeDefinition} for the root node.
+     * 
+     * @return the definition; never null
+     * @throws RepositoryException
+     * @throws NoSuchNodeTypeException
+     */
+    JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException {
+        // TODO: Fix this
+        for (NodeDefinition definition : getNodeType("nt:unstructured").getChildNodeDefinitions()) {
+            if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition;
+        }
+        assert false; // should not get here
+        return null;
+    }
+
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -25,6 +25,7 @@
 
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
+import javax.jcr.nodetype.NodeDefinition;
 import net.jcip.annotations.NotThreadSafe;
 
 /**
@@ -33,8 +34,9 @@
 @NotThreadSafe
 final class JcrRootNode extends AbstractJcrNode {
 
-    JcrRootNode( JcrSession session ) {
-        super(session);
+    JcrRootNode( JcrSession session,
+                 NodeDefinition nodeDefinition ) {
+        super(session, nodeDefinition);
     }
 
     /**

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -51,6 +51,7 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.nodetype.PropertyDefinition;
@@ -59,9 +60,10 @@
 import javax.security.auth.login.LoginException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-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.connector.RepositorySourceException;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -123,8 +125,9 @@
     private final ReferenceMap<UUID, Node> nodesByUuid;
     private final ReferenceMap<String, Node> nodesByJcrUuid;
     private boolean isLive;
-    private JcrRootNode rootNode;
+    private AbstractJcrNode rootNode;
     private PropertyDefinition anyMultiplePropertyDefinition;
+    private transient org.jboss.dna.graph.property.Property defaultPrimaryType;
 
     JcrSession( JcrRepository repository,
                 JcrWorkspace workspace,
@@ -165,6 +168,14 @@
         return this.executionContext;
     }
 
+    final JcrNodeTypeManager nodeTypeManager() {
+        return this.workspace.nodeTypeManager();
+    }
+
+    final NamespaceRegistry namespaces() {
+        return this.executionContext.getNamespaceRegistry();
+    }
+
     /**
      * Return an unmodifiable map of nodes given then UUID.
      * 
@@ -369,7 +380,7 @@
             Path parentPath = path.getParent();
             Name propertyName = path.getLastSegment().getName();
             try {
-                return getNode(parentPath).getProperty(propertyName.getString(executionContext.getNamespaceRegistry()));
+                return getNode(parentPath).getProperty(propertyName.getString(namespaces()));
             } catch (org.jboss.dna.graph.property.PathNotFoundException e2) {
                 // If the node isn't found, throw a PathNotFoundException
                 throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
@@ -387,25 +398,138 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * Find or create a JCR Node for the given path. This method works for the root node, too.
+     * 
+     * @param path the path; may not be null
+     * @return the JCR node instance for the given path; never null
+     * @throws RepositoryException if there is a problem
+     */
     private Node getNode( Path path ) throws RepositoryException {
-        // Get node from source
+        boolean isRoot = path.isRoot();
+        if (isRoot && rootNode != null) return rootNode;
+
+        // Get node from source and get it's UUID ...
         org.jboss.dna.graph.Node graphNode = graph.getNodeAt(path);
-        // First check if node already exists. We don't need to check for changes since that will be handled by an observer
-        org.jboss.dna.graph.property.Property dnaUuidProp = graphNode.getPropertiesByName().get(JcrLexicon.UUID);
-        if (dnaUuidProp == null) dnaUuidProp = graphNode.getPropertiesByName().get(DnaLexicon.UUID);
-        if (dnaUuidProp != null) {
-            UUID uuid = executionContext.getValueFactories().getUuidFactory().create(dnaUuidProp.getValues()).next();
+
+        // Now get the DNA node's UUID ...
+        Location location = graphNode.getLocation();
+        ValueFactories factories = executionContext.getValueFactories();
+        UUID uuid = location.getUuid();
+        org.jboss.dna.graph.property.Property uuidProperty = null;
+        if (uuid != null) {
+            // Considered an identification property ...
+            uuidProperty = location.getIdProperty(JcrLexicon.UUID);
+            if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+        }
+        if (uuidProperty == null) {
+            uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
+            if (uuidProperty != null) {
+                // Grab the first 'good' UUID value ...
+                for (Object uuidValue : uuidProperty) {
+                    try {
+                        uuid = factories.getUuidFactory().create(uuidValue);
+                        break;
+                    } catch (ValueFormatException e) {
+                        // Ignore; just continue with the next property value
+                    }
+                }
+            }
+            if (uuid == null) {
+                // Look for the DNA UUID property ...
+                org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
+                if (dnaUuidProperty != null) {
+                    // Grab the first 'good' UUID value ...
+                    for (Object uuidValue : dnaUuidProperty) {
+                        try {
+                            uuid = factories.getUuidFactory().create(uuidValue);
+                            break;
+                        } catch (ValueFormatException e) {
+                            // Ignore; just continue with the next property value
+                        }
+                    }
+                }
+            }
+        }
+        if (uuid == null) uuid = UUID.randomUUID();
+        if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
+
+        // See if there is already a JCR node object for this UUID ...
+        if (uuid != null && !isRoot) {
             Node node = getNode(uuid);
-            if (node != null) {
-                return node;
+            if (node != null) return node;
+        }
+
+        // Either the UUID is not known, or there was no node. Either way, we have to create the node ...
+        if (uuid == null) uuid = UUID.randomUUID();
+
+        // Look for the primary type of the node ...
+        String primaryTypeNameString = null;
+        NamespaceRegistry namespaces = namespaces();
+        org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+        if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
+            Name primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
+            primaryTypeNameString = primaryTypeName.getString(namespaces);
+        } else {
+            // We have to have a primary type, so use the default ...
+            if (defaultPrimaryType == null) {
+                defaultPrimaryType = executionContext.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE,
+                                                                                  JcrNtLexicon.UNSTRUCTURED);
             }
+            primaryTypeProperty = defaultPrimaryType;
+            // We have to add this property to the graph node...
+            graphNode.getPropertiesByName().put(primaryTypeProperty.getName(), primaryTypeProperty);
         }
-        // If not create a new one & populate it
-        JcrNode node;
-        Path parentPath = path.getParent();
-        if (parentPath.isRoot()) node = new JcrNode(this, ((JcrRootNode)getRootNode()).internalUuid(), path.getLastSegment());
-        else node = new JcrNode(this, ((JcrNode)getNode(parentPath)).internalUuid(), path.getLastSegment());
-        populateNode(node, graphNode);
+        assert primaryTypeProperty.isEmpty() == false;
+
+        // Look for a node definition stored on the node ...
+        NodeDefinition definition = null;
+        org.jboss.dna.graph.property.Property nodeDefnProperty = graphNode.getProperty(DnaLexicon.NODE_DEFINITON);
+        if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
+            Path nodeDefnPath = factories.getPathFactory().create(nodeDefnProperty.getFirstValue());
+            // Look up the node definition ...
+            Name nodeTypeName = nodeDefnPath.getSegment(0).getName();
+            JcrNodeType nodeType = nodeTypeManager().getNodeType(nodeTypeName);
+            if (nodeType != null && nodeDefnPath.size() > 1) {
+                // Look up the definition for the child name rule (in the second segment of the relative path) ...
+                String childNameRule = nodeDefnPath.getSegment(1).getString(namespaces);
+                definition = nodeType.findBestNodeDefinitionForChild(childNameRule, primaryTypeNameString);
+            }
+        }
+
+        AbstractJcrNode node = null;
+        if (isRoot) {
+            // The node definition should be set ...
+            if (definition == null) {
+                definition = nodeTypeManager().getRootNodeDefinition();
+                assert definition != null;
+            }
+
+            // Create the new node ...
+            node = new JcrRootNode(this, definition);
+        } else {
+            // Find the parent ...
+            AbstractJcrNode parent = (AbstractJcrNode)getNode(path.getParent());
+
+            // Find the node definition for this node ...
+            if (definition == null) {
+                // Look for the parent's node type, and look for a node definition based upon the name ...
+                JcrNodeType nodeType = (JcrNodeType)parent.getPrimaryNodeType();
+                String childName = path.getLastSegment().getName().getString(namespaces);
+                definition = nodeType.findBestNodeDefinitionForChild(childName, primaryTypeNameString);
+                if (definition == null) {
+                    String msg = JcrI18n.nodeDefinitionCouldBeDeterminedForNode.text(path, workspace.getName());
+                    throw new RepositorySourceException(msg);
+                }
+            }
+
+            // Now create the node object ...
+            node = new JcrNode(this, parent.internalUuid(), path.getLastSegment(), definition);
+        }
+
+        // Now populate the node and add to the cache ...
+        populateNode(node, graphNode, uuid, uuidProperty, primaryTypeProperty);
+        if (isRoot) rootNode = node;
         return node;
     }
 
@@ -443,21 +567,7 @@
         if (rootNode != null) {
             return rootNode;
         }
-        // Get root node from source
-        assert executionContext.getValueFactories() != null;
-        assert executionContext.getValueFactories().getPathFactory() != null;
-        rootNode = new JcrRootNode(this);
-
-        // Get root node from source
-        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-        org.jboss.dna.graph.Node dnaRootNode = graph.getNodeAt(rootPath);
-        if (dnaRootNode.getProperty(JcrLexicon.PRIMARY_TYPE) == null) {
-            // Add the primary type and update the source ...
-            graph.set(JcrLexicon.PRIMARY_TYPE).to(JcrNtLexicon.BASE).on(rootPath);
-            dnaRootNode = graph.getNodeAt(rootPath);
-        }
-        populateNode(rootNode, dnaRootNode);
-        return rootNode;
+        return getNode(executionContext.getValueFactories().getPathFactory().createRootPath());
     }
 
     /**
@@ -653,10 +763,16 @@
         return PropertyType.UNDEFINED;
     }
 
-    private void populateNode( AbstractJcrNode node,
-                               org.jboss.dna.graph.Node graphNode ) throws RepositoryException {
+    final void populateNode( AbstractJcrNode node,
+                             org.jboss.dna.graph.Node graphNode,
+                             UUID uuid,
+                             org.jboss.dna.graph.property.Property uuidProperty,
+                             org.jboss.dna.graph.property.Property primaryTypeProperty ) throws RepositoryException {
         assert node != null;
         assert graphNode != null;
+        assert uuid != null;
+        assert uuidProperty != null;
+        assert primaryTypeProperty != null;
 
         // --------------------------------------------------
         // Create JCR children for corresponding DNA children
@@ -670,35 +786,34 @@
         Map<Name, PropertyDefinition> propertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
         boolean referenceable = false;
 
-        NamespaceRegistry registry = executionContext.getNamespaceRegistry();
+        NamespaceRegistry registry = namespaces();
         ValueFactories factories = executionContext.getValueFactories();
         NameFactory nameFactory = factories.getNameFactory();
         NodeTypeManager nodeTypeManager = getWorkspace().getNodeTypeManager();
         List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
-        org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
-        if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
-            Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
-            String primaryTypeNameString = primaryTypeName.getString(registry);
-            NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
-            for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
-                String nameString = propertyDefn.getName();
-                if ("*".equals(nameString)) {
-                    anyPropertyDefinitions.add(propertyDefn);
-                    continue;
-                }
-                Name name = nameFactory.create(nameString);
-                PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
-                if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+        // Start with the primary type ...
+        Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
+        String primaryTypeNameString = primaryTypeName.getString(registry);
+        NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
+        for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+            String nameString = propertyDefn.getName();
+            if ("*".equals(nameString)) {
+                anyPropertyDefinitions.add(propertyDefn);
+                continue;
             }
+            Name name = nameFactory.create(nameString);
+            PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
+            if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
         }
+        // The process the mixin types ...
         org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
         if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
             for (Object mixinTypeValue : mixinTypesProperty) {
                 Name mixinTypeName = nameFactory.create(mixinTypeValue);
                 if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
                 String mixinTypeNameString = mixinTypeName.getString(registry);
-                NodeType primaryType = nodeTypeManager.getNodeType(mixinTypeNameString);
-                for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+                NodeType mixinType = nodeTypeManager.getNodeType(mixinTypeNameString);
+                for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
                     String nameString = propertyDefn.getName();
                     if ("*".equals(nameString)) {
                         anyPropertyDefinitions.add(propertyDefn);
@@ -711,45 +826,9 @@
             }
         }
 
-        // Look for the UUID property ...
-        UUID uuid = null;
-        org.jboss.dna.graph.property.Property uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
-        Name jcrUuidPropertyName = null;
-        Name dnaUuidPropertyName = null;
-        if (uuidProperty != null) {
-            jcrUuidPropertyName = uuidProperty.getName();
-            // Grab the first 'good' UUID value ...
-            for (Object uuidValue : uuidProperty) {
-                try {
-                    uuid = factories.getUuidFactory().create(uuidValue);
-                    break;
-                } catch (ValueFormatException e) {
-                    // Ignore; just continue with the next property value
-                }
-            }
-        }
-        if (uuid == null) {
-            // Look for the DNA UUID property ...
-            org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
-            if (dnaUuidProperty != null) {
-                dnaUuidPropertyName = dnaUuidProperty.getName();
-                // Grab the first 'good' UUID value ...
-                for (Object uuidValue : dnaUuidProperty) {
-                    try {
-                        uuid = factories.getUuidFactory().create(uuidValue);
-                        break;
-                    } catch (ValueFormatException e) {
-                        // Ignore; just continue with the next property value
-                    }
-                }
-            }
-        }
-
         // Now create the JCR property object wrapper around the "jcr:uuid" property ...
         Map<Name, Property> properties = new HashMap<Name, Property>();
-        if (uuid == null) uuid = UUID.randomUUID();
         if (referenceable) {
-            if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
             properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, executionContext, propertyDefinition,
                                                                        PropertyType.STRING, uuidProperty));
@@ -760,7 +839,7 @@
             Name name = dnaProp.getName();
 
             // Skip the JCR and DNA UUID properties (using the EXACT Name instances on the Property) ...
-            if (name == jcrUuidPropertyName || name == dnaUuidPropertyName) continue;
+            if (JcrLexicon.UUID.equals(name) || DnaLexicon.UUID.equals(name)) continue;
 
             // Figure out the JCR property type for this property ...
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(name);
@@ -826,10 +905,6 @@
             }
         }
 
-        if (referenceable) {
-            assert uuidProperty != null;
-        }
-
         // Now set the properties on the node ...
         node.setProperties(properties);
 

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -25,10 +25,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.jcr.AccessDeniedException;
@@ -41,9 +37,6 @@
 import javax.jcr.Workspace;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.nodetype.NoSuchNodeTypeException;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.observation.ObservationManager;
 import javax.jcr.query.QueryManager;
@@ -111,14 +104,13 @@
     /**
      * Reference to the JCR type manager for this workspace.
      */
-    private final NodeTypeManager nodeTypeManager;
+    private final JcrNodeTypeManager nodeTypeManager;
 
     /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
     private final JcrSession session;
 
-    @SuppressWarnings( "synthetic-access" )
     JcrWorkspace( JcrRepository repository,
                   String workspaceName,
                   ExecutionContext context,
@@ -156,26 +148,31 @@
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
 
         // This must be initialized after the session
-        this.nodeTypeManager = new JcrNodeTypeManager(new DnaBuiltinNodeTypeSource(this.session,
+        this.nodeTypeManager = new JcrNodeTypeManager(this.session,
+                                                      new DnaBuiltinNodeTypeSource(this.session,
                                                                                    new JcrBuiltinNodeTypeSource(this.session)));
 
     }
 
-    String getSourceName() {
+    final String getSourceName() {
         return this.repository.getRepositorySourceName();
     }
 
+    final JcrNodeTypeManager nodeTypeManager() {
+        return this.nodeTypeManager;
+    }
+
     /**
      * {@inheritDoc}
      */
-    public String getName() {
+    public final String getName() {
         return name;
     }
 
     /**
      * {@inheritDoc}
      */
-    public Session getSession() {
+    public final Session getSession() {
         return this.session;
     }
 
@@ -184,7 +181,7 @@
      * 
      * @see javax.jcr.Workspace#getNamespaceRegistry()
      */
-    public NamespaceRegistry getNamespaceRegistry() {
+    public final NamespaceRegistry getNamespaceRegistry() {
         return workspaceRegistry;
     }
 
@@ -203,21 +200,21 @@
     /**
      * {@inheritDoc}
      */
-    public NodeTypeManager getNodeTypeManager() {
+    public final NodeTypeManager getNodeTypeManager() {
         return nodeTypeManager;
     }
 
     /**
      * {@inheritDoc}
      */
-    public ObservationManager getObservationManager() {
+    public final ObservationManager getObservationManager() {
         throw new UnsupportedOperationException();
     }
 
     /**
      * {@inheritDoc}
      */
-    public QueryManager getQueryManager() {
+    public final QueryManager getQueryManager() {
         throw new UnsupportedOperationException();
     }
 
@@ -350,95 +347,4 @@
         throw new UnsupportedOperationException();
     }
 
-    /**
-     * Local implementation of @{link NodeTypeManager}. Initialized with {@link NodeType} source data when it is created (in the
-     * {@link JcrWorkspace} constructor.
-     */
-    @NotThreadSafe
-    private class JcrNodeTypeManager implements NodeTypeManager {
-
-        private final Map<Name, JcrNodeType> primaryNodeTypes;
-        private final Map<Name, JcrNodeType> mixinNodeTypes;
-
-        private JcrNodeTypeManager( JcrNodeTypeSource source ) {
-            Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
-            Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
-
-            primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
-            for (JcrNodeType nodeType : primary) {
-                primaryNodeTypes.put(nodeType.getInternalName(), nodeType);
-            }
-
-            mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
-            for (JcrNodeType nodeType : mixins) {
-                mixinNodeTypes.put(nodeType.getInternalName(), nodeType);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes()
-         */
-        public NodeTypeIterator getAllNodeTypes() {
-
-            // TODO: Can revisit this approach later if it becomes a performance issue
-            /*
-             * Note also that this creates a subtle difference in behavior for concurrent modification
-             * between this method and the specific get*NodeTypes methods.  That is, if a type is added
-             * while an iterator from the corresponding specific get*NodeType method is being traversed,
-             * a ConcurrentModificationException will be thrown.  Because this iterator is based on a copy
-             * of the underlying maps, no exception would be thrown in the same case.
-             */
-
-            List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
-            allTypes.addAll(primaryNodeTypes.values());
-            allTypes.addAll(mixinNodeTypes.values());
-            return new JcrNodeTypeIterator(allTypes);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
-         */
-        public NodeTypeIterator getMixinNodeTypes() {
-            return new JcrNodeTypeIterator(mixinNodeTypes.values());
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
-         */
-        @SuppressWarnings( "synthetic-access" )
-        public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
-            Name ntName = session.getExecutionContext().getValueFactories().getNameFactory().create(nodeTypeName);
-
-            NodeType nodeType = primaryNodeTypes.get(ntName);
-
-            if (nodeType != null) {
-                return nodeType;
-            }
-
-            nodeType = mixinNodeTypes.get(ntName);
-
-            if (nodeType != null) {
-                return nodeType;
-            }
-
-            throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
-         */
-        public NodeTypeIterator getPrimaryNodeTypes() {
-            return new JcrNodeTypeIterator(primaryNodeTypes.values());
-        }
-
-    }
-
 }

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-06 16:51:55 UTC (rev 761)
@@ -53,6 +53,7 @@
 invalidNamePattern = The "{1}" name pattern contained the '{0}' character, which is not allowed in a name pattern
 itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}"
 errorWhileFindingNodeWithUuid = Error while finding the item with UUID "{0}" in workspace "{1}"
+nodeDefinitionCouldBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
 
 REP_NAME_DESC = DNA Repository
 REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC

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-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -26,6 +26,7 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -46,6 +47,7 @@
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
@@ -83,7 +85,7 @@
         MockAbstractJcrNode( JcrSession session,
                              String name,
                              Node parent ) {
-            super(session);
+            super(session, mock(NodeDefinition.class));
             this.name = name;
             this.parent = parent;
         }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -33,6 +33,7 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.Property;
+import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -52,18 +53,22 @@
     private Node root;
     @Mock
     private JcrSession session;
+    @Mock
+    private NodeDefinition rootNodeDefinition;
+    @Mock
+    private NodeDefinition nodeDefinition;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        root = new JcrRootNode(session);
+        root = new JcrRootNode(session, rootNodeDefinition);
         Segment segment = Mockito.mock(Segment.class);
         Name name = Mockito.mock(Name.class);
         stub(name.getString((NamespaceRegistry)anyObject())).toReturn("name");
         stub(segment.getName()).toReturn(name);
         stub(segment.getIndex()).toReturn(2);
         UUID uuid = UUID.randomUUID();
-        node = new JcrNode(session, uuid, segment);
+        node = new JcrNode(session, uuid, segment, nodeDefinition);
         ExecutionContext context = Mockito.mock(ExecutionContext.class);
         stub(session.getExecutionContext()).toReturn(context);
         stub(session.getNode(uuid)).toReturn(root);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-05 18:20:33 UTC (rev 760)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-06 16:51:55 UTC (rev 761)
@@ -30,6 +30,7 @@
 import java.util.Map;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Property;
+import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.property.Name;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,13 +45,15 @@
     private JcrRootNode root;
     @Mock
     private JcrSession session;
+    @Mock
+    private NodeDefinition nodeDefinition;
     private Map<Name, Property> properties;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         properties = new HashMap<Name, Property>();
-        root = new JcrRootNode(session);
+        root = new JcrRootNode(session, nodeDefinition);
         root.setProperties(properties);
     }
 



From dna-commits at lists.jboss.org  Fri Mar  6 12:03:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 06 Mar 2009 12:03:00 -0500
Subject: [dna-commits] DNA SVN: r762 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LfdRs-0007h9-7U@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-06 12:03:00 -0500 (Fri, 06 Mar 2009)
New Revision: 762

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/JcrItemDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
Log:
DNA-294 Define a custom node type for the root node in a repository

Applied the patch.  Also changed the JcrNodeTypeManager.getRootNodeDefinition() method to return "dna:root" node type (rather than "nt:unstructured").

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-03-06 16:51:55 UTC (rev 761)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-06 17:03:00 UTC (rev 762)
@@ -29,6 +29,7 @@
 import java.util.List;
 import javax.jcr.PropertyType;
 import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.graph.JcrMixLexicon;
 import net.jcip.annotations.Immutable;
 
 /**
@@ -57,17 +58,86 @@
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
+        JcrNodeType referenceable = findType(JcrMixLexicon.REFERENCEABLE);
+
+        if (referenceable == null) {
+            String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(session.getExecutionContext().getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.SYSTEM.getString(session.getExecutionContext().getNamespaceRegistry());
+            throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+        }
+
         // Stubbing in child node and property definitions for now
-        JcrNodeType namespace = new JcrNodeType(session, DnaLexicon.NAMESPACE, Arrays.asList(new NodeType[] {base}),
-                                                DnaLexicon.URI, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
-                                                    new JcrPropertyDefinition(session, null, DnaLexicon.URI,
-                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                              true, true, true, NO_DEFAULT_VALUES,
-                                                                              PropertyType.STRING, NO_CONSTRAINTS, false)}),
+        JcrNodeType namespace = new JcrNodeType(
+                                                session,
+                                                DnaLexicon.NAMESPACE,
+                                                Arrays.asList(new NodeType[] {base}),
+                                                DnaLexicon.URI,
+                                                NO_CHILD_NODES,
+                                                Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                     session,
+                                                                                                                     null,
+                                                                                                                     DnaLexicon.URI,
+                                                                                                                     OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                     true,
+                                                                                                                     true,
+                                                                                                                     true,
+                                                                                                                     NO_DEFAULT_VALUES,
+                                                                                                                     PropertyType.STRING,
+                                                                                                                     NO_CONSTRAINTS,
+                                                                                                                     false)}),
                                                 NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {namespace}));
+        JcrNodeType namespaces = new JcrNodeType(
+                                                 session,
+                                                 DnaLexicon.NAMESPACES,
+                                                 Arrays.asList(new NodeType[] {base}),
+                                                 NO_PRIMARY_ITEM_NAME,
+                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                              session,
+                                                                                                              null,
+                                                                                                              null,
+                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                              false,
+                                                                                                              false,
+                                                                                                              true,
+                                                                                                              false,
+                                                                                                              DnaLexicon.NAMESPACE,
+                                                                                                              new NodeType[] {namespace})}),
+                                                 NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
+        JcrNodeType system = new JcrNodeType(
+                                             session,
+                                             DnaLexicon.SYSTEM,
+                                             Arrays.asList(new NodeType[] {base}),
+                                             NO_PRIMARY_ITEM_NAME,
+                                             Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                          session,
+                                                                                                          null,
+                                                                                                          DnaLexicon.NAMESPACES,
+                                                                                                          OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                          true,
+                                                                                                          true,
+                                                                                                          true,
+                                                                                                          false,
+                                                                                                          DnaLexicon.NAMESPACES,
+                                                                                                          new NodeType[] {namespaces})}),
+                                             NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        JcrNodeType root = new JcrNodeType(session, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base, referenceable}),
+                                           NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
+                                               new JcrNodeDefinition(session, null, JcrLexicon.SYSTEM,
+                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
+                                                                     true, false, DnaLexicon.NAMESPACES,
+                                                                     new NodeType[] {namespaces}),
+                                               new JcrNodeDefinition(session, null, null,
+                                                                     OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
+                                                                     false, true, DnaLexicon.NAMESPACES,
+                                                                     new NodeType[] {namespaces}),
+
+                                           }), NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, 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-03-06 16:51:55 UTC (rev 761)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-06 17:03:00 UTC (rev 762)
@@ -33,6 +33,8 @@
 
     public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
+    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");
     public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-06 16:51:55 UTC (rev 761)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-06 17:03:00 UTC (rev 762)
@@ -29,8 +29,8 @@
 import org.jboss.dna.graph.property.Name;
 
 /**
- * DNA implementation of the {@link ItemDefinition} interface.  This implementation is immutable and has all fields initialized
- * through its constructor. 
+ * DNA implementation of the {@link ItemDefinition} interface. This implementation is immutable and has all fields initialized
+ * through its constructor.
  */
 @Immutable
 class JcrItemDefinition implements ItemDefinition {
@@ -77,7 +77,7 @@
      */
     public String getName() {
         if (name == null) {
-            return "*";
+            return JcrNodeType.RESIDUAL_ITEM_NAME;
         }
 
         return name.getString(session.getExecutionContext().getNamespaceRegistry());

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-03-06 16:51:55 UTC (rev 761)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java	2009-03-06 17:03:00 UTC (rev 762)
@@ -63,6 +63,7 @@
     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");
@@ -73,5 +74,7 @@
     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");
+    public static final Name XMLTEXT = new BasicName(Namespace.URI, "xmltext");
+    public static final Name XMLCHARACTERS = new BasicName(Namespace.URI, "xmlcharacters");
 
 }

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-03-06 16:51:55 UTC (rev 761)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-06 17:03:00 UTC (rev 762)
@@ -134,8 +134,7 @@
      * @throws NoSuchNodeTypeException
      */
     JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException {
-        // TODO: Fix this
-        for (NodeDefinition definition : getNodeType("nt:unstructured").getChildNodeDefinitions()) {
+        for (NodeDefinition definition : getNodeType(DnaLexicon.ROOT).getChildNodeDefinitions()) {
             if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition;
         }
         assert false; // should not get here



From dna-commits at lists.jboss.org  Mon Mar  9 13:57:18 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 09 Mar 2009 13:57:18 -0400
Subject: [dna-commits] DNA SVN: r763 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1Lgjj4-00045I-Co@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-09 13:57:18 -0400 (Mon, 09 Mar 2009)
New Revision: 763

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.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/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.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/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/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
Log:
DNA-194 Implement update JCR capability

Refactored the fields that are used in the JcrAbstractNode, JcrNode, and JcrRootNode implementations.  Because Location objects will play a much more significant role, several new implementations of Location were added, including an optimum implementation of a Location with a path and UUID.

Also made a number of the very-oft-used package-level methods final, hopefully increasing the chance they're inlined.  Minimal risk of overriding, although some tests were made a little more complicated (but probably better overall anyway).

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -38,7 +38,6 @@
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
 
 /**
  * The location of a node, as specified by either its path, UUID, and/or identification properties.
@@ -102,8 +101,12 @@
      */
     public static Location create( Path path,
                                    UUID uuid ) {
-        CheckArg.isNotNull(uuid, "uuid");
-        return new LocationWithPathAndProperty(path, new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
+        if (path == null) {
+            CheckArg.isNotNull(uuid, "uuid");
+            return new LocationWithUuid(uuid);
+        }
+        if (uuid == null) return new LocationWithPath(path);
+        return new LocationWithPathAndUuid(path, uuid);
     }
 
     /**
@@ -182,7 +185,7 @@
      */
     public static Location create( Property idProperty ) {
         CheckArg.isNotNull(idProperty, "idProperty");
-        return new LocationWithPathAndProperty(null, idProperty);
+        return new LocationWithProperty(idProperty);
     }
 
     /**
@@ -197,6 +200,7 @@
                                    Property... remainingIdProperties ) {
         CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
         CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
+        if (remainingIdProperties.length == 0) return new LocationWithProperty(firstIdProperty);
         List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
         Set<Name> names = new HashSet<Name>();
         names.add(firstIdProperty.getName());
@@ -227,9 +231,9 @@
                 assert false;
                 return null; // never get here
             case 1:
-                return new LocationWithPathAndProperty(null, idPropertiesList.get(0));
+                return new LocationWithProperty(idPropertiesList.get(0));
             default:
-                return new LocationWithPathAndProperties(null, idPropertiesList);
+                return new LocationWithProperties(idPropertiesList);
         }
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPath.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -39,7 +39,7 @@
  * @see Location
  */
 @Immutable
-class LocationWithPath extends Location {
+final class LocationWithPath extends Location {
 
     private final Path path;
     private final int hashCode;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperties.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -40,7 +40,7 @@
  * @see Location
  */
 @Immutable
-class LocationWithPathAndProperties extends Location {
+final class LocationWithPathAndProperties extends Location {
 
     private final Path path;
     private final List<Property> idProperties;
@@ -54,7 +54,7 @@
      * @param idProperties the identification properties
      */
     LocationWithPathAndProperties( Path path,
-                            List<Property> idProperties ) {
+                                   List<Property> idProperties ) {
         assert idProperties != null;
         assert !idProperties.isEmpty();
         this.path = path;
@@ -125,7 +125,7 @@
             newIdProperties = Collections.unmodifiableList(newIdProperties);
             return new LocationWithPathAndProperties(path, newIdProperties);
         }
-        return Location.create(path, newIdProperty);
+        return new LocationWithPathAndProperty(path, newIdProperty);
     }
 
     /**
@@ -136,7 +136,7 @@
     @Override
     public Location with( Path newPath ) {
         if (newPath == null || this.getPath().equals(newPath)) return this;
-        return Location.create(newPath, idProperties);
+        return new LocationWithPathAndProperties(newPath, idProperties);
     }
 
     /**
@@ -156,6 +156,6 @@
         List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
         newIdProperties.addAll(idProperties);
         newIdProperties.add(newProperty);
-        return Location.create(path, newIdProperties);
+        return new LocationWithPathAndProperties(path, newIdProperties);
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndProperty.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -41,7 +41,7 @@
  * @see Location
  */
 @Immutable
-class LocationWithPathAndProperty extends Location {
+final class LocationWithPathAndProperty extends Location {
 
     private final Path path;
     private final List<Property> idProperties;
@@ -56,16 +56,16 @@
      */
     LocationWithPathAndProperty( Path path,
                                  Property idProperty ) {
+        assert path != null;
         assert idProperty != null;
         assert !idProperty.isEmpty();
-        // The path could be null
         this.path = path;
         this.idProperties = Collections.singletonList(idProperty);
 
         // Paths are immutable, Properties are immutable, the idProperties list
         // is wrapped in an unmodifiableList by the Location factory methods...
         // ... so we can cache the hash code.
-        hashCode = HashCode.compute(path, idProperties);
+        hashCode = HashCode.compute(this.path, idProperties);
     }
 
     /**
@@ -81,6 +81,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.Location#hasPath()
+     */
+    @Override
+    public final boolean hasPath() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see Location#getIdProperties()
      */
     @Override
@@ -146,9 +156,9 @@
         if (newIdProperty == null || newIdProperty.isEmpty()) return this;
         Property idProperty = idProperties.get(0); // fast
         if (newIdProperty.getName().equals(idProperty.getName())) {
-            return path == null ? Location.create(newIdProperty) : Location.create(path, newIdProperty);
+            return new LocationWithPathAndProperty(path, newIdProperty);
         }
-        return path == null ? Location.create(idProperty, newIdProperty) : Location.create(path, idProperty, newIdProperty);
+        return Location.create(path, idProperty, newIdProperty);
     }
 
     /**
@@ -158,10 +168,10 @@
      */
     @Override
     public Location with( Path newPath ) {
-        if (newPath == null && path == null) return this;
-        if (path != null && (path.equals(newPath))) return this;
+        if (newPath == null) return Location.create(idProperties);
+        if (path.equals(newPath)) return this;
         Property idProperty = idProperties.get(0); // fast
-        return Location.create(newPath, idProperty);
+        return new LocationWithPathAndProperty(newPath, idProperty);
     }
 
     /**
@@ -171,12 +181,10 @@
      */
     @Override
     public Location with( UUID uuid ) {
-        if (uuid == null) return this;
         Property idProperty = idProperties.get(0); // fast
-        if (DnaLexicon.UUID.equals(idProperty.getName())) {
-            return path == null ? Location.create(uuid) : Location.create(path, uuid);
-        }
+        assert !DnaLexicon.UUID.equals(idProperty.getName());
+        if (uuid == null) return Location.create(path);
         Property newUuidProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
-        return path == null ? Location.create(idProperty, newUuidProperty) : Location.create(path, idProperty, newUuidProperty);
+        return Location.create(path, idProperty, newUuidProperty);
     }
 }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithPathAndUuid.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -0,0 +1,181 @@
+/*
+ * 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.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
+
+/**
+ * General purpose location type that supports a path and a UUID property. This class should never be directly instantiated by
+ * users of the DNA framework. Instead, use @{link Location#create} to create the correct location.
+ * 
+ * @see Location
+ */
+ at Immutable
+final class LocationWithPathAndUuid extends Location {
+
+    private final Path path;
+    private final List<Property> idProperties;
+
+    private final int hashCode;
+
+    /**
+     * Create a new location with a given path and identification property.
+     * 
+     * @param path the path
+     * @param uuid the UUID
+     */
+    LocationWithPathAndUuid( Path path,
+                             UUID uuid ) {
+        assert path != null;
+        assert uuid != null;
+        this.path = path;
+        this.idProperties = Collections.singletonList((Property)new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
+
+        // Paths are immutable, Properties are immutable, the idProperties list
+        // is wrapped in an unmodifiableList by the Location factory methods...
+        // ... so we can cache the hash code.
+        hashCode = HashCode.compute(this.path, idProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getPath()
+     */
+    @Override
+    public final Path getPath() {
+        return path;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.Location#hasPath()
+     */
+    @Override
+    public final boolean hasPath() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getIdProperties()
+     */
+    @Override
+    public final List<Property> getIdProperties() {
+        return idProperties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hasIdProperties()
+     */
+    @Override
+    public final boolean hasIdProperties() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getIdProperty(Name)
+     */
+    @Override
+    public final Property getIdProperty( Name name ) {
+        CheckArg.isNotNull(name, "name");
+        Property property = idProperties.get(0); // this is fast
+        return property.getName().equals(name) ? property : null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getUuid()
+     */
+    @Override
+    public final UUID getUuid() {
+        return (UUID)idProperties.get(0).getFirstValue(); // this is fast, and we know this is a UUID object
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Property)
+     */
+    @Override
+    public Location with( Property newIdProperty ) {
+        if (newIdProperty == null || newIdProperty.isEmpty()) return this;
+        Property idProperty = idProperties.get(0); // fast
+        if (newIdProperty.getName().equals(idProperty.getName())) {
+            return new LocationWithPathAndProperty(path, newIdProperty);
+        }
+        return Location.create(path, idProperty, newIdProperty);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Path)
+     */
+    @Override
+    public Location with( Path newPath ) {
+        if (newPath == null) return Location.create(idProperties);
+        if (path.equals(newPath)) return this;
+        Property idProperty = idProperties.get(0); // fast
+        return new LocationWithPathAndProperty(newPath, idProperty);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(UUID)
+     */
+    @Override
+    public Location with( UUID uuid ) {
+        if (uuid == null) return Location.create(path);
+        if (uuid.equals(getUuid())) return this;
+        return new LocationWithPathAndUuid(path, uuid);
+    }
+}


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

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperties.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -0,0 +1,164 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
+
+/**
+ * General purpose location type that supports a path and zero or more properties. This class should never be directly
+ * instantiated by users of the DNA framework. Instead, use @{link Location#create} to create the correct location.
+ * 
+ * @see Location
+ */
+ at Immutable
+final class LocationWithProperties extends Location {
+
+    private final List<Property> idProperties;
+    private final int hashCode;
+
+    /**
+     * Create a new location with a given path and set of identification properties.
+     * 
+     * @param idProperties the identification properties
+     */
+    LocationWithProperties( List<Property> idProperties ) {
+        assert idProperties != null;
+        assert !idProperties.isEmpty();
+        this.idProperties = Collections.unmodifiableList(idProperties);
+        // Paths are immutable, Properties are immutable, the idProperties list
+        // is wrapped in an unmodifiableList by the Location factory methods...
+        // ... so we can cache the hash code.
+        hashCode = HashCode.compute(null, idProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getPath()
+     */
+    @Override
+    public final Path getPath() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.Location#hasPath()
+     */
+    @Override
+    public final boolean hasPath() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getIdProperties()
+     */
+    @Override
+    public final List<Property> getIdProperties() {
+        return idProperties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hasIdProperties()
+     */
+    @Override
+    public final boolean hasIdProperties() {
+        return idProperties.size() > 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Property)
+     */
+    @Override
+    public Location with( Property newIdProperty ) {
+        if (newIdProperty == null || newIdProperty.isEmpty()) return this;
+
+        List<Property> newIdProperties;
+
+        assert hasIdProperties();
+        newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        for (Property property : idProperties) {
+            if (!newIdProperty.getName().equals(property.getName())) newIdProperties.add(property);
+        }
+        newIdProperties.add(newIdProperty);
+        newIdProperties = Collections.unmodifiableList(newIdProperties);
+        return new LocationWithProperties(newIdProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Path)
+     */
+    @Override
+    public Location with( Path newPath ) {
+        if (newPath == null || this.getPath().equals(newPath)) return this;
+        return new LocationWithPathAndProperties(newPath, idProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(UUID)
+     */
+    @Override
+    public Location with( UUID uuid ) {
+        if (uuid == null) return this;
+        Property newProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
+        if (this.hasIdProperties()) {
+            Property existing = this.getIdProperty(DnaLexicon.UUID);
+            if (existing != null && existing.equals(newProperty)) return this;
+        }
+
+        List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        newIdProperties.addAll(idProperties);
+        newIdProperties.add(newProperty);
+        return new LocationWithProperties(newIdProperties);
+    }
+}


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

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithProperty.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -0,0 +1,193 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.basic.BasicSingleValueProperty;
+
+/**
+ * General purpose location type that supports a path and a property. This class should never be directly instantiated by users of
+ * the DNA framework. Instead, use @{link Location#create} to create the correct location.
+ * 
+ * @see Location
+ */
+ at Immutable
+final class LocationWithProperty extends Location {
+
+    protected final List<Property> idProperties;
+
+    private final int hashCode;
+
+    /**
+     * Create a new location with a given path and identification property.
+     * 
+     * @param idProperty the identification property
+     */
+    LocationWithProperty( Property idProperty ) {
+        assert idProperty != null;
+        assert !idProperty.isEmpty();
+        // The path could be null
+        this.idProperties = Collections.singletonList(idProperty);
+
+        // Paths are immutable, Properties are immutable, the idProperties list
+        // is wrapped in an unmodifiableList by the Location factory methods...
+        // ... so we can cache the hash code.
+        hashCode = HashCode.compute(null, idProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getPath()
+     */
+    @Override
+    public final Path getPath() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.Location#hasPath()
+     */
+    @Override
+    public boolean hasPath() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getIdProperties()
+     */
+    @Override
+    public final List<Property> getIdProperties() {
+        return idProperties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hasIdProperties()
+     */
+    @Override
+    public final boolean hasIdProperties() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getIdProperty(Name)
+     */
+    @Override
+    public final Property getIdProperty( Name name ) {
+        CheckArg.isNotNull(name, "name");
+        Property property = idProperties.get(0); // this is fast
+        return property.getName().equals(name) ? property : null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#getUuid()
+     */
+    @Override
+    public UUID getUuid() {
+        Property property = idProperties.get(0); // this is fast
+        if (DnaLexicon.UUID.equals(property.getName())) {
+            Object value = property.getFirstValue();
+            if (value instanceof UUID) return (UUID)value;
+            if (value instanceof String) return UUID.fromString((String)value);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Property)
+     */
+    @Override
+    public Location with( Property newIdProperty ) {
+        if (newIdProperty == null || newIdProperty.isEmpty()) return this;
+        Property idProperty = idProperties.get(0); // fast
+        if (newIdProperty.getName().equals(idProperty.getName())) {
+            return Location.create(newIdProperty);
+        }
+        List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        newIdProperties.add(newIdProperty);
+        newIdProperties.addAll(idProperties);
+        return new LocationWithProperties(newIdProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(Path)
+     */
+    @Override
+    public Location with( Path newPath ) {
+        if (newPath == null) return this;
+        Property idProperty = idProperties.get(0); // fast
+        return new LocationWithPathAndProperty(newPath, idProperty);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see Location#with(UUID)
+     */
+    @Override
+    public Location with( UUID uuid ) {
+        if (uuid == null) return this;
+        Property idProperty = idProperties.get(0); // fast
+        if (DnaLexicon.UUID.equals(idProperty.getName())) {
+            return new LocationWithUuid(uuid);
+        }
+        List<Property> newIdProperties = new ArrayList<Property>(idProperties.size() + 1);
+        newIdProperties.add(new BasicSingleValueProperty(DnaLexicon.UUID, uuid));
+        newIdProperties.addAll(idProperties);
+        return new LocationWithProperties(newIdProperties);
+    }
+}


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

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/LocationWithUuid.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -39,7 +39,7 @@
  * @see Location
  */
 @Immutable
-public class LocationWithUuid extends Location {
+final class LocationWithUuid extends Location {
 
     private final UUID uuid;
     private final int hashCode;

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -53,6 +53,7 @@
 import javax.jcr.version.VersionHistory;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
@@ -68,15 +69,18 @@
     private static final NodeType[] EMPTY_NODE_TYPES = new NodeType[] {};
 
     private final JcrSession session;
-    Map<Name, Property> properties;
-    List<Path.Segment> children;
-    private UUID uuid;
     private final NodeDefinition definition;
+    protected Location location;
+    private Map<Name, Property> properties;
+    private List<Path.Segment> children;
 
     AbstractJcrNode( JcrSession session,
+                     Location location,
                      NodeDefinition definition ) {
         assert session != null;
         assert definition != null;
+        assert location != null;
+        this.location = location;
         this.session = session;
         this.definition = definition;
     }
@@ -86,12 +90,16 @@
      * 
      * @see javax.jcr.Item#getSession()
      */
-    public final Session getSession() {
+    public Session getSession() {
         return session;
     }
 
+    final JcrSession session() {
+        return session;
+    }
+
     final UUID internalUuid() {
-        return uuid;
+        return location.getUuid();
     }
 
     /**
@@ -103,7 +111,7 @@
      */
     final void setInternalUuid( UUID uuid ) {
         assert uuid != null;
-        this.uuid = uuid;
+        location = location.with(uuid);
     }
 
     final void setChildren( List<Segment> children ) {

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -43,23 +43,19 @@
 @NotThreadSafe
 abstract class AbstractJcrProperty extends AbstractJcrItem implements Property {
 
-    private final Node node;
-    private final ExecutionContext executionContext;
+    private final AbstractJcrNode node;
     private final org.jboss.dna.graph.property.Property dnaProperty;
     private final PropertyDefinition jcrPropertyDefinition;
     private final int propertyType;
 
-    AbstractJcrProperty( Node node,
-                         ExecutionContext executionContext,
+    AbstractJcrProperty( AbstractJcrNode node,
                          PropertyDefinition definition,
                          int propertyType,
                          org.jboss.dna.graph.property.Property dnaProperty ) {
         assert node != null;
-        assert executionContext != null;
         assert dnaProperty != null;
         assert definition != null;
         this.node = node;
-        this.executionContext = executionContext;
         this.dnaProperty = dnaProperty;
         this.jcrPropertyDefinition = definition;
         this.propertyType = propertyType;
@@ -77,11 +73,11 @@
     }
 
     JcrValue createValue( Object value ) {
-        return new JcrValue(executionContext.getValueFactories(), getType(), value);
+        return new JcrValue(getExecutionContext().getValueFactories(), getType(), value);
     }
 
     final ExecutionContext getExecutionContext() {
-        return executionContext;
+        return node.session().getExecutionContext();
     }
 
     final org.jboss.dna.graph.property.Property getDnaProperty() {
@@ -116,7 +112,7 @@
      * @see javax.jcr.Item#getName()
      */
     public final String getName() {
-        return dnaProperty.getName().getString(executionContext.getNamespaceRegistry());
+        return dnaProperty.getName().getString(node.namespaces());
     }
 
     /**
@@ -142,7 +138,7 @@
      * 
      * @see javax.jcr.Item#getSession()
      */
-    public final Session getSession() throws RepositoryException {
+    public final Session getSession() {
         return node.getSession();
     }
 

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -32,7 +32,6 @@
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Property;
 
 /**
@@ -41,12 +40,11 @@
 @NotThreadSafe
 final class JcrMultiValueProperty extends AbstractJcrProperty {
 
-    JcrMultiValueProperty( Node node,
-                           ExecutionContext executionContext,
+    JcrMultiValueProperty( AbstractJcrNode node,
                            PropertyDefinition definition,
                            int propertyType,
                            Property dnaProperty ) {
-        super(node, executionContext, definition, propertyType, dnaProperty);
+        super(node, definition, propertyType, dnaProperty);
     }
 
     /**

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -29,7 +29,8 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.NodeDefinition;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
 
 /**
  * @author jverhaeg
@@ -38,26 +39,27 @@
 final class JcrNode extends AbstractJcrNode {
 
     private final UUID parentUuid;
-    private final Segment segment;
 
     JcrNode( JcrSession session,
              UUID parentUuid,
-             Segment segment,
+             Location location,
              NodeDefinition nodeDefinition ) {
-        super(session, nodeDefinition);
+        super(session, location, nodeDefinition);
         assert parentUuid != null;
-        assert segment != null;
         this.parentUuid = parentUuid;
-        this.segment = segment;
     }
 
+    final Path.Segment segment() {
+        return location.getPath().getLastSegment();
+    }
+
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Node#getIndex()
      */
     public int getIndex() {
-        return segment.getIndex();
+        return segment().getIndex();
     }
 
     /**
@@ -66,7 +68,7 @@
      * @see javax.jcr.Item#getName()
      */
     public String getName() {
-        return segment.getName().getString(((JcrSession)getSession()).getExecutionContext().getNamespaceRegistry());
+        return segment().getName().getString(((JcrSession)getSession()).getExecutionContext().getNamespaceRegistry());
     }
 
     /**
@@ -75,7 +77,7 @@
      * @see javax.jcr.Item#getParent()
      */
     public Node getParent() throws ItemNotFoundException {
-        Node node = ((JcrSession)getSession()).getNode(parentUuid);
+        Node node = session().getNode(parentUuid);
         if (node == null) {
             throw new ItemNotFoundException();
         }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -27,6 +27,7 @@
 import javax.jcr.Node;
 import javax.jcr.nodetype.NodeDefinition;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.Location;
 
 /**
  * @author jverhaeg
@@ -35,8 +36,9 @@
 final class JcrRootNode extends AbstractJcrNode {
 
     JcrRootNode( JcrSession session,
+                 Location location,
                  NodeDefinition nodeDefinition ) {
-        super(session, nodeDefinition);
+        super(session, location, nodeDefinition);
     }
 
     /**

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -506,7 +506,7 @@
             }
 
             // Create the new node ...
-            node = new JcrRootNode(this, definition);
+            node = new JcrRootNode(this, location, definition);
         } else {
             // Find the parent ...
             AbstractJcrNode parent = (AbstractJcrNode)getNode(path.getParent());
@@ -524,7 +524,7 @@
             }
 
             // Now create the node object ...
-            node = new JcrNode(this, parent.internalUuid(), path.getLastSegment(), definition);
+            node = new JcrNode(this, parent.internalUuid(), location, definition);
         }
 
         // Now populate the node and add to the cache ...
@@ -830,8 +830,8 @@
         Map<Name, Property> properties = new HashMap<Name, Property>();
         if (referenceable) {
             PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
-            properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, executionContext, propertyDefinition,
-                                                                       PropertyType.STRING, uuidProperty));
+            properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, propertyDefinition, PropertyType.STRING,
+                                                                       uuidProperty));
         }
 
         // Now create the JCR property object wrappers around the other properties ...
@@ -892,16 +892,16 @@
             }
 
             // Figure out the property type ...
-            int type = propertyDefinition.getRequiredType();
-            if (type == PropertyType.UNDEFINED) {
-                type = jcrPropertyTypeFor(dnaProp);
+            int propertyType = propertyDefinition.getRequiredType();
+            if (propertyType == PropertyType.UNDEFINED) {
+                propertyType = jcrPropertyTypeFor(dnaProp);
             }
 
             // Create the appropriate JCR property wrapper ...
             if (isMultiple) {
-                properties.put(name, new JcrMultiValueProperty(node, executionContext, propertyDefinition, type, dnaProp));
+                properties.put(name, new JcrMultiValueProperty(node, propertyDefinition, propertyType, dnaProp));
             } else {
-                properties.put(name, new JcrSingleValueProperty(node, executionContext, propertyDefinition, type, dnaProp));
+                properties.put(name, new JcrSingleValueProperty(node, propertyDefinition, propertyType, dnaProp));
             }
         }
 

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -31,7 +31,6 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.PropertyDefinition;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.Reference;
@@ -42,12 +41,11 @@
  */
 final class JcrSingleValueProperty extends AbstractJcrProperty {
 
-    JcrSingleValueProperty( Node node,
-                            ExecutionContext executionContext,
+    JcrSingleValueProperty( AbstractJcrNode node,
                             PropertyDefinition definition,
                             int propertyType,
                             Property dnaProperty ) {
-        super(node, executionContext, definition, propertyType, dnaProperty);
+        super(node, definition, propertyType, dnaProperty);
     }
 
     /**

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -50,6 +50,7 @@
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path.Segment;
 import org.junit.Before;
@@ -85,7 +86,7 @@
         MockAbstractJcrNode( JcrSession session,
                              String name,
                              Node parent ) {
-            super(session, mock(NodeDefinition.class));
+            super(session, Location.create(UUID.randomUUID()), mock(NodeDefinition.class));
             this.name = name;
             this.parent = parent;
         }

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-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.stub;
 import java.io.InputStream;
 import java.util.Calendar;
+import java.util.UUID;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
@@ -38,9 +39,12 @@
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -58,13 +62,16 @@
     @Mock
     private Repository repository;
     @Mock
-    private Session session;
+    private JcrSession session;
+    private AbstractJcrNode node;
     @Mock
-    private Node node;
+    private NodeDefinition nodeDefinition;
     @Mock
     private PropertyDefinition propertyDefinition;
     private ExecutionContext executionContext;
     private org.jboss.dna.graph.property.Property dnaProperty;
+    private Location rootLocation;
+    private Location nodeLocation;
 
     @Before
     public void before() throws Exception {
@@ -74,8 +81,21 @@
         stub(propertyDefinition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(session.getWorkspace()).toReturn(workspace);
         stub(session.getRepository()).toReturn(repository);
-        stub(node.getSession()).toReturn(session);
-        prop = new MockAbstractJcrProperty(node, executionContext, propertyDefinition, dnaProperty);
+        stub(session.getExecutionContext()).toReturn(executionContext);
+
+        UUID rootUuid = UUID.randomUUID();
+        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
+        rootLocation = Location.create(rootPath, rootUuid);
+        JcrRootNode rootNode = new JcrRootNode(session, rootLocation, nodeDefinition);
+        stub(session.getNode(rootUuid)).toReturn(rootNode);
+
+        UUID uuid = UUID.randomUUID();
+        Path path = executionContext.getValueFactories().getPathFactory().create("/nodeName");
+        nodeLocation = Location.create(path, uuid);
+        node = new JcrNode(session, rootUuid, nodeLocation, nodeDefinition);
+        stub(session.getNode(uuid)).toReturn(node);
+
+        prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
     }
 
     @Test
@@ -99,19 +119,17 @@
     @Test
     public void shouldProvideAncestor() throws Exception {
         assertThat(prop.getAncestor(prop.getDepth()), is((Item)prop));
-        stub(node.getAncestor(node.getDepth())).toReturn(node);
         assertThat(prop.getAncestor(prop.getDepth() - 1), is((Item)node));
     }
 
     @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
-        stub(node.getAncestor(1)).toThrow(new ItemNotFoundException());
-        prop.getAncestor(2);
+        prop.getAncestor(3);
     }
 
     @Test
     public void shouldProvideDepth() throws Exception {
-        assertThat(prop.getDepth(), is(1));
+        assertThat(prop.getDepth(), is(2));
     }
 
     @Test
@@ -126,20 +144,17 @@
 
     @Test
     public void shouldProvideParent() throws Exception {
-        assertThat(prop.getParent(), is(node));
+        assertThat(prop.getParent(), is((Node)node));
     }
 
     @Test
     public void shouldProvidePath() throws Exception {
-        stub(node.getPath()).toReturn("/nodeName");
         assertThat(prop.getPath(), is("/nodeName/jcr:mimeType"));
     }
 
     @Test
     public void shouldProvideSession() throws Exception {
-        Session session = Mockito.mock(Session.class);
-        stub(node.getSession()).toReturn(session);
-        assertThat(prop.getSession(), is(session));
+        assertThat(prop.getSession(), is((Session)session));
     }
 
     @Test
@@ -151,30 +166,40 @@
     public void shouldIndicateSameAsNodeWithSameParentAndSamePropertyName() throws Exception {
         org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
                                                                                  .create(dnaProperty.getName());
-        Node otherNode = Mockito.mock(Node.class);
-        stub(otherNode.getSession()).toReturn(session);
-        stub(node.isSame(otherNode)).toReturn(true);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
+        // Make the other node have the same UUID ...
+        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+
+        assertThat(node.isSame(otherNode), is(true));
+        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(true));
     }
 
     @Test
     public void shouldIndicateDifferentThanNodeWithDifferentParent() throws Exception {
-        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
-        Node otherNode = Mockito.mock(Node.class);
-        stub(otherNode.getSession()).toReturn(session);
-        stub(node.isSame(otherNode)).toReturn(false);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
+        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
+                                                                                 .create(dnaProperty.getName());
+        UUID otherUuid = UUID.randomUUID();
+        Path otherPath = executionContext.getValueFactories().getPathFactory().create("/nodeName");
+        Location location = Location.create(otherPath, otherUuid);
+        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), location, nodeDefinition);
+        stub(session.getNode(otherUuid)).toReturn(otherNode);
+
+        assertThat(node.isSame(otherNode), is(false));
+        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
     @Test
     public void shouldIndicateDifferentThanPropertyWithSameNodeWithDifferentPropertyName() throws Exception {
         org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
-        Node otherNode = Mockito.mock(Node.class);
-        stub(otherNode.getSession()).toReturn(session);
-        stub(node.isSame(otherNode)).toReturn(true);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, executionContext, propertyDefinition, otherDnaProperty);
+        // Make the other node have the same UUID ...
+        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+
+        assertThat(node.isSame(otherNode), is(true));
+        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
+        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
@@ -230,11 +255,10 @@
 
     private class MockAbstractJcrProperty extends AbstractJcrProperty {
 
-        MockAbstractJcrProperty( Node node,
-                                 ExecutionContext executionContext,
+        MockAbstractJcrProperty( AbstractJcrNode node,
                                  PropertyDefinition propertyDefinition,
                                  org.jboss.dna.graph.property.Property dnaProperty ) {
-            super(node, executionContext, propertyDefinition, propertyDefinition.getRequiredType(), dnaProperty);
+            super(node, propertyDefinition, propertyDefinition.getRequiredType(), dnaProperty);
         }
 
         /**

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -27,13 +27,16 @@
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.stub;
-import javax.jcr.Node;
+import java.util.UUID;
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -45,10 +48,13 @@
 public class JcrMultiValuePropertyTest {
 
     private Property prop;
-    @Mock
-    private Node node;
+    private AbstractJcrNode node;
     private ExecutionContext executionContext;
     @Mock
+    private JcrSession session;
+    @Mock
+    private NodeDefinition nodeDefinition;
+    @Mock
     private PropertyDefinition definition;
     private org.jboss.dna.graph.property.Property dnaProperty;
 
@@ -56,10 +62,17 @@
     public void before() {
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
+
+        UUID rootUuid = UUID.randomUUID();
+        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
+        Location rootLocation = Location.create(rootPath, rootUuid);
+        node = new JcrRootNode(session, rootLocation, nodeDefinition);
+        stub(session.getExecutionContext()).toReturn(executionContext);
+
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
         stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
     }
 
     @Test
@@ -138,7 +151,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -149,7 +162,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -159,7 +172,7 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object[])values);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, executionContext, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(values.length));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -25,7 +25,6 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.stub;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -35,12 +34,12 @@
 import javax.jcr.Property;
 import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Path.Segment;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoAnnotations.Mock;
 
@@ -61,17 +60,18 @@
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        root = new JcrRootNode(session, rootNodeDefinition);
-        Segment segment = Mockito.mock(Segment.class);
-        Name name = Mockito.mock(Name.class);
-        stub(name.getString((NamespaceRegistry)anyObject())).toReturn("name");
-        stub(segment.getName()).toReturn(name);
-        stub(segment.getIndex()).toReturn(2);
+        ExecutionContext context = new ExecutionContext();
+        UUID rootUuid = UUID.randomUUID();
+        Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
+        Location rootLocation = Location.create(rootPath, rootUuid);
+        root = new JcrRootNode(session, rootLocation, rootNodeDefinition);
         UUID uuid = UUID.randomUUID();
-        node = new JcrNode(session, uuid, segment, nodeDefinition);
-        ExecutionContext context = Mockito.mock(ExecutionContext.class);
+        Path path = context.getValueFactories().getPathFactory().create("/name[2]");
+        Location location = Location.create(path, uuid);
+        node = new JcrNode(session, rootUuid, location, nodeDefinition);
         stub(session.getExecutionContext()).toReturn(context);
-        stub(session.getNode(uuid)).toReturn(root);
+        stub(session.getNode(rootUuid)).toReturn(root);
+        stub(session.getNode(uuid)).toReturn(node);
         node.setProperties(new HashMap<Name, Property>());
         node.setChildren(new ArrayList<Segment>());
     }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -28,10 +28,14 @@
 import static org.junit.Assert.assertThat;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.UUID;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.nodetype.NodeDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -53,7 +57,11 @@
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         properties = new HashMap<Name, Property>();
-        root = new JcrRootNode(session, nodeDefinition);
+        ExecutionContext context = new ExecutionContext();
+        UUID rootUuid = UUID.randomUUID();
+        Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
+        Location rootLocation = Location.create(rootPath, rootUuid);
+        root = new JcrRootNode(session, rootLocation, nodeDefinition);
         root.setProperties(properties);
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-06 17:03:00 UTC (rev 762)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-09 17:57:18 UTC (rev 763)
@@ -35,8 +35,10 @@
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
@@ -51,10 +53,13 @@
 public class JcrSingleValuePropertyTest {
 
     private Property prop;
-    @Mock
-    private Node node;
+    private AbstractJcrNode node;
     private ExecutionContext executionContext;
     @Mock
+    private JcrSession session;
+    @Mock
+    private NodeDefinition nodeDefinition;
+    @Mock
     Name name;
     @Mock
     private PropertyDefinition definition;
@@ -64,15 +69,22 @@
     public void before() {
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
+
+        UUID rootUuid = UUID.randomUUID();
+        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
+        Location rootLocation = Location.create(rootPath, rootUuid);
+        node = new JcrRootNode(session, rootLocation, nodeDefinition);
+        stub(session.getExecutionContext()).toReturn(executionContext);
+
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
         stub(definition.isMultiple()).toReturn(false);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
     }
 
     @Test
     public void shouldProvideBoolean() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BOOLEAN, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.BOOLEAN, dnaProperty);
         assertThat(prop.getBoolean(), is(true));
         assertThat(prop.getType(), is(PropertyType.BOOLEAN));
     }
@@ -88,7 +100,7 @@
     public void shouldProvideDate() throws Exception {
         DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DATE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.DATE, dnaProperty);
         assertThat(prop.getDate(), is(dnaDate.toCalendar()));
         assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
         assertThat(prop.getType(), is(PropertyType.DATE));
@@ -100,10 +112,8 @@
         UUID referencedUuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
         Node referencedNode = mock(Node.class);
-        JcrSession session = mock(JcrSession.class);
-        stub(node.getSession()).toReturn(session);
         stub(session.getNode(referencedUuid)).toReturn(referencedNode);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.REFERENCE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.REFERENCE, dnaProperty);
         assertThat(prop.getNode(), is(referencedNode));
         assertThat(prop.getType(), is(PropertyType.REFERENCE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -112,12 +122,12 @@
     @Test
     public void shouldProvideDouble() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -126,13 +136,13 @@
     @Test
     public void shouldProvideLong() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getLong(), is(1L));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
 
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.DOUBLE, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
         assertThat(prop.getLong(), is(1L));
         assertThat(prop.getString(), is("1"));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
@@ -142,7 +152,7 @@
     @Test
     public void shouldProvideStream() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BINARY, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
         assertThat(prop.getType(), is(PropertyType.BINARY));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         InputStream stream = prop.getStream();
@@ -158,7 +168,7 @@
     @Test
     public void shouldProvideString() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getString(), is("value"));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -168,7 +178,7 @@
     public void shouldAllowReferenceValue() throws Exception {
         UUID uuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getString(), is(uuid.toString()));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -179,7 +189,7 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.NAME, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.NAME, dnaProperty);
         assertThat(prop.getString(), is("acme:something"));
         assertThat(prop.getType(), is(PropertyType.NAME));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -193,7 +203,7 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.PATH, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.PATH, dnaProperty);
         assertThat(prop.getString(), is("/a/b/acme:c"));
         assertThat(prop.getType(), is(PropertyType.PATH));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -220,13 +230,13 @@
 
         dnaValue = "some other value";
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.STRING, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
         assertThat(prop.getLength(), is((long)dnaValue.length()));
 
         Object obj = new Object();
         long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
-        prop = new JcrSingleValueProperty(node, executionContext, definition, PropertyType.BINARY, dnaProperty);
+        prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
         assertThat(prop.getLength(), is(binaryLength));
     }
 



From dna-commits at lists.jboss.org  Tue Mar 10 11:17:23 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 11:17:23 -0400
Subject: [dna-commits] DNA SVN: r764 - in trunk:
	dna-common/src/main/java/org/jboss/dna/common/util and 5
	other directories.
Message-ID: <E1Lh3hr-0002cW-Tx@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 11:17:23 -0400 (Tue, 10 Mar 2009)
New Revision: 764

Added:
   trunk/dna-common/src/main/java/org/jboss/dna/common/text/XmlValueEncoder.java
   trunk/dna-common/src/test/java/org/jboss/dna/common/text/XmlValueEncoderTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java
   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/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.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/repositoryJackrabbitTck.xml
Log:
DNA-295 TCK Tests for Session.export* Methods Fail

Applied the "DNA-295_final.patch" and "DNA-295_namespace2.patch" files, and cleaned up a few very minor things (like changed the repositoryJackrabbitTck.xml file to be more DRY by removing the "jcr:primaryTime" attributes since the importer looks at the element name for the primary type).

Added: trunk/dna-common/src/main/java/org/jboss/dna/common/text/XmlValueEncoder.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/XmlValueEncoder.java	                        (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/XmlValueEncoder.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -0,0 +1,162 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.text;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An encoder useful for converting text to be used within XML attribute values.
+ * The following translations will be performed:
+ * <table cellspacing="0" cellpadding="1" border="1">
+ * <tr>
+ * <th>Raw (Unencoded)<br/>Character</th>
+ * <th>Translated (Encoded)<br/>Entity</th>
+ * </tr>
+ * <tr>
+ * <td> &amp; </td>
+ * <td> &amp;amp; </td>
+ * </tr>
+ * <tr>
+ * <td> &lt; </td>
+ * <td> &amp;lt; </td>
+ * </tr>
+ * <tr>
+ * <td> &gt; </td>
+ * <td> &amp;gt; </td>
+ * </tr>
+ * <tr>
+ * <td> &quot; </td>
+ * <td> &amp;quot; </td>
+ * </tr>
+ * <tr>
+ * <td> &#039; </td>
+ * <td> &amp;#039; </td>
+ * </tr>
+ * <tr>
+ * <td>All Others</td>
+ * <td>No Translation</td>
+ * </tr>
+ * </table>
+ * </p>
+ */
+public class XmlValueEncoder implements TextEncoder, TextDecoder {
+    
+    private static final Map<String, Character> SPECIAL_ENTITIES;
+    
+    static {
+        SPECIAL_ENTITIES = new HashMap<String, Character>();
+        
+        SPECIAL_ENTITIES.put("quot", '"');
+        SPECIAL_ENTITIES.put("gt", '>');
+        SPECIAL_ENTITIES.put("lt", '<');
+        SPECIAL_ENTITIES.put("amp", '&');
+        
+    }
+    
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.jboss.dna.common.text.TextEncoder#encode(java.lang.String)
+     */
+    public String encode( String text ) {
+        if (text == null) return null;
+        StringBuilder sb = new StringBuilder();
+        CharacterIterator iter = new StringCharacterIterator(text);
+        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
+            switch (c) {
+                case '&':
+                    sb.append("&amp;");
+                    break;
+                case '"':
+                    sb.append("&quot;");
+                    break;
+                case '<':
+                    sb.append("&lt;");
+                    break;
+                case '>':
+                    sb.append("&gt;");
+                    break;
+                case '\'':
+                    sb.append("&#039;");
+                    break;
+                default:
+                    sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.jboss.dna.common.text.TextDecoder#decode(java.lang.String)
+     */
+    public String decode( String encodedText ) {
+        if (encodedText == null) return null;
+        StringBuilder sb = new StringBuilder();
+        CharacterIterator iter = new StringCharacterIterator(encodedText);
+        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
+            if (c == '&') {
+                int index = iter.getIndex();
+                
+                do {
+                    c = iter.next();
+                }
+                while (c != CharacterIterator.DONE && c != ';');
+
+                // We found a closing semicolon
+                if (c == ';') {
+                    String s = encodedText.substring(index + 1, iter.getIndex());
+                    
+                    if (SPECIAL_ENTITIES.containsKey(s)) {
+                        sb.append(SPECIAL_ENTITIES.get(s));
+                        continue;
+                        
+                    }
+                    
+                    if (s.length() > 0 && s.charAt(0) == '#') {
+                        try {
+                            sb.append((char) Short.parseShort(s.substring(1, s.length())));
+                            continue;
+                        }
+                        catch (NumberFormatException nfe) {
+                            // This is possible in malformed encodings, but let it fall through
+                        }
+                    }
+                }
+                
+                // Malformed encoding, restore state and pass poorly encoded data back
+                c = '&';
+                iter.setIndex(index);                            
+            }
+
+            sb.append(c);
+
+        }
+        return sb.toString();
+    }
+}


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

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java	2009-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -23,6 +23,8 @@
  */
 package org.jboss.dna.common.util;
 
+import java.io.IOException;
+
 /**
  * <p>
  * Encodes and decodes to and from Base64 notation.
@@ -93,6 +95,37 @@
  */
 public class Base64 {
 
+    /* ********  P U B L I C   F I E L D S  ******** */
+
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+
+    /**
+     * Encode using Base64-like encoding that is URL- and Filename-safe as described in Section 4 of RFC3548: <a
+     * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. It is important to note that data
+     * encoded this way is <em>not</em> officially valid Base64, or at the very least should not be called Base64 without also
+     * specifying that is was encoded using the URL- and Filename-safe dialect.
+     */
+    public final static int URL_SAFE = 16;
+
+    /**
+     * Encode using the special "ordered" dialect of Base64 described here: <a
+     * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+     */
+    public final static int ORDERED = 32;
+
     /* ********  P R I V A T E   F I E L D S  ******** */
 
     /** Maximum line length (76) of Base64 output. */
@@ -107,12 +140,15 @@
     /** Preferred encoding. */
     private final static String PREFERRED_ENCODING = "UTF-8";
 
+    // I think I end up not using the BAD_ENCODING indicator.
+    // private final static byte BAD_ENCODING = -9; // Indicates error in encoding
     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
 
     /* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */
 
     /** The 64 valid Base64 values. */
+    // private final static byte[] ALPHABET;
     /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
     private final static byte[] _STANDARD_ALPHABET = {(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F',
         (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q',
@@ -158,6 +194,147 @@
     -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
     };
 
+    /* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
+
+    /**
+     * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: <a
+     * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. Notice that the last two bytes
+     * become "hyphen" and "underscore" instead of "plus" and "slash."
+     */
+    private final static byte[] _URL_SAFE_ALPHABET = {(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F',
+        (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q',
+        (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b',
+        (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',
+        (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
+        (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8',
+        (byte)'9', (byte)'-', (byte)'_'};
+
+    /**
+     * Used in decoding URL- and Filename-safe dialects of Base64.
+     */
+    private final static byte[] _URL_SAFE_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
+        -5, -5, // Whitespace: Tab and Linefeed
+        -9, -9, // Decimal 11 - 12
+        -5, // Whitespace: Carriage Return
+        -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
+        -9, -9, -9, -9, -9, // Decimal 27 - 31
+        -5, // Whitespace: Space
+        -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+        -9, // Plus sign at decimal 43
+        -9, // Decimal 44
+        62, // Minus sign at decimal 45
+        -9, // Decimal 46
+        -9, // Slash at decimal 47
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
+        -9, -9, -9, // Decimal 58 - 60
+        -1, // Equals sign at decimal 61
+        -9, -9, -9, // Decimal 62 - 64
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
+        14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
+        -9, -9, -9, -9, // Decimal 91 - 94
+        63, // Underscore at decimal 95
+        -9, // Decimal 96
+        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
+        39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
+        -9, -9, -9, -9 // Decimal 123 - 126
+    /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+    -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+    /* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
+
+    /**
+     * I don't get the point of this technique, but it is described here: <a
+     * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+     */
+    private final static byte[] _ORDERED_ALPHABET = {(byte)'-', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R',
+        (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'_', (byte)'a', (byte)'b',
+        (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',
+        (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
+        (byte)'y', (byte)'z'};
+
+    /**
+     * Used in decoding the "ordered" dialect of Base64.
+     */
+    private final static byte[] _ORDERED_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
+        -5, -5, // Whitespace: Tab and Linefeed
+        -9, -9, // Decimal 11 - 12
+        -5, // Whitespace: Carriage Return
+        -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
+        -9, -9, -9, -9, -9, // Decimal 27 - 31
+        -5, // Whitespace: Space
+        -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+        -9, // Plus sign at decimal 43
+        -9, // Decimal 44
+        0, // Minus sign at decimal 45
+        -9, // Decimal 46
+        -9, // Slash at decimal 47
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
+        -9, -9, -9, // Decimal 58 - 60
+        -1, // Equals sign at decimal 61
+        -9, -9, -9, // Decimal 62 - 64
+        11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M'
+        24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z'
+        -9, -9, -9, -9, // Decimal 91 - 94
+        37, // Underscore at decimal 95
+        -9, // Decimal 96
+        38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm'
+        51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z'
+        -9, -9, -9, -9 // Decimal 123 - 126
+    /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+    /* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
+
+    /**
+     * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options specified. It's possible, though silly, to
+     * specify ORDERED and URLSAFE in which case one of them will be picked, though there is no guarantee as to which one will be
+     * picked.
+     * 
+     * @param options The options to use in this operation
+     * @return the appropriate alphabet
+     */
+    private final static byte[] getAlphabet( int options ) {
+        if ((options & URL_SAFE) == URL_SAFE) return _URL_SAFE_ALPHABET;
+        else if ((options & ORDERED) == ORDERED) return _ORDERED_ALPHABET;
+        else return _STANDARD_ALPHABET;
+
+    } // end getAlphabet
+
+    /**
+     * Returns one of the _SOMETHING_DECODABET byte arrays depending on the options specified. It's possible, though silly, to
+     * specify ORDERED and URL_SAFE in which case one of them will be picked, though there is no guarantee as to which one will be
+     * picked.
+     * 
+     * @param options The options to use in this operation
+     * @return the appropriate decodabets
+     */
+    private final static byte[] getDecodabet( int options ) {
+        if ((options & URL_SAFE) == URL_SAFE) return _URL_SAFE_DECODABET;
+        else if ((options & ORDERED) == ORDERED) return _ORDERED_DECODABET;
+        else return _STANDARD_DECODABET;
+
+    } // end getAlphabet
+
     /** Defeats instantiation. */
     private Base64() {
     }
@@ -165,6 +342,26 @@
     /* ********  E N C O D I N G   M E T H O D S  ******** */
 
     /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var> and returns a four-byte array in Base64 notation. The
+     * actual number of significant bytes in your array is given by <var>numSigBytes</var>. The array <var>threeBytes</var> needs
+     * only be as big as <var>numSigBytes</var>. Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     * 
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @param options The options to use in this operation
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4,
+                                      byte[] threeBytes,
+                                      int numSigBytes,
+                                      int options ) {
+        encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
+        return b4;
+    } // end encode3to4
+
+    /**
      * <p>
      * Encodes up to three bytes of the array <var>source</var> and writes the resulting four Base64 bytes to
      * <var>destination</var>. The source and destination arrays can be manipulated anywhere along their length by specifying
@@ -181,6 +378,7 @@
      * @param numSigBytes the number of significant bytes in your array
      * @param destination the array to hold the conversion
      * @param destOffset the index where output will be put
+     * @param options The options to use in this operation
      * @return the <var>destination</var> array
      * @since 1.3
      */
@@ -188,8 +386,9 @@
                                       int srcOffset,
                                       int numSigBytes,
                                       byte[] destination,
-                                      int destOffset ) {
-        byte[] ALPHABET = _STANDARD_ALPHABET;
+                                      int destOffset,
+                                      int options ) {
+        byte[] ALPHABET = getAlphabet(options);
 
         // 1 2 3
         // 01234567890123456789012345678901 Bit position
@@ -234,50 +433,268 @@
     } // end encode3to4
 
     /**
-     * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+     * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be serialized
+     * or there is another error, the method will return <tt>null</tt>. The object is not GZip-compressed before being encoded.
      * 
-     * @param source The data to convert
-     * @return the encoded data
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
      * @since 1.4
      */
-    public static String encodeBytes( byte[] source ) {
-        // Convert option to boolean in way that code likes it.
-        boolean breakLines = false;
-        int len = source.length;
-        int len43 = len * 4 / 3;
-        byte[] outBuff = new byte[(len43) // Main 4:3
-                                  + ((len % 3) > 0 ? 4 : 0) // Account for padding
-                                  + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
-        int d = 0;
-        int e = 0;
-        int len2 = len - 2;
-        int lineLength = 0;
-        for (; d < len2; d += 3, e += 4) {
-            encode3to4(source, d, 3, outBuff, e);
+    public static String encodeObject( java.io.Serializable serializableObject ) {
+        return encodeObject(serializableObject, NO_OPTIONS);
+    } // end encodeObject
 
-            lineLength += 4;
-            if (breakLines && lineLength == MAX_LINE_LENGTH) {
-                outBuff[e + 4] = NEW_LINE;
-                e++;
-                lineLength = 0;
-            } // end if: end of line
-        } // en dfor: each piece of array
+    /**
+     * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be serialized
+     * or there is another error, the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:
+     * 
+     * <pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     * 
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject,
+                                       int options ) {
+        // Streams
+        java.io.ByteArrayOutputStream baos = null;
+        java.io.OutputStream b64os = null;
+        java.io.ObjectOutputStream oos = null;
+        java.util.zip.GZIPOutputStream gzos = null;
 
-        if (d < len) {
-            encode3to4(source, d, len - d, outBuff, e);
-            e += 4;
-        } // end if: some padding needed
+        // Isolate options
+        int gzip = (options & GZIP);
+        int dontBreakLines = (options & DONT_BREAK_LINES);
 
+        try {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos = new java.io.ByteArrayOutputStream();
+            b64os = new Base64.OutputStream(baos, ENCODE | options);
+
+            // GZip?
+            if (gzip == GZIP) {
+                gzos = new java.util.zip.GZIPOutputStream(b64os);
+                oos = new java.io.ObjectOutputStream(gzos);
+            } // end if: gzip
+            else oos = new java.io.ObjectOutputStream(b64os);
+
+            oos.writeObject(serializableObject);
+        } // end try
+        catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } // end catch
+        finally {
+            try {
+                oos.close();
+            } catch (Exception e) {
+            }
+            try {
+                gzos.close();
+            } catch (Exception e) {
+            }
+            try {
+                b64os.close();
+            } catch (Exception e) {
+            }
+            try {
+                baos.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
         // Return value according to relevant encoding.
         try {
-            return new String(outBuff, 0, e, PREFERRED_ENCODING);
+            return new String(baos.toByteArray(), PREFERRED_ENCODING);
         } // end try
         catch (java.io.UnsupportedEncodingException uue) {
-            return new String(outBuff, 0, e);
+            return new String(baos.toByteArray());
         } // end catch
 
-    } // end else: don't compress
+    } // end encode
 
+    /**
+     * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+     * 
+     * @param source The data to convert
+     * @return the encoded bytes
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source ) {
+        return encodeBytes(source, 0, source.length, NO_OPTIONS);
+    } // end encodeBytes
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:
+     * 
+     * <pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     * 
+     * @param source The data to convert
+     * @param options Specified options
+     * @return the encoded bytes
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source,
+                                      int options ) {
+        return encodeBytes(source, 0, source.length, options);
+    } // end encodeBytes
+
+    /**
+     * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+     * 
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data
+     * @return the encoded bytes
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source,
+                                      int off,
+                                      int len ) {
+        return encodeBytes(source, off, len, NO_OPTIONS);
+    } // end encodeBytes
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:
+     * 
+     * <pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     * 
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options Specified options- the alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return the encoded bytes
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source,
+                                      int off,
+                                      int len,
+                                      int options ) {
+        // Isolate options
+        int dontBreakLines = (options & DONT_BREAK_LINES);
+        int gzip = (options & GZIP);
+
+        // Compress?
+        if (gzip == GZIP) {
+            java.io.ByteArrayOutputStream baos = null;
+            java.util.zip.GZIPOutputStream gzos = null;
+            Base64.OutputStream b64os = null;
+
+            try {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream(baos, ENCODE | options);
+                gzos = new java.util.zip.GZIPOutputStream(b64os);
+
+                gzos.write(source, off, len);
+                gzos.close();
+            } // end try
+            catch (IOException e) {
+                e.printStackTrace();
+                return null;
+            } // end catch
+            finally {
+                try {
+                    gzos.close();
+                } catch (Exception e) {
+                }
+                try {
+                    b64os.close();
+                } catch (Exception e) {
+                }
+                try {
+                    baos.close();
+                } catch (Exception e) {
+                }
+            } // end finally
+
+            // Return value according to relevant encoding.
+            try {
+                return new String(baos.toByteArray(), PREFERRED_ENCODING);
+            } // end try
+            catch (java.io.UnsupportedEncodingException uue) {
+                return new String(baos.toByteArray());
+            } // end catch
+        } // end if: compress
+
+        // Else, don't compress. Better not to use streams at all then.
+        else {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+
+            int len43 = len * 4 / 3;
+            byte[] outBuff = new byte[(len43) // Main 4:3
+                                      + ((len % 3) > 0 ? 4 : 0) // Account for padding
+                                      + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for (; d < len2; d += 3, e += 4) {
+                encode3to4(source, d + off, 3, outBuff, e, options);
+
+                lineLength += 4;
+                if (breakLines && lineLength == MAX_LINE_LENGTH) {
+                    outBuff[e + 4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                } // end if: end of line
+            } // en dfor: each piece of array
+
+            if (d < len) {
+                encode3to4(source, d + off, len - d, outBuff, e, options);
+                e += 4;
+            } // end if: some padding needed
+
+            // Return value according to relevant encoding.
+            try {
+                return new String(outBuff, 0, e, PREFERRED_ENCODING);
+            } // end try
+            catch (java.io.UnsupportedEncodingException uue) {
+                return new String(outBuff, 0, e);
+            } // end catch
+
+        } // end else: don't compress
+
+    } // end encodeBytes
+
     /* ********  D E C O D I N G   M E T H O D S  ******** */
 
     /**
@@ -293,15 +710,17 @@
      * @param source the array to convert
      * @param srcOffset the index where conversion begins
      * @param destination the array to hold the conversion
-     * @param destOffset destination offset
+     * @param destOffset the index where output will be put
+     * @param options alphabet type is pulled from this (standard, url-safe, ordered)
      * @return the number of decoded bytes converted
      * @since 1.3
      */
     private static int decode4to3( byte[] source,
                                    int srcOffset,
                                    byte[] destination,
-                                   int destOffset ) {
-        byte[] DECODABET = _STANDARD_DECODABET;
+                                   int destOffset,
+                                   int options ) {
+        byte[] DECODABET = getDecodabet(options);
 
         // Example: Dk==
         if (source[srcOffset + 2] == EQUALS_SIGN) {
@@ -330,44 +749,49 @@
 
         // Example: DkLE
         else {
-            // Two ways to do the same thing. Don't know which way I like best.
-            // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
-            // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
-            // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
-            // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
-            int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
-                          | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
+            try {
+                // Two ways to do the same thing. Don't know which way I like best.
+                // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
+                // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+                // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+                // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+                int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
+                              | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
 
-            destination[destOffset] = (byte)(outBuff >> 16);
-            destination[destOffset + 1] = (byte)(outBuff >> 8);
-            destination[destOffset + 2] = (byte)(outBuff);
+                destination[destOffset] = (byte)(outBuff >> 16);
+                destination[destOffset + 1] = (byte)(outBuff >> 8);
+                destination[destOffset + 2] = (byte)(outBuff);
 
-            return 3;
+                return 3;
+            } catch (Exception e) {
+                System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
+                System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
+                System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
+                System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
+                return -1;
+            } // end catch
         }
     } // end decodeToBytes
 
     /**
-     * Decodes data from Base64 notation.
+     * Very low-level access to decoding ASCII characters in the form of a byte array. Does not support automatically gunzipping
+     * or any other "fancy" features.
      * 
-     * @param s the string to decode
-     * @return the decoded data
-     * @since 1.4
+     * @param source The Base64 encoded data
+     * @param off The offset of where to begin decoding
+     * @param len The length of characters to decode
+     * @param options The options to use in this operation
+     * @return decoded data
+     * @since 1.3
      */
-    public static byte[] decode( String s ) {
-        byte[] source;
-        try {
-            source = s.getBytes(PREFERRED_ENCODING);
-        } // end try
-        catch (java.io.UnsupportedEncodingException uee) {
-            source = s.getBytes();
-        } // end catch
-        // </change>
-        if (source.length % 4 != 0) {
-            throw new IllegalArgumentException("Source bytes are not valid"); //$NON-NLS-1$
-        }
-        byte[] DECODABET = _STANDARD_DECODABET;
-        int len = source.length;
-        byte[] outBuff = new byte[len * 3 / 4]; // Upper limit on size of output
+    public static byte[] decode( byte[] source,
+                                 int off,
+                                 int len,
+                                 int options ) {
+        byte[] DECODABET = getDecodabet(options);
+
+        int len34 = len * 3 / 4;
+        byte[] outBuff = new byte[len34]; // Upper limit on size of output
         int outBuffPosn = 0;
 
         byte[] b4 = new byte[4];
@@ -375,7 +799,7 @@
         int i = 0;
         byte sbiCrop = 0;
         byte sbiDecode = 0;
-        for (i = 0; i < len; i++) {
+        for (i = off; i < off + len; i++) {
             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
             sbiDecode = DECODABET[sbiCrop];
 
@@ -384,7 +808,7 @@
                 if (sbiDecode >= EQUALS_SIGN_ENC) {
                     b4[b4Posn++] = sbiCrop;
                     if (b4Posn > 3) {
-                        outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
+                        outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
                         b4Posn = 0;
 
                         // If that was the equals sign, break out of 'for' loop
@@ -395,7 +819,8 @@
 
             } // end if: white space, equals sign or better
             else {
-                throw new IllegalArgumentException("Source bytes are not valid"); //$NON-NLS-1$
+                System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
+                return null;
             } // end else:
         } // each input character
 
@@ -403,4 +828,763 @@
         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
         return out;
     } // end decode
-}
+
+    /**
+     * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
+     * 
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s ) {
+        return decode(s, NO_OPTIONS);
+    }
+
+    /**
+     * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
+     * 
+     * @param s the string to decode
+     * @param options encode options such as URL_SAFE
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s,
+                                 int options ) {
+        byte[] bytes;
+        try {
+            bytes = s.getBytes(PREFERRED_ENCODING);
+        } // end try
+        catch (java.io.UnsupportedEncodingException uee) {
+            bytes = s.getBytes();
+        } // end catch
+        // </change>
+
+        // Decode
+        bytes = decode(bytes, 0, bytes.length, options);
+
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if (bytes != null && bytes.length >= 4) {
+
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+            if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
+                java.io.ByteArrayInputStream bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int length = 0;
+
+                try {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream(bytes);
+                    gzis = new java.util.zip.GZIPInputStream(bais);
+
+                    while ((length = gzis.read(buffer)) >= 0) {
+                        baos.write(buffer, 0, length);
+                    } // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                } // end try
+                catch (IOException e) {
+                    // Just return originally-decoded bytes
+                } // end catch
+                finally {
+                    try {
+                        baos.close();
+                    } catch (Exception e) {
+                    }
+                    try {
+                        gzis.close();
+                    } catch (Exception e) {
+                    }
+                    try {
+                        bais.close();
+                    } catch (Exception e) {
+                    }
+                } // end finally
+
+            } // end if: gzipped
+        } // end if: bytes.length >= 2
+
+        return bytes;
+    } // end decode
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java Object within. Returns <tt>null</tt> if there was an error.
+     * 
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject ) {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode(encodedObject);
+
+        java.io.ByteArrayInputStream bais = null;
+        java.io.ObjectInputStream ois = null;
+        Object obj = null;
+
+        try {
+            bais = new java.io.ByteArrayInputStream(objBytes);
+            ois = new java.io.ObjectInputStream(bais);
+
+            obj = ois.readObject();
+        } // end try
+        catch (IOException e) {
+            e.printStackTrace();
+            obj = null;
+        } // end catch
+        catch (java.lang.ClassNotFoundException e) {
+            e.printStackTrace();
+            obj = null;
+        } // end catch
+        finally {
+            try {
+                bais.close();
+            } catch (Exception e) {
+            }
+            try {
+                ois.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
+        return obj;
+    } // end decodeObject
+
+    /**
+     * Convenience method for encoding data to a file.
+     * 
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode,
+                                        String filename ) {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try {
+            bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
+            bos.write(dataToEncode);
+            success = true;
+        } // end try
+        catch (IOException e) {
+
+            success = false;
+        } // end catch: IOException
+        finally {
+            try {
+                bos.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
+        return success;
+    } // end encodeToFile
+
+    /**
+     * Convenience method for decoding data to a file.
+     * 
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode,
+                                        String filename ) {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try {
+            bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
+            bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
+            success = true;
+        } // end try
+        catch (IOException e) {
+            success = false;
+        } // end catch: IOException
+        finally {
+            try {
+                bos.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
+        return success;
+    } // end decodeToFile
+
+    /**
+     * Convenience method for reading a base64-encoded file and decoding it.
+     * 
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename ) {
+        byte[] decodedData = null;
+        Base64.InputStream bis = null;
+        try {
+            // Set up some useful variables
+            java.io.File file = new java.io.File(filename);
+            byte[] buffer = null;
+            int length = 0;
+            int numBytes = 0;
+
+            // Check for size of file
+            if (file.length() > Integer.MAX_VALUE) {
+                System.err.println("File is too big for this convenience method (" + file.length() + " bytes).");
+                return null;
+            } // end if: file too big for int index
+            buffer = new byte[(int)file.length()];
+
+            // Open a stream
+            bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.DECODE);
+
+            // Read until done
+            while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
+                length += numBytes;
+
+            // Save in a variable to return
+            decodedData = new byte[length];
+            System.arraycopy(buffer, 0, decodedData, 0, length);
+
+        } // end try
+        catch (IOException e) {
+            System.err.println("Error decoding from file " + filename);
+        } // end catch: IOException
+        finally {
+            try {
+                bis.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
+        return decodedData;
+    } // end decodeFromFile
+
+    /**
+     * Convenience method for reading a binary file and base64-encoding it.
+     * 
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename ) {
+        String encodedData = null;
+        Base64.InputStream bis = null;
+        try {
+            // Set up some useful variables
+            java.io.File file = new java.io.File(filename);
+            byte[] buffer = new byte[Math.max((int)(file.length() * 1.4), 40)]; // Need max() for math on small files (v2.2.1)
+            int length = 0;
+            int numBytes = 0;
+
+            // Open a stream
+            bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.ENCODE);
+
+            // Read until done
+            while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
+                length += numBytes;
+
+            // Save in a variable to return
+            encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
+
+        } // end try
+        catch (IOException e) {
+            System.err.println("Error encoding from file " + filename);
+        } // end catch: IOException
+        finally {
+            try {
+                bis.close();
+            } catch (Exception e) {
+            }
+        } // end finally
+
+        return encodedData;
+    } // end encodeFromFile
+
+    /**
+     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
+     * 
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean encodeFileToFile( String infile,
+                                            String outfile ) {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try {
+            in = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(infile)), Base64.ENCODE);
+            out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while ((read = in.read(buffer)) >= 0) {
+                out.write(buffer, 0, read);
+            } // end while: through file
+            success = true;
+        } catch (IOException exc) {
+            exc.printStackTrace();
+        } finally {
+            try {
+                in.close();
+            } catch (Exception exc) {
+            }
+            try {
+                out.close();
+            } catch (Exception exc) {
+            }
+        } // end finally
+
+        return success;
+    } // end encodeFileToFile
+
+    /**
+     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
+     * 
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean decodeFileToFile( String infile,
+                                            String outfile ) {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try {
+            in = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(infile)), Base64.DECODE);
+            out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while ((read = in.read(buffer)) >= 0) {
+                out.write(buffer, 0, read);
+            } // end while: through file
+            success = true;
+        } catch (IOException exc) {
+            exc.printStackTrace();
+        } finally {
+            try {
+                in.close();
+            } catch (Exception exc) {
+            }
+            try {
+                out.close();
+            } catch (Exception exc) {
+            }
+        } // end finally
+
+        return success;
+    } // end decodeFileToFile
+
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+
+    /**
+     * A {@link Base64.InputStream} will read data from another <tt>java.io.InputStream</tt>, given in the constructor, and
+     * encode/decode to/from Base64 notation on the fly.
+     * 
+     * @see Base64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream {
+        private boolean encode; // Encoding or decoding
+        private int position; // Current position in the buffer
+        private byte[] buffer; // Small buffer holding converted data
+        private int bufferLength; // Length of buffer (3 or 4)
+        private int numSigBytes; // Number of meaningful bytes in the buffer
+        private int lineLength;
+        private boolean breakLines; // Break lines at less than 80 characters
+        private int options; // Record options used to create the stream.
+        private byte[] alphabet; // Local copies to avoid extra method calls
+        private byte[] decodabet; // Local copies to avoid extra method calls
+
+        /**
+         * Constructs a {@link Base64.InputStream} in DECODE mode.
+         * 
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in ) {
+            this(in, DECODE);
+        } // end constructor
+
+        /**
+         * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:
+         * 
+         * <pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+         * 
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in,
+                            int options ) {
+            super(in);
+            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer = new byte[bufferLength];
+            this.position = -1;
+            this.lineLength = 0;
+            this.options = options; // Record for later, mostly to determine which alphabet to use
+            this.alphabet = getAlphabet(options);
+            this.decodabet = getDecodabet(options);
+        } // end constructor
+
+        /**
+         * Reads enough of the input stream to convert to/from Base64 and returns the next byte.
+         * 
+         * @return next byte
+         * @throws IOException
+         * @since 1.3
+         */
+        @Override
+        public int read() throws IOException {
+            // Do we need to get data?
+            if (position < 0) {
+                if (encode) {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for (int i = 0; i < 3; i++) {
+                        try {
+                            int b = in.read();
+
+                            // If end of stream, b is -1.
+                            if (b >= 0) {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            } // end if: not end of stream
+
+                        } // end try: read
+                        catch (IOException e) {
+                            // Only a problem if we got no data at all.
+                            if (i == 0) throw e;
+
+                        } // end catch
+                    } // end for: each needed input byte
+
+                    if (numBinaryBytes > 0) {
+                        encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
+                        position = 0;
+                        numSigBytes = 4;
+                    } // end if: got data
+                    else {
+                        return -1;
+                    } // end else
+                } // end if: encoding
+
+                // Else decoding
+                else {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for (i = 0; i < 4; i++) {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do {
+                            b = in.read();
+                        } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
+
+                        if (b < 0) break; // Reads a -1 if end of stream
+
+                        b4[i] = (byte)b;
+                    } // end for: each needed input byte
+
+                    if (i == 4) {
+                        numSigBytes = decode4to3(b4, 0, buffer, 0, options);
+                        position = 0;
+                    } // end if: got four characters
+                    else if (i == 0) {
+                        return -1;
+                    } // end else if: also padded correctly
+                    else {
+                        // Must have broken out from above.
+                        throw new IOException("Improperly padded Base64 input.");
+                    } // end
+
+                } // end else: decode
+            } // end else: get data
+
+            // Got data?
+            if (position >= 0) {
+                // End of relevant data?
+                if ( /*!encode &&*/position >= numSigBytes) return -1;
+
+                if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
+                    lineLength = 0;
+                    return '\n';
+                } // end if
+                else {
+                    lineLength++; // This isn't important when decoding
+                    // but throwing an extra "if" seems
+                    // just as wasteful.
+
+                    int b = buffer[position++];
+
+                    if (position >= bufferLength) position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                    // intended to be unsigned.
+                } // end else
+            } // end if: position >= 0
+
+            // Else error
+            else {
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new IOException("Error in Base64 code reading stream.");
+            } // end else
+        } // end read
+
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream is reached or <var>len</var> bytes are read. Returns number of
+         * bytes read into array or -1 if end of stream is encountered.
+         * 
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @throws IOException
+         * @since 1.3
+         */
+        @Override
+        public int read( byte[] dest,
+                         int off,
+                         int len ) throws IOException {
+            int i;
+            int b;
+            for (i = 0; i < len; i++) {
+                b = read();
+
+                // if( b < 0 && i == 0 )
+                // return -1;
+
+                if (b >= 0) dest[off + i] = (byte)b;
+                else if (i == 0) return -1;
+                else break; // Out of 'for' loop
+            } // end for: each byte read
+            return i;
+        } // end read
+
+    } // end inner class InputStream
+
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+
+    /**
+     * A {@link Base64.OutputStream} will write data to another <tt>java.io.OutputStream</tt>, given in the constructor, and
+     * encode/decode to/from Base64 notation on the fly.
+     * 
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream {
+        private boolean encode;
+        private int position;
+        private byte[] buffer;
+        private int bufferLength;
+        private int lineLength;
+        private boolean breakLines;
+        private byte[] b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+        private int options; // Record for later
+        private byte[] alphabet; // Local copies to avoid extra method calls
+        private byte[] decodabet; // Local copies to avoid extra method calls
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         * 
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out ) {
+            this(out, ENCODE);
+        } // end constructor
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:
+         * 
+         * <pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         * 
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out,
+                             int options ) {
+            super(out);
+            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer = new byte[bufferLength];
+            this.position = 0;
+            this.lineLength = 0;
+            this.suspendEncoding = false;
+            this.b4 = new byte[4];
+            this.options = options;
+            this.alphabet = getAlphabet(options);
+            this.decodabet = getDecodabet(options);
+        } // end constructor
+
+        /**
+         * Writes the byte to the output stream after converting to/from Base64 notation. When encoding, bytes are buffered three
+         * at a time before the output stream actually gets a write() call. When decoding, bytes are buffered four at a time.
+         * 
+         * @param theByte the byte to write
+         * @throws IOException
+         * @since 1.3
+         */
+        @Override
+        public void write( int theByte ) throws IOException {
+            // Encoding suspended?
+            if (suspendEncoding) {
+                super.out.write(theByte);
+                return;
+            } // end if: supsended
+
+            // Encode?
+            if (encode) {
+                buffer[position++] = (byte)theByte;
+                if (position >= bufferLength) // Enough to encode.
+                {
+                    out.write(encode3to4(b4, buffer, bufferLength, options));
+
+                    lineLength += 4;
+                    if (breakLines && lineLength >= MAX_LINE_LENGTH) {
+                        out.write(NEW_LINE);
+                        lineLength = 0;
+                    } // end if: end of line
+
+                    position = 0;
+                } // end if: enough to output
+            } // end if: encoding
+
+            // Else, Decoding
+            else {
+                // Meaningful Base64 character?
+                if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
+                    buffer[position++] = (byte)theByte;
+                    if (position >= bufferLength) // Enough to output.
+                    {
+                        int len = Base64.decode4to3(buffer, 0, b4, 0, options);
+                        out.write(b4, 0, len);
+                        // out.write( Base64.decode4to3( buffer ) );
+                        position = 0;
+                    } // end if: enough to output
+                } // end if: meaningful base64 character
+                else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
+                    throw new IOException("Invalid character in Base64 data.");
+                } // end else: not white space either
+            } // end else: decoding
+        } // end write
+
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are written.
+         * 
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @throws IOException
+         * @since 1.3
+         */
+        @Override
+        public void write( byte[] theBytes,
+                           int off,
+                           int len ) throws IOException {
+            // Encoding suspended?
+            if (suspendEncoding) {
+                super.out.write(theBytes, off, len);
+                return;
+            } // end if: supsended
+
+            for (int i = 0; i < len; i++) {
+                write(theBytes[off + i]);
+            } // end for: each byte written
+
+        } // end write
+
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without closing the stream.
+         * 
+         * @throws IOException
+         */
+        public void flushBase64() throws IOException {
+            if (position > 0) {
+                if (encode) {
+                    out.write(encode3to4(b4, buffer, position, options));
+                    position = 0;
+                } // end if: encoding
+                else {
+                    throw new IOException("Base64 input not properly padded.");
+                } // end else: decoding
+            } // end if: buffer partially full
+
+        } // end flush
+
+        /**
+         * Flushes and closes (I think, in the superclass) the stream.
+         * 
+         * @throws IOException
+         * @since 1.3
+         */
+        @Override
+        public void close() throws IOException {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+
+            buffer = null;
+            out = null;
+        } // end close
+
+        /**
+         * Suspends encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a stream.
+         * 
+         * @throws IOException
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws IOException {
+            flushBase64();
+            this.suspendEncoding = true;
+        } // end suspendEncoding
+
+        /**
+         * Resumes encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a stream.
+         * 
+         * @since 1.5.1
+         */
+        public void resumeEncoding() {
+            this.suspendEncoding = false;
+        } // end resumeEncoding
+
+    } // end inner class OutputStream
+
+} // end class Base64

Added: trunk/dna-common/src/test/java/org/jboss/dna/common/text/XmlValueEncoderTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/XmlValueEncoderTest.java	                        (rev 0)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/XmlValueEncoderTest.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -0,0 +1,110 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.text;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+public class XmlValueEncoderTest {
+
+    private XmlValueEncoder encoder = new XmlValueEncoder();
+
+    @Before
+    public void beforeEach() {
+    }
+
+    protected void checkEncoding( String input,
+                                  String expected ) {
+        String output = this.encoder.encode(input);
+        assertThat(output, is(notNullValue()));
+        assertEquals(expected, output);
+        assertThat(output.length(), is(expected.length()));
+        assertThat(output, is(expected));
+
+        checkDecoding(output, input);
+    }
+
+    protected void checkForNoEncoding( String input ) {
+        String output = this.encoder.encode(input);
+        assertThat(output, is(notNullValue()));
+        assertEquals(input, output);
+        assertThat(output.length(), is(input.length()));
+        assertThat(output, is(input));
+
+        checkForNoDecoding(input);
+    }
+
+    protected void checkForNoDecoding( String input ) {
+        String output = this.encoder.decode(input);
+        assertThat(output, is(notNullValue()));
+        assertEquals(input, output);
+        assertThat(output.length(), is(input.length()));
+        assertThat(output, is(input));
+    }
+
+    protected void checkDecoding( String input,
+                                  String output ) {
+        String decoded = this.encoder.decode(input);
+        assertEquals(output, decoded);
+        assertThat(decoded.length(), is(output.length()));
+        assertThat(decoded, is(output));
+    }
+
+    @Test 
+    public void shouldEncodeStringWithNoSpecialChars() {
+        checkForNoEncoding("The quick brown fox jumped over the lazy dog.?+=!@#$%^*()_+-[]{}|\\");
+    }
+
+    @Test
+    public void shouldEncodeStringWithSpecialChars() {
+        checkEncoding("<>&'\"", "&lt;&gt;&amp;&#039;&quot;");
+    }
+
+    @Test
+    public void shouldHandleTrivialCase() {
+        assertNull(encoder.encode(null));
+        assertNull(encoder.decode(null));
+        checkEncoding("", "");
+
+    }
+
+    @Test
+    public void shouldDecodeStringWithInvalidMappings() {
+        checkDecoding("&amp", "&amp");
+        checkDecoding("&quot", "&quot");
+        checkDecoding("&gt", "&gt");
+        checkDecoding("&lt", "&lt");
+        checkDecoding("amp;", "amp;");
+        checkDecoding("quot;", "quot;");
+        checkDecoding("gt;", "gt;");
+        checkDecoding("lt;", "lt;");
+        checkDecoding("&;", "&;");
+        checkDecoding("&amp;&", "&&");
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -0,0 +1,359 @@
+/*
+ * 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;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.jcr.ItemVisitor;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.text.XmlNameEncoder;
+import org.jboss.dna.common.text.XmlValueEncoder;
+import org.jboss.dna.graph.property.Name;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Superclass of DNA JCR exporters, provides basic support for traversing through the nodes recursively (if needed), exception
+ * wrapping (since {@link ItemVisitor} does not allow checked exceptions to be thrown from its visit* methods, and the ability to
+ * wrap an {@link OutputStream} with a {@link ContentHandler}.
+ * <p />
+ * Each exporter is only intended to be used once (by calling <code>exportView</code>) and discarded. This class is <b>NOT</b>
+ * thread-safe.
+ * 
+ * @see JcrSystemViewExporter
+ * @see JcrDocumentViewExporter
+ */
+ at NotThreadSafe
+abstract class AbstractJcrExporter {
+
+    /**
+     * Encoder to properly escape XML names.
+     * 
+     * @see XmlNameEncoder
+     */
+    private static final TextEncoder NAME_ENCODER = new XmlNameEncoder();
+
+    /**
+     * The session in which this exporter was created.
+     */
+    protected final JcrSession session;
+
+    /**
+     * The list of XML namespace prefixes that should never be exported.
+     */
+    private final Collection<String> restrictedPrefixes;
+
+    /**
+     * Creates the exporter
+     * 
+     * @param session the session in which the exporter is created
+     * @param restrictedPrefixes the list of XML namespace prefixes that should not be exported
+     */
+    AbstractJcrExporter( JcrSession session,
+                         Collection<String> restrictedPrefixes ) {
+        this.session = session;
+        this.restrictedPrefixes = restrictedPrefixes;
+    }
+
+    /**
+     * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by invoking SAX events on
+     * <code>contentHandler</code>.
+     * 
+     * @param exportRootNode the node which should be exported. If <code>noRecursion</code> was set to <code>false</code> in the
+     *        constructor, the entire subtree rooted at <code>node</code> will 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
+     * @param noRecurse if<code>true</code>, indicates that only the given node should be exported, otherwise a recursice export
+     *        and not any of its child nodes.
+     * @throws SAXException if an exception occurs during generation of the XML document
+     * @throws RepositoryException if an exception occurs accessing the content repository
+     */
+    public void exportView( Node exportRootNode,
+                            ContentHandler contentHandler,
+                            boolean skipBinary,
+                            boolean noRecurse ) throws RepositoryException, SAXException {
+        assert exportRootNode != null;
+        assert contentHandler != null;
+
+        // Export the namespace mappings used in this session
+        NamespaceRegistry registry = session.getWorkspace().getNamespaceRegistry();
+
+        contentHandler.startDocument();
+        String[] namespacePrefixes = registry.getPrefixes();
+        for (int i = 0; i < namespacePrefixes.length; i++) {
+            String prefix = namespacePrefixes[i];
+
+            if (!restrictedPrefixes.contains(prefix)) {
+                contentHandler.startPrefixMapping(prefix, registry.getURI(prefix));
+            }
+        }
+
+        exportNode(exportRootNode, contentHandler, skipBinary, noRecurse);
+
+        for (int i = 0; i < namespacePrefixes.length; i++) {
+            if (!restrictedPrefixes.contains(namespacePrefixes[i])) {
+                contentHandler.endPrefixMapping(namespacePrefixes[i]);
+            }
+        }
+
+        contentHandler.endDocument();
+    }
+
+    /**
+     * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document that is written to
+     * <code>os</code>.
+     * 
+     * @param node the node which should be exported. If <code>noRecursion</code> was set to <code>false</code> in the
+     *        constructor, the entire subtree rooted at <code>node</code> will be exported.
+     * @param os the {@link OutputStream} to which the XML document will be written
+     * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
+     * @param noRecurse if<code>true</code>, indicates that only the given node should be exported, otherwise a recursive export
+     *        and not any of its child nodes.
+     * @throws RepositoryException if an exception occurs accessing the content repository, generating the XML document, or
+     *         writing it to the output stream <code>os</code>.
+     */
+    public void exportView( Node node,
+                            OutputStream os,
+                            boolean skipBinary,
+                            boolean noRecurse ) throws RepositoryException {
+        try {
+            exportView(node, new StreamingContentHandler(os), skipBinary, noRecurse);
+            os.flush();
+        } catch (IOException ioe) {
+            throw new RepositoryException(ioe);
+        } catch (SAXException se) {
+            throw new RepositoryException(se);
+        }
+    }
+
+    /**
+     * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by invoking SAX events on
+     * <code>contentHandler</code>.
+     * 
+     * @param node the node which should be exported. If <code>noRecursion</code> was set to <code>false</code> in the
+     *        constructor, the entire subtree rooted at <code>node</code> will 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
+     * @param noRecurse if<code>true</code>, indicates that only the given node should be exported, otherwise a recursive export
+     *        and not any of its child nodes.
+     * @throws SAXException if an exception occurs during generation of the XML document
+     * @throws RepositoryException if an exception occurs accessing the content repository
+     */
+    public abstract void exportNode( Node node,
+                                     ContentHandler contentHandler,
+                                     boolean skipBinary,
+                                     boolean noRecurse ) throws RepositoryException, SAXException;
+
+    /**
+     * Convenience method to invoke the {@link ContentHandler#startElement(String, String, String, Attributes)} method on the
+     * given content handler. The name will be encoded to properly escape invalid XML characters.
+     * 
+     * @param contentHandler the content handler on which the <code>startElement</code> method should be invoked.
+     * @param name the un-encoded, un-prefixed name of the element to start
+     * @param atts the attributes that should be created for the given element
+     * @throws SAXException if there is an error starting the element
+     */
+    protected void startElement( ContentHandler contentHandler,
+                                 Name name,
+                                 Attributes atts ) throws SAXException {
+        contentHandler.startElement(name.getNamespaceUri(),
+                                    NAME_ENCODER.encode(name.getLocalName()),
+                                    NAME_ENCODER.encode(name.getString(session.getExecutionContext().getNamespaceRegistry())),
+                                    atts);
+    }
+
+    /**
+     * Convenience method to invoke the {@link ContentHandler#endElement(String, String, String)} method on the given content
+     * handler. The name will be encoded to properly escape invalid XML characters.
+     * 
+     * @param contentHandler the content handler on which the <code>endElement</code> method should be invoked.
+     * @param name the un-encoded, un-prefixed name of the element to end
+     * @throws SAXException if there is an error ending the element
+     */
+    protected void endElement( ContentHandler contentHandler,
+                               Name name ) throws SAXException {
+        contentHandler.endElement(name.getNamespaceUri(),
+                                  NAME_ENCODER.encode(name.getLocalName()),
+                                  NAME_ENCODER.encode(name.getString(session.getExecutionContext().getNamespaceRegistry())));
+    }
+
+    /**
+     * Helper class that adapts an arbitrary, open {@link OutputStream} to the {@link ContentHandler} interface. SAX events
+     * invoked on this object will be translated into their corresponding XML text and written to the output stream.
+     * 
+     * @see AbstractJcrExporter#exportView(Node, OutputStream, boolean, boolean)
+     */
+    private class StreamingContentHandler extends DefaultHandler {
+
+        /** Debug setting that allows all output to be written to {@link System#out}. */
+        private static final boolean LOG_TO_CONSOLE = false;
+
+        /**
+         * Encoder to properly escape XML attribute values
+         * 
+         * @see XmlValueEncoder
+         */
+        private final TextEncoder VALUE_ENCODER = new XmlValueEncoder();
+
+        /**
+         * The list of XML namespaces that are predefined and should not be exported by the content handler.
+         */
+        private final List<String> UNEXPORTABLE_NAMESPACES = Arrays.asList(new String[] {"", "xml", "xmlns"});
+
+        /**
+         * The output stream to which the XML will be written
+         */
+        private final OutputStream os;
+
+        /**
+         * The XML namespace prefixes that are currently mapped
+         */
+        private final Map<String, String> mappedPrefixes;
+
+        public StreamingContentHandler( OutputStream os ) {
+            this.os = os;
+            mappedPrefixes = new HashMap<String, String>();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+         */
+        @Override
+        public void characters( char[] ch,
+                                int start,
+                                int length ) throws SAXException {
+            emit(VALUE_ENCODER.encode(new String(ch, start, length)));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.helpers.DefaultHandler#startDocument()
+         */
+        @Override
+        public void startDocument() throws SAXException {
+            emit("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.helpers.DefaultHandler#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 attributes ) throws SAXException {
+            emit("<");
+            emit(name);
+
+            for (Map.Entry<String, String> mapping : mappedPrefixes.entrySet()) {
+                emit(" xmlns:");
+                emit(mapping.getKey());
+                emit("=\"");
+                emit(mapping.getValue());
+                emit("\"");
+            }
+
+            mappedPrefixes.clear();
+
+            if (attributes != null) {
+                for (int i = 0; i < attributes.getLength(); i++) {
+                    emit(" ");
+                    emit(attributes.getQName(i));
+                    emit("=\"");
+                    emit(VALUE_ENCODER.encode(attributes.getValue(i)));
+                    emit("\"");
+                }
+            }
+
+            emit(">");
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+         */
+        @Override
+        public void endElement( String uri,
+                                String localName,
+                                String name ) throws SAXException {
+            emit("</");
+            emit(name);
+            emit(">");
+            System.out.println();
+
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(java.lang.String, java.lang.String)
+         */
+        @Override
+        public void startPrefixMapping( String prefix,
+                                        String uri ) {
+            if (!UNEXPORTABLE_NAMESPACES.contains(prefix)) {
+                mappedPrefixes.put(prefix, uri);
+            }
+        }
+
+        /**
+         * Writes the given text to the output stream for this {@link StreamingContentHandler}.
+         * 
+         * @param text the text to output
+         * @throws SAXException if there is an error writing to the stream
+         * @see StreamingContentHandler#os
+         */
+        private void emit( String text ) throws SAXException {
+
+            try {
+                if (LOG_TO_CONSOLE) {
+                    System.out.print(text);
+                }
+
+                os.write(text.getBytes());
+            } catch (IOException ioe) {
+                throw new SAXException(ioe);
+            }
+        }
+    }
+
+}


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

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -29,8 +29,8 @@
 import java.util.List;
 import javax.jcr.PropertyType;
 import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.JcrMixLexicon;
-import net.jcip.annotations.Immutable;
 
 /**
  * {@link JcrNodeTypeSource} that provides built-in node types provided by DNA.
@@ -136,8 +136,49 @@
 
                                            }), NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace}));
+        /* Name of node type that holds xmltext from document view import (see JCR 1.0 spec section 7.3.2) */
+        JcrNodeType xmlText = new JcrNodeType(
+                                              session,
+                                              DnaLexicon.XML_TEXT_TYPE,
+                                              Arrays.asList(new NodeType[] {base}),
+                                              NO_PRIMARY_ITEM_NAME,
+                                              NO_CHILD_NODES,
+                                              Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                   session,
+                                                                                                                   null,
+                                                                                                                   JcrLexicon.XMLCHARACTERS,
+                                                                                                                   OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                                   true,
+                                                                                                                   true,
+                                                                                                                   true,
+                                                                                                                   NO_DEFAULT_VALUES,
+                                                                                                                   PropertyType.STRING,
+                                                                                                                   NO_CONSTRAINTS,
+                                                                                                                   false)}),
+                                              NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
+        /* Mixin type that indicates the node contains an xmltext node which holds xmltext from document view import (see JCR 1.0 spec section 7.3.2) */
+        JcrNodeType xmlContent = new JcrNodeType(
+                                                 session,
+                                                 DnaLexicon.XML_CONTENT,
+                                                 Arrays.asList(new NodeType[] {base}),
+                                                 NO_PRIMARY_ITEM_NAME,
+                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
+                                                                                                              session,
+                                                                                                              null,
+                                                                                                              DnaLexicon.XML_TEXT,
+                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
+                                                                                                              false,
+                                                                                                              true,
+                                                                                                              false,
+                                                                                                              false,
+                                                                                                              DnaLexicon.XML_TEXT_TYPE,
+                                                                                                              new NodeType[] {xmlText})}),
+                                                 NO_PROPERTIES, IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace, xmlText,}));
+        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {xmlContent}));
+
     }
 
     /**

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-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -33,9 +33,28 @@
 
     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");
-    public static final Name NODE_DEFINITON = new BasicName(Namespace.URI, "nodeDefinition");
 
+    /**
+     * Mixin type that indicates the node contains an xmltext node which holds xmltext from document view import (see JCR 1.0
+     * specification section 7.3.2). This node has a child node named {@link DnaLexicon#XML_TEXT} of type
+     * {@link DnaLexicon#XML_TEXT_TYPE}.
+     */
+    public static final Name XML_CONTENT = new BasicName(Namespace.URI, "xmlContent");
+
+    /**
+     * Name of node type that holds xmltext from document view import (see JCR 1.0 specification section 7.3.2). It is defined in
+     * the node type named {@link DnaLexicon#XML_CONTENT}.
+     */
+    public static final Name XML_TEXT_TYPE = new BasicName(Namespace.URI, "xmlText");
+
+    /**
+     * Name of the child node that holds xmltext from document view import (see JCR 1.0 specification section 7.3.2). This is the
+     * name of the child node of the {@link DnaLexicon#XML_CONTENT} mixin type. By definition, this node has a required primary
+     * type of {@link DnaLexicon#XML_TEXT_TYPE}.
+     */
+    public static final Name XML_TEXT = new BasicName(Namespace.URI, "xmltext");
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -0,0 +1,216 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.io.OutputStream;
+import java.util.Collections;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Implementation of {@link AbstractJcrExporter} that implements the document view mapping described in section 6.4.2 of the JCR
+ * 1.0 specification.
+ * 
+ * @see JcrSession#exportDocumentView(String, ContentHandler, boolean, boolean)
+ * @see JcrSession#exportDocumentView(String, OutputStream, boolean, boolean)
+ */
+ at NotThreadSafe
+class JcrDocumentViewExporter extends AbstractJcrExporter {
+
+    JcrDocumentViewExporter( JcrSession session ) {
+        super(session, Collections.<String>emptyList());
+    }
+
+    /**
+     * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by invoking SAX events on
+     * <code>contentHandler</code>.
+     * 
+     * @param node the node which should be exported. If <code>noRecursion</code> was set to <code>false</code> in the
+     *        constructor, the entire subtree rooted at <code>node</code> will 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
+     * @param noRecurse if<code>true</code>, indicates that only the given node should be exported, otherwise a recursive export
+     *        and not any of its child nodes.
+     * @throws SAXException if an exception occurs during generation of the XML document
+     * @throws RepositoryException if an exception occurs accessing the content repository
+     */
+    @Override
+    public void exportNode( Node node,
+                            ContentHandler contentHandler,
+                            boolean skipBinary,
+                            boolean noRecurse ) throws RepositoryException, SAXException {
+        ExecutionContext executionContext = session.getExecutionContext();
+
+        // If this node is a special xmltext node, output it as raw content (see JCR 1.0 spec - section 6.4.2.3
+        if (node.getParent() != null && isXmlTextNode(node)) {
+
+            String xmlCharacters = getXmlCharacters(node);
+            contentHandler.characters(xmlCharacters.toCharArray(), 0, xmlCharacters.length());
+
+            return;
+        }
+
+        ValueFactories valueFactories = executionContext.getValueFactories();
+        AttributesImpl atts = new AttributesImpl();
+
+        // Build the attributes for this node's element
+        PropertyIterator properties = node.getProperties();
+        while (properties.hasNext()) {
+            Property prop = properties.nextProperty();
+
+            if (skipBinary && PropertyType.BINARY == prop.getType()) {
+                continue;
+            }
+
+            Name propName = ((AbstractJcrProperty)prop).getDnaProperty().getName();
+
+            String localPropName = propName.getString(executionContext.getNamespaceRegistry());
+
+            Value value;
+            if (prop instanceof JcrSingleValueProperty) {
+                value = prop.getValue();
+            } else {
+                // Only output the first value of the multi-valued property. This is acceptable as per JCR 1.0 Spec - section
+                // 6.4.2.5
+                value = prop.getValues()[0];
+            }
+            atts.addAttribute(propName.getNamespaceUri(),
+                              propName.getLocalName(),
+                              localPropName,
+                              PropertyType.nameFromValue(prop.getType()),
+                              value.getString());
+        }
+
+        Name name;
+
+        // Special case to stub in name for root node as per JCR 1.0 Spec - 6.4.2.2
+        if ("/".equals(node.getPath())) {
+            name = JcrLexicon.ROOT;
+        } else {
+            name = valueFactories.getNameFactory().create(node.getName());
+        }
+
+        startElement(contentHandler, name, atts);
+
+        if (!noRecurse) {
+            NodeIterator nodes = node.getNodes();
+            while (nodes.hasNext()) {
+                exportNode(nodes.nextNode(), contentHandler, skipBinary, noRecurse);
+            }
+        }
+
+        endElement(contentHandler, name);
+    
+    }
+
+    /**
+     * Indicates whether the current node is an XML text node as per section 6.4.2.3 of the JCR 1.0 specification.
+     * XML text nodes are nodes that have the name &quot;jcr:xmltext&quot; and only one property (besides the mandatory
+     * &quot;jcr:primaryType&quot;).  The property must have a property name of &quot;jcr:xmlcharacters&quot;, a type of <code>String</code>,
+     * and does not have multiple values.<p/>
+     * In practice, this is handled in DNA by making XML text nodes have a type of &quot;dna:xmltext&quot;, which
+     * enforces these property characteristics.
+     * 
+     * @param node the node to test
+     * @return whether this node is a special xml text node
+     * @throws RepositoryException if there is an error accessing the repository
+     */
+    private boolean isXmlTextNode( Node node ) throws RepositoryException {
+        // ./xmltext/xmlcharacters exception (see JSR-170 Spec 6.4.2.3)
+
+        ExecutionContext executionContext = session.getExecutionContext();
+        if (JcrLexicon.XMLTEXT.getString(executionContext.getNamespaceRegistry()).equals(node.getName())) {
+            if (node.getNodes().getSize() == 0) {
+
+                PropertyIterator properties = node.getProperties();
+                boolean xmlCharactersFound = false;
+
+                while (properties.hasNext()) {
+                    Property property = properties.nextProperty();
+
+                    if (JcrLexicon.PRIMARY_TYPE.getString(executionContext.getNamespaceRegistry()).equals(property.getName())) {
+                        continue;
+                    }
+
+                    if (JcrLexicon.XMLCHARACTERS.getString(executionContext.getNamespaceRegistry()).equals(property.getName())) {
+                        xmlCharactersFound = true;
+                        continue;
+                    }
+
+                    // If the xmltext node has any properties other than primaryType or xmlcharacters, return false;
+                    return false;
+                }
+
+                return xmlCharactersFound;
+            }
+        }
+
+        return false;
+
+    }
+
+    /**
+     * Returns the XML characters for the given node. 
+     * The node must be an XML text node, as defined in {@link #isXmlTextNode(Node)}.
+     * 
+     * @param node the node for which XML characters will be retrieved.
+     * @return the xml characters for this node
+     * @throws RepositoryException if there is an error accessing this node
+     */
+    private String getXmlCharacters( Node node ) throws RepositoryException {
+        // ./xmltext/xmlcharacters exception (see JSR-170 Spec 6.4.2.3)
+
+        assert isXmlTextNode(node);
+        
+        ExecutionContext executionContext = session.getExecutionContext();
+        Property xmlCharacters = node.getProperty(JcrLexicon.XMLCHARACTERS.getString(executionContext.getNamespaceRegistry()));
+
+        assert xmlCharacters != null;
+
+        if (xmlCharacters.getDefinition().isMultiple()) {
+            StringBuffer buff = new StringBuffer();
+
+            for (Value value : xmlCharacters.getValues()) {
+                buff.append(value.getString());
+            }
+
+            return buff.toString();
+        }
+
+        return xmlCharacters.getValue().getString();
+    }
+
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -63,7 +63,7 @@
     public static I18n invalidNamePattern;
     public static I18n itemNotFoundWithUuid;
     public static I18n errorWhileFindingNodeWithUuid;
-    public static I18n nodeDefinitionCouldBeDeterminedForNode;
+    public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
 
     public static I18n typeNotFound;
     public static I18n supertypeNotFound;

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNamespaceRegistry.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -67,6 +67,11 @@
     static final String XMLNS_NAMESPACE_PREFIX = XMLConstants.XMLNS_ATTRIBUTE;
     static final String XMLNS_NAMESPACE_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
 
+    static final String XML_SCHEMA_NAMESPACE_PREFIX = "xsd";
+    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;
@@ -82,6 +87,8 @@
         namespaces.put(JcrSvLexicon.Namespace.PREFIX, JcrSvLexicon.Namespace.URI);
         namespaces.put(XML_NAMESPACE_PREFIX, XML_NAMESPACE_URI);
         namespaces.put(XMLNS_NAMESPACE_PREFIX, XMLNS_NAMESPACE_URI);
+        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);
         // Set up the reverse map for the standard namespaces ...
         Map<String, String> prefixes = new HashMap<String, String>();

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -171,6 +171,7 @@
         // Check if the node can be added with the named child node definition
         if (childNode != null && primaryNodeTypeName != null) {
             NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+            if (primaryNodeType == null) return null;
             if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) return null;
         }
         return childNode;

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -73,6 +73,7 @@
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
 import com.google.common.base.ReferenceType;
 import com.google.common.collect.ReferenceMap;
 
@@ -290,53 +291,81 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#exportDocumentView(java.lang.String, org.xml.sax.ContentHandler, boolean, boolean)
      */
     public void exportDocumentView( String absPath,
                                     ContentHandler contentHandler,
                                     boolean skipBinary,
-                                    boolean noRecurse ) {
-        throw new UnsupportedOperationException();
+                                    boolean noRecurse ) throws RepositoryException, SAXException {
+        CheckArg.isNotNull(absPath, "absPath");
+        CheckArg.isNotNull(contentHandler, "contentHandler");
+
+        Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
+        Node exportRootNode = getNode(exportRootPath);
+
+        AbstractJcrExporter exporter = new JcrDocumentViewExporter(this);
+
+        exporter.exportView(exportRootNode, contentHandler, skipBinary, noRecurse);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#exportDocumentView(java.lang.String, java.io.OutputStream, boolean, boolean)
      */
     public void exportDocumentView( String absPath,
                                     OutputStream out,
                                     boolean skipBinary,
-                                    boolean noRecurse ) {
-        throw new UnsupportedOperationException();
+                                    boolean noRecurse ) throws RepositoryException {
+        CheckArg.isNotNull(absPath, "absPath");
+        CheckArg.isNotNull(out, "out");
+
+        Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
+        Node exportRootNode = getNode(exportRootPath);
+
+        AbstractJcrExporter exporter = new JcrDocumentViewExporter(this);
+
+        exporter.exportView(exportRootNode, out, skipBinary, noRecurse);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#exportSystemView(java.lang.String, org.xml.sax.ContentHandler, boolean, boolean)
      */
     public void exportSystemView( String absPath,
                                   ContentHandler contentHandler,
                                   boolean skipBinary,
-                                  boolean noRecurse ) {
-        throw new UnsupportedOperationException();
+                                  boolean noRecurse ) throws RepositoryException, SAXException {
+        CheckArg.isNotNull(absPath, "absPath");
+        CheckArg.isNotNull(contentHandler, "contentHandler");
+
+        Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
+        Node exportRootNode = getNode(exportRootPath);
+
+        AbstractJcrExporter exporter = new JcrSystemViewExporter(this);
+
+        exporter.exportView(exportRootNode, contentHandler, skipBinary, noRecurse);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#exportSystemView(java.lang.String, java.io.OutputStream, boolean, boolean)
      */
     public void exportSystemView( String absPath,
                                   OutputStream out,
                                   boolean skipBinary,
-                                  boolean noRecurse ) {
-        throw new UnsupportedOperationException();
+                                  boolean noRecurse ) throws RepositoryException {
+        CheckArg.isNotNull(absPath, "absPath");
+        CheckArg.isNotNull(out, "out");
+
+        Path exportRootPath = executionContext.getValueFactories().getPathFactory().create(absPath);
+        Node exportRootNode = getNode(exportRootPath);
+
+        AbstractJcrExporter exporter = new JcrSystemViewExporter(this);
+
+        exporter.exportView(exportRootNode, out, skipBinary, noRecurse);
     }
 
     /**
@@ -518,7 +547,7 @@
                 String childName = path.getLastSegment().getName().getString(namespaces);
                 definition = nodeType.findBestNodeDefinitionForChild(childName, primaryTypeNameString);
                 if (definition == null) {
-                    String msg = JcrI18n.nodeDefinitionCouldBeDeterminedForNode.text(path, workspace.getName());
+                    String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspace.getName());
                     throw new RepositorySourceException(msg);
                 }
             }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -0,0 +1,244 @@
+package org.jboss.dna.jcr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.util.Base64;
+import org.jboss.dna.common.xml.XmlCharacters;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Implementation of {@link AbstractJcrExporter} that implements the system view mapping described in section 6.4.1 of the JCR 1.0
+ * specification.
+ * 
+ * @see JcrSession#exportSystemView(String, ContentHandler, boolean, boolean)
+ * @see JcrSession#exportSystemView(String, OutputStream, boolean, boolean)
+ */
+ at NotThreadSafe
+class JcrSystemViewExporter extends AbstractJcrExporter {
+
+    /**
+     * Buffer size for reading Base64-encoded binary streams for export.
+     */
+    private static final int BASE_64_BUFFER_SIZE = 1024;
+
+    /**
+     * The list of the special JCR properties that must be exported first for each node. These properties must be exported in list
+     * order if they are present on the node as per section 6.4.1 rule 11.
+     */
+    private static final List<Name> SPECIAL_PROPERTY_NAMES = Arrays.asList(new Name[] {JcrLexicon.PRIMARY_TYPE,
+        JcrLexicon.MIXIN_TYPES, JcrLexicon.UUID});
+
+    JcrSystemViewExporter( JcrSession session ) {
+        super(session, Arrays.asList(new String[] {"xml"}));
+    }
+
+    /**
+     * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by invoking SAX events on
+     * <code>contentHandler</code>.
+     * 
+     * @param node the node which should be exported. If <code>noRecursion</code> was set to <code>false</code> in the
+     *        constructor, the entire subtree rooted at <code>node</code> will 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
+     * @param noRecurse if<code>true</code>, indicates that only the given node should be exported, otherwise a recursive export
+     *        and not any of its child nodes.
+     * @throws SAXException if an exception occurs during generation of the XML document
+     * @throws RepositoryException if an exception occurs accessing the content repository
+     */
+    @Override
+    public void exportNode( Node node,
+                            ContentHandler contentHandler,
+                            boolean skipBinary,
+                            boolean noRecurse ) throws RepositoryException, SAXException {
+        ExecutionContext executionContext = session.getExecutionContext();
+
+        // start the sv:node element for this JCR node
+        AttributesImpl atts = new AttributesImpl();
+        atts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
+                          JcrSvLexicon.NAME.getLocalName(),
+                          JcrSvLexicon.NAME.getString(executionContext.getNamespaceRegistry()),
+                          PropertyType.nameFromValue(PropertyType.STRING),
+                          node.getName());
+
+        startElement(contentHandler, JcrSvLexicon.NODE, atts);
+
+        // Output any special properties first (see Javadoc for SPECIAL_PROPERTY_NAMES for more context)
+        for (Name specialPropertyName : SPECIAL_PROPERTY_NAMES) {
+            Property specialProperty = ((AbstractJcrNode)node).getProperty(specialPropertyName);
+
+            if (specialProperty != null) {
+                emitProperty(specialProperty, contentHandler);
+            }
+        }
+
+        PropertyIterator properties = node.getProperties();
+        while (properties.hasNext()) {
+            exportProperty(properties.nextProperty(), contentHandler, skipBinary);
+        }
+
+        if (!noRecurse) {
+            NodeIterator nodes = node.getNodes();
+            while (nodes.hasNext()) {
+                exportNode(nodes.nextNode(), contentHandler, skipBinary, noRecurse);
+            }
+        }
+
+        endElement(contentHandler, JcrSvLexicon.NODE);
+    }
+
+    /**
+     * @param property
+     * @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 exportProperty( Property property,
+                                 ContentHandler contentHandler,
+                                 boolean skipBinary ) throws RepositoryException, SAXException {
+        assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
+                                                         + " on non-DNA property";
+
+        AbstractJcrProperty prop = (AbstractJcrProperty)property;
+
+        Name propertyName = prop.getDnaProperty().getName();
+        if (SPECIAL_PROPERTY_NAMES.contains(propertyName)) {
+            return;
+        }
+
+        if (skipBinary && PropertyType.BINARY == prop.getType()) {
+            return;
+        }
+
+        emitProperty(property, contentHandler);
+    }
+
+    /**
+     * Fires the appropriate SAX events on the content handler to build the XML elements for the property.
+     * 
+     * @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.
+     * @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 {
+        assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
+                                                         + " on non-DNA property";
+
+        ExecutionContext executionContext = session.getExecutionContext();
+        AbstractJcrProperty prop = (AbstractJcrProperty)property;
+
+        // first set the property sv:name attribute
+        AttributesImpl propAtts = new AttributesImpl();
+        propAtts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
+                              JcrSvLexicon.NAME.getLocalName(),
+                              JcrSvLexicon.NAME.getString(executionContext.getNamespaceRegistry()),
+                              PropertyType.nameFromValue(PropertyType.STRING),
+                              prop.getName());
+
+        // and it's sv:type attribute
+        propAtts.addAttribute(JcrSvLexicon.TYPE.getNamespaceUri(),
+                              JcrSvLexicon.TYPE.getLocalName(),
+                              JcrSvLexicon.TYPE.getString(executionContext.getNamespaceRegistry()),
+                              PropertyType.nameFromValue(PropertyType.STRING),
+                              PropertyType.nameFromValue(prop.getType()));
+
+        // output the sv:property element
+        startElement(contentHandler, JcrSvLexicon.PROPERTY, propAtts);
+
+        // then output a sv:value element for each of its values
+        if (prop instanceof JcrMultiValueProperty) {
+            Value[] values = prop.getValues();
+            for (int i = 0; i < values.length; i++) {
+
+                emitValue(values[i], contentHandler, property.getType());
+            }
+        } else {
+            emitValue(property.getValue(), contentHandler, property.getType());
+        }
+
+        // end the sv:property element
+        endElement(contentHandler, JcrSvLexicon.PROPERTY);
+    }
+
+    /**
+     * Fires the appropriate SAX events on the content handler to build the XML elements for the value.
+     * 
+     * @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
+     * @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 {
+
+        if (PropertyType.BINARY == propertyType) {
+            startElement(contentHandler, JcrSvLexicon.VALUE, null);
+
+            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);
+
+                while (-1 != (len = stream.read(bytes))) {
+                    contentHandler.characters(new String(bytes, 0, len).toCharArray(), 0, len);
+                }
+            } catch (IOException ioe) {
+                throw new RepositoryException(ioe);
+            }
+
+            endElement(contentHandler, JcrSvLexicon.VALUE);
+        } else {
+            String s = value.getString();
+
+            // Per 6.4.1.2 Rule #7 of the JCR 1.0 spec, need to check invalid XML characters
+
+            char[] chars = s.toCharArray();
+
+            boolean allCharsAreValidXml = true;
+            for (int i = 0; i < chars.length; i++) {
+                if (!XmlCharacters.isValid(chars[i])) {
+                    allCharsAreValidXml = false;
+                    break;
+                }
+            }
+
+            if (allCharsAreValidXml) {
+                
+                startElement(contentHandler, JcrSvLexicon.VALUE, null);
+                contentHandler.characters(chars, 0, chars.length);
+                endElement(contentHandler, JcrSvLexicon.VALUE);
+            } else {
+                AttributesImpl valueAtts = new AttributesImpl();
+                valueAtts.addAttribute("xsi", "type", "xsi:type", "STRING", "xsd:base64Binary");
+
+                startElement(contentHandler, JcrSvLexicon.VALUE, valueAtts);
+                chars = Base64.encodeBytes(s.getBytes(), Base64.URL_SAFE).toCharArray();
+                contentHandler.characters(chars, 0, chars.length);
+                endElement(contentHandler, JcrSvLexicon.VALUE);
+            }
+        }
+
+    }
+
+}


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

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-10 15:17:23 UTC (rev 764)
@@ -53,7 +53,7 @@
 invalidNamePattern = The "{1}" name pattern contained the '{0}' character, which is not allowed in a name pattern
 itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}"
 errorWhileFindingNodeWithUuid = Error while finding the item with UUID "{0}" in workspace "{1}"
-nodeDefinitionCouldBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
+nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
 
 REP_NAME_DESC = DNA Repository
 REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC

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-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-10 15:17:23 UTC (rev 764)
@@ -108,8 +108,8 @@
             addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
 
             // These might not all be level one tests

Modified: trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	2009-03-09 17:57:18 UTC (rev 763)
+++ trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	2009-03-10 15:17:23 UTC (rev 764)
@@ -26,9 +26,13 @@
   -->
 <testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
             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" jcr:primaryType="nt:unstructured" prop1="foo" />
-	<nt:unstructured jcr:name="node2" jcr:primaryType="nt:unstructured" prop2="bar" />
-	<nt:unstructured jcr:name="node3" jcr:primaryType="nt:unstructured" />
-	<nt:unstructured jcr:name="node4" jcr:primaryType="nt:unstructured" />
+	<nt:unstructured jcr:name="node1" prop1="&lt;foo&amp;foo&gt;" >
+	<!--  This stanza checks for the jcr:xmltext special case for document export.  DO NOT change this element. -->
+	   <dna:xmlText jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+	</nt:unstructured>
+	<nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
+	<nt:unstructured jcr:name="node3" />
+	<nt:unstructured jcr:name="node4" />
 </testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Tue Mar 10 12:18:31 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 12:18:31 -0400
Subject: [dna-commits] DNA SVN: r765 -
	trunk/dna-common/src/main/java/org/jboss/dna/common/util.
Message-ID: <E1Lh4f1-0004bV-Et@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 12:18:31 -0400 (Tue, 10 Mar 2009)
New Revision: 765

Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java
Log:
Cleaned up the Base64 code to eliminate many warnings, fix some coding errors, and improve error handling.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java	2009-03-10 15:17:23 UTC (rev 764)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java	2009-03-10 16:18:31 UTC (rev 765)
@@ -23,7 +23,17 @@
  */
 package org.jboss.dna.common.util;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.zip.GZIPInputStream;
+import org.jboss.dna.common.SystemFailureException;
 
 /**
  * <p>
@@ -318,7 +328,7 @@
         else if ((options & ORDERED) == ORDERED) return _ORDERED_ALPHABET;
         else return _STANDARD_ALPHABET;
 
-    } // end getAlphabet
+    }
 
     /**
      * Returns one of the _SOMETHING_DECODABET byte arrays depending on the options specified. It's possible, though silly, to
@@ -328,12 +338,12 @@
      * @param options The options to use in this operation
      * @return the appropriate decodabets
      */
-    private final static byte[] getDecodabet( int options ) {
+    protected final static byte[] getDecodabet( int options ) {
         if ((options & URL_SAFE) == URL_SAFE) return _URL_SAFE_DECODABET;
         else if ((options & ORDERED) == ORDERED) return _ORDERED_DECODABET;
         else return _STANDARD_DECODABET;
 
-    } // end getAlphabet
+    }
 
     /** Defeats instantiation. */
     private Base64() {
@@ -351,15 +361,14 @@
      * @param numSigBytes the number of significant bytes in your array
      * @param options The options to use in this operation
      * @return four byte array in Base64 notation.
-     * @since 1.5.1
      */
-    private static byte[] encode3to4( byte[] b4,
-                                      byte[] threeBytes,
-                                      int numSigBytes,
-                                      int options ) {
+    protected static byte[] encode3to4( byte[] b4,
+                                        byte[] threeBytes,
+                                        int numSigBytes,
+                                        int options ) {
         encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
         return b4;
-    } // end encode3to4
+    }
 
     /**
      * <p>
@@ -380,14 +389,13 @@
      * @param destOffset the index where output will be put
      * @param options The options to use in this operation
      * @return the <var>destination</var> array
-     * @since 1.3
      */
-    private static byte[] encode3to4( byte[] source,
-                                      int srcOffset,
-                                      int numSigBytes,
-                                      byte[] destination,
-                                      int destOffset,
-                                      int options ) {
+    protected static byte[] encode3to4( byte[] source,
+                                        int srcOffset,
+                                        int numSigBytes,
+                                        byte[] destination,
+                                        int destOffset,
+                                        int options ) {
         byte[] ALPHABET = getAlphabet(options);
 
         // 1 2 3
@@ -429,8 +437,8 @@
 
             default:
                 return destination;
-        } // end switch
-    } // end encode3to4
+        }
+    }
 
     /**
      * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be serialized
@@ -438,11 +446,11 @@
      * 
      * @param serializableObject The object to encode
      * @return The Base64-encoded object
-     * @since 1.4
+     * @throws IOException if there is an IOException while serializing
      */
-    public static String encodeObject( java.io.Serializable serializableObject ) {
+    public static String encodeObject( java.io.Serializable serializableObject ) throws IOException {
         return encodeObject(serializableObject, NO_OPTIONS);
-    } // end encodeObject
+    }
 
     /**
      * Serializes an object and returns the Base64-encoded version of that serialized object. If the object cannot be serialized
@@ -465,77 +473,60 @@
      * @return The Base64-encoded object
      * @see Base64#GZIP
      * @see Base64#DONT_BREAK_LINES
-     * @since 2.0
+     * @throws IOException if there is an IOException while serializing
      */
     public static String encodeObject( java.io.Serializable serializableObject,
-                                       int options ) {
-        // Streams
-        java.io.ByteArrayOutputStream baos = null;
-        java.io.OutputStream b64os = null;
+                                       int options ) throws IOException {
+        // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+        java.io.ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        java.io.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
         java.io.ObjectOutputStream oos = null;
         java.util.zip.GZIPOutputStream gzos = null;
 
         // Isolate options
         int gzip = (options & GZIP);
-        int dontBreakLines = (options & DONT_BREAK_LINES);
-
+        boolean error = false;
         try {
-            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
-            baos = new java.io.ByteArrayOutputStream();
-            b64os = new Base64.OutputStream(baos, ENCODE | options);
-
             // GZip?
             if (gzip == GZIP) {
                 gzos = new java.util.zip.GZIPOutputStream(b64os);
                 oos = new java.io.ObjectOutputStream(gzos);
-            } // end if: gzip
-            else oos = new java.io.ObjectOutputStream(b64os);
+            } else {
+                oos = new java.io.ObjectOutputStream(b64os);
+            }
 
             oos.writeObject(serializableObject);
-        } // end try
-        catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        } // end catch
-        finally {
-            try {
-                oos.close();
-            } catch (Exception e) {
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } finally {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException e) {
+                    if (!error) throw e;
+                }
             }
-            try {
-                gzos.close();
-            } catch (Exception e) {
-            }
-            try {
-                b64os.close();
-            } catch (Exception e) {
-            }
-            try {
-                baos.close();
-            } catch (Exception e) {
-            }
-        } // end finally
+        }
 
         // Return value according to relevant encoding.
         try {
             return new String(baos.toByteArray(), PREFERRED_ENCODING);
-        } // end try
-        catch (java.io.UnsupportedEncodingException uue) {
+        } catch (java.io.UnsupportedEncodingException uue) {
             return new String(baos.toByteArray());
-        } // end catch
+        }
 
-    } // end encode
+    }
 
     /**
      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
      * 
      * @param source The data to convert
      * @return the encoded bytes
-     * @since 1.4
      */
     public static String encodeBytes( byte[] source ) {
         return encodeBytes(source, 0, source.length, NO_OPTIONS);
-    } // end encodeBytes
+    }
 
     /**
      * Encodes a byte array into Base64 notation.
@@ -557,12 +548,11 @@
      * @return the encoded bytes
      * @see Base64#GZIP
      * @see Base64#DONT_BREAK_LINES
-     * @since 2.0
      */
     public static String encodeBytes( byte[] source,
                                       int options ) {
         return encodeBytes(source, 0, source.length, options);
-    } // end encodeBytes
+    }
 
     /**
      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
@@ -571,13 +561,12 @@
      * @param off Offset in array where conversion should begin
      * @param len Length of data
      * @return the encoded bytes
-     * @since 1.4
      */
     public static String encodeBytes( byte[] source,
                                       int off,
                                       int len ) {
         return encodeBytes(source, off, len, NO_OPTIONS);
-    } // end encodeBytes
+    }
 
     /**
      * Encodes a byte array into Base64 notation.
@@ -601,7 +590,6 @@
      * @return the encoded bytes
      * @see Base64#GZIP
      * @see Base64#DONT_BREAK_LINES
-     * @since 2.0
      */
     public static String encodeBytes( byte[] source,
                                       int off,
@@ -613,88 +601,76 @@
 
         // Compress?
         if (gzip == GZIP) {
-            java.io.ByteArrayOutputStream baos = null;
+            // GZip -> Base64 -> ByteArray
+            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
+            Base64.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
             java.util.zip.GZIPOutputStream gzos = null;
-            Base64.OutputStream b64os = null;
 
+            boolean error = false;
             try {
-                // GZip -> Base64 -> ByteArray
-                baos = new java.io.ByteArrayOutputStream();
-                b64os = new Base64.OutputStream(baos, ENCODE | options);
                 gzos = new java.util.zip.GZIPOutputStream(b64os);
 
                 gzos.write(source, off, len);
                 gzos.close();
-            } // end try
-            catch (IOException e) {
-                e.printStackTrace();
-                return null;
-            } // end catch
-            finally {
-                try {
-                    gzos.close();
-                } catch (Exception e) {
+            } catch (IOException e) {
+                error = true;
+                throw new SystemFailureException(e); // error using reading from byte array!
+            } finally {
+                if (gzos != null) {
+                    try {
+                        gzos.close();
+                    } catch (IOException e) {
+                        if (!error) new SystemFailureException(e); // error closing streams over byte array!
+                    }
                 }
-                try {
-                    b64os.close();
-                } catch (Exception e) {
-                }
-                try {
-                    baos.close();
-                } catch (Exception e) {
-                }
-            } // end finally
+            }
 
             // Return value according to relevant encoding.
             try {
                 return new String(baos.toByteArray(), PREFERRED_ENCODING);
-            } // end try
-            catch (java.io.UnsupportedEncodingException uue) {
+            } catch (java.io.UnsupportedEncodingException uue) {
                 return new String(baos.toByteArray());
-            } // end catch
-        } // end if: compress
+            }
+        }
 
-        // Else, don't compress. Better not to use streams at all then.
-        else {
-            // Convert option to boolean in way that code likes it.
-            boolean breakLines = dontBreakLines == 0;
+        // Otherwise, don't compress. Better not to use streams at all then.
+        // Convert option to boolean in way that code likes it.
+        boolean breakLines = dontBreakLines == 0;
 
-            int len43 = len * 4 / 3;
-            byte[] outBuff = new byte[(len43) // Main 4:3
-                                      + ((len % 3) > 0 ? 4 : 0) // Account for padding
-                                      + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
-            int d = 0;
-            int e = 0;
-            int len2 = len - 2;
-            int lineLength = 0;
-            for (; d < len2; d += 3, e += 4) {
-                encode3to4(source, d + off, 3, outBuff, e, options);
+        int len43 = len * 4 / 3;
+        byte[] outBuff = new byte[(len43) // Main 4:3
+                                  + ((len % 3) > 0 ? 4 : 0) // Account for padding
+                                  + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
+        int d = 0;
+        int e = 0;
+        int len2 = len - 2;
+        int lineLength = 0;
+        for (; d < len2; d += 3, e += 4) {
+            encode3to4(source, d + off, 3, outBuff, e, options);
 
-                lineLength += 4;
-                if (breakLines && lineLength == MAX_LINE_LENGTH) {
-                    outBuff[e + 4] = NEW_LINE;
-                    e++;
-                    lineLength = 0;
-                } // end if: end of line
-            } // en dfor: each piece of array
+            lineLength += 4;
+            if (breakLines && lineLength == MAX_LINE_LENGTH) {
+                outBuff[e + 4] = NEW_LINE;
+                e++;
+                lineLength = 0;
+            }
+        }
 
-            if (d < len) {
-                encode3to4(source, d + off, len - d, outBuff, e, options);
-                e += 4;
-            } // end if: some padding needed
+        if (d < len) {
+            // Padding is needed
+            encode3to4(source, d + off, len - d, outBuff, e, options);
+            e += 4;
+        }
 
-            // Return value according to relevant encoding.
-            try {
-                return new String(outBuff, 0, e, PREFERRED_ENCODING);
-            } // end try
-            catch (java.io.UnsupportedEncodingException uue) {
-                return new String(outBuff, 0, e);
-            } // end catch
+        // Return value according to relevant encoding.
+        try {
+            return new String(outBuff, 0, e, PREFERRED_ENCODING);
+        } catch (java.io.UnsupportedEncodingException uue) {
+            return new String(outBuff, 0, e);
+        }
 
-        } // end else: don't compress
+    }
 
-    } // end encodeBytes
-
     /* ********  D E C O D I N G   M E T H O D S  ******** */
 
     /**
@@ -713,13 +689,12 @@
      * @param destOffset the index where output will be put
      * @param options alphabet type is pulled from this (standard, url-safe, ordered)
      * @return the number of decoded bytes converted
-     * @since 1.3
      */
-    private static int decode4to3( byte[] source,
-                                   int srcOffset,
-                                   byte[] destination,
-                                   int destOffset,
-                                   int options ) {
+    protected static int decode4to3( byte[] source,
+                                     int srcOffset,
+                                     byte[] destination,
+                                     int destOffset,
+                                     int options ) {
         byte[] DECODABET = getDecodabet(options);
 
         // Example: Dk==
@@ -764,14 +739,15 @@
 
                 return 3;
             } catch (Exception e) {
-                System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
-                System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
-                System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
-                System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
-                return -1;
-            } // end catch
+                StringBuilder sb = new StringBuilder();
+                sb.append("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]) + "\n");
+                sb.append("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]) + "\n");
+                sb.append("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]) + "\n");
+                sb.append("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]) + "\n");
+                throw new SystemFailureException(sb.toString(), e);
+            }
         }
-    } // end decodeToBytes
+    }
 
     /**
      * Very low-level access to decoding ASCII characters in the form of a byte array. Does not support automatically gunzipping
@@ -782,7 +758,6 @@
      * @param len The length of characters to decode
      * @param options The options to use in this operation
      * @return decoded data
-     * @since 1.3
      */
     public static byte[] decode( byte[] source,
                                  int off,
@@ -803,38 +778,37 @@
             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
             sbiDecode = DECODABET[sbiCrop];
 
-            if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
-            {
+            if (sbiDecode >= WHITE_SPACE_ENC) {
+                // White space, Equals sign or better
                 if (sbiDecode >= EQUALS_SIGN_ENC) {
+                    // Equal sign or better
                     b4[b4Posn++] = sbiCrop;
                     if (b4Posn > 3) {
+                        // build a quartet
                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
                         b4Posn = 0;
 
                         // If that was the equals sign, break out of 'for' loop
                         if (sbiCrop == EQUALS_SIGN) break;
-                    } // end if: quartet built
+                    }
 
-                } // end if: equals sign or better
+                }
 
-            } // end if: white space, equals sign or better
-            else {
-                System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
-                return null;
-            } // end else:
-        } // each input character
+            } else {
+                throw new SystemFailureException("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
+            }
+        }
 
         byte[] out = new byte[outBuffPosn];
         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
         return out;
-    } // end decode
+    }
 
     /**
      * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
      * 
      * @param s the string to decode
      * @return the decoded data
-     * @since 1.4
      */
     public static byte[] decode( String s ) {
         return decode(s, NO_OPTIONS);
@@ -846,18 +820,15 @@
      * @param s the string to decode
      * @param options encode options such as URL_SAFE
      * @return the decoded data
-     * @since 1.4
      */
     public static byte[] decode( String s,
                                  int options ) {
         byte[] bytes;
         try {
             bytes = s.getBytes(PREFERRED_ENCODING);
-        } // end try
-        catch (java.io.UnsupportedEncodingException uee) {
+        } catch (java.io.UnsupportedEncodingException uee) {
             bytes = s.getBytes();
-        } // end catch
-        // </change>
+        }
 
         // Decode
         bytes = decode(bytes, 0, bytes.length, options);
@@ -866,93 +837,86 @@
         // GZIP Magic Two-Byte Number: 0x8b1f (35615)
         if (bytes != null && bytes.length >= 4) {
 
-            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
-            if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
-                java.io.ByteArrayInputStream bais = null;
-                java.util.zip.GZIPInputStream gzis = null;
-                java.io.ByteArrayOutputStream baos = null;
+            int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+            if (GZIPInputStream.GZIP_MAGIC == head) {
+                ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+                GZIPInputStream gzis = null;
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                 byte[] buffer = new byte[2048];
                 int length = 0;
 
                 try {
-                    baos = new java.io.ByteArrayOutputStream();
-                    bais = new java.io.ByteArrayInputStream(bytes);
                     gzis = new java.util.zip.GZIPInputStream(bais);
-
                     while ((length = gzis.read(buffer)) >= 0) {
                         baos.write(buffer, 0, length);
-                    } // end while: reading input
+                    }
 
                     // No error? Get new bytes.
                     bytes = baos.toByteArray();
 
-                } // end try
-                catch (IOException e) {
+                } catch (IOException e) {
                     // Just return originally-decoded bytes
                 } // end catch
                 finally {
+                    boolean error = false;
                     try {
-                        baos.close();
-                    } catch (Exception e) {
+                        if (gzis != null) {
+                            try {
+                                gzis.close();
+                            } catch (IOException e) {
+                                throw new SystemFailureException(e); // bad problems with JRE if this doesn't work
+                            }
+                        }
+                    } finally {
+                        try {
+                            bais.close();
+                        } catch (Exception e) {
+                            if (!error) throw new SystemFailureException(e); // bad problems with JRE if this doesn't work
+                        }
                     }
-                    try {
-                        gzis.close();
-                    } catch (Exception e) {
-                    }
-                    try {
-                        bais.close();
-                    } catch (Exception e) {
-                    }
-                } // end finally
+                }
 
-            } // end if: gzipped
-        } // end if: bytes.length >= 2
+            }
+        }
 
         return bytes;
-    } // end decode
+    }
 
     /**
      * Attempts to decode Base64 data and deserialize a Java Object within. Returns <tt>null</tt> if there was an error.
      * 
      * @param encodedObject The Base64 data to decode
      * @return The decoded and deserialized object
-     * @since 1.5
+     * @throws IOException if there is an error deserializing the encoded object
+     * @throws ClassNotFoundException if the class for the deserialized object could not be found
      */
-    public static Object decodeToObject( String encodedObject ) {
+    public static Object decodeToObject( String encodedObject ) throws IOException, ClassNotFoundException {
         // Decode and gunzip if necessary
         byte[] objBytes = decode(encodedObject);
 
-        java.io.ByteArrayInputStream bais = null;
-        java.io.ObjectInputStream ois = null;
+        ByteArrayInputStream bais = new ByteArrayInputStream(objBytes);
+        ObjectInputStream ois = new ObjectInputStream(bais);
         Object obj = null;
 
+        boolean error = false;
         try {
-            bais = new java.io.ByteArrayInputStream(objBytes);
-            ois = new java.io.ObjectInputStream(bais);
-
             obj = ois.readObject();
-        } // end try
-        catch (IOException e) {
-            e.printStackTrace();
-            obj = null;
-        } // end catch
-        catch (java.lang.ClassNotFoundException e) {
-            e.printStackTrace();
-            obj = null;
-        } // end catch
-        finally {
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } catch (java.lang.ClassNotFoundException e) {
+            error = true;
+            throw e;
+        } finally {
             try {
-                bais.close();
-            } catch (Exception e) {
-            }
-            try {
                 ois.close();
-            } catch (Exception e) {
+            } catch (IOException e) {
+                if (!error) throw e;
             }
-        } // end finally
+        }
 
         return obj;
-    } // end decodeObject
+    }
 
     /**
      * Convenience method for encoding data to a file.
@@ -960,227 +924,212 @@
      * @param dataToEncode byte array of data to encode in base64 form
      * @param filename Filename for saving encoded data
      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
-     * @since 2.1
+     * @throws IOException if there is a problem writing to the file
      */
     public static boolean encodeToFile( byte[] dataToEncode,
-                                        String filename ) {
-        boolean success = false;
-        Base64.OutputStream bos = null;
+                                        String filename ) throws IOException {
+        boolean error = false;
+        Base64.OutputStream bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
+
         try {
-            bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
             bos.write(dataToEncode);
-            success = true;
-        } // end try
-        catch (IOException e) {
-
-            success = false;
-        } // end catch: IOException
-        finally {
+            return true;
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } finally {
             try {
                 bos.close();
-            } catch (Exception e) {
+            } catch (IOException e) {
+                if (!error) throw e;
             }
-        } // end finally
+        }
+    }
 
-        return success;
-    } // end encodeToFile
-
     /**
      * Convenience method for decoding data to a file.
      * 
      * @param dataToDecode Base64-encoded data as a string
      * @param filename Filename for saving decoded data
      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
-     * @since 2.1
+     * @throws IOException if there is a problem writing to the file
      */
     public static boolean decodeToFile( String dataToDecode,
-                                        String filename ) {
-        boolean success = false;
-        Base64.OutputStream bos = null;
+                                        String filename ) throws IOException {
+        boolean error = false;
+        Base64.OutputStream bos = new Base64.OutputStream(new FileOutputStream(filename), Base64.DECODE);
         try {
-            bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
             bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
-            success = true;
-        } // end try
-        catch (IOException e) {
-            success = false;
-        } // end catch: IOException
-        finally {
+            return true;
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } finally {
             try {
                 bos.close();
-            } catch (Exception e) {
+            } catch (IOException e) {
+                if (!error) throw e;
             }
-        } // end finally
+        }
+    }
 
-        return success;
-    } // end decodeToFile
-
     /**
      * Convenience method for reading a base64-encoded file and decoding it.
      * 
      * @param filename Filename for reading encoded data
      * @return decoded byte array or null if unsuccessful
-     * @since 2.1
+     * @throws IOException if there is a problem reading from the file
      */
-    public static byte[] decodeFromFile( String filename ) {
-        byte[] decodedData = null;
-        Base64.InputStream bis = null;
-        try {
-            // Set up some useful variables
-            java.io.File file = new java.io.File(filename);
-            byte[] buffer = null;
-            int length = 0;
-            int numBytes = 0;
+    public static byte[] decodeFromFile( String filename ) throws IOException {
+        // Set up some useful variables
+        byte[] buffer = null;
+        int length = 0;
+        int numBytes = 0;
 
-            // Check for size of file
-            if (file.length() > Integer.MAX_VALUE) {
-                System.err.println("File is too big for this convenience method (" + file.length() + " bytes).");
-                return null;
-            } // end if: file too big for int index
+        File file = new File(filename);
+        Base64.InputStream bis = new Base64.InputStream(new BufferedInputStream(new FileInputStream(file)), Base64.DECODE);
+
+        boolean error = false;
+        try {
             buffer = new byte[(int)file.length()];
 
-            // Open a stream
-            bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.DECODE);
-
             // Read until done
-            while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
+            while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
                 length += numBytes;
+            }
 
             // Save in a variable to return
-            decodedData = new byte[length];
+            byte[] decodedData = new byte[length];
             System.arraycopy(buffer, 0, decodedData, 0, length);
-
-        } // end try
-        catch (IOException e) {
-            System.err.println("Error decoding from file " + filename);
-        } // end catch: IOException
-        finally {
+            return decodedData;
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } finally {
             try {
                 bis.close();
-            } catch (Exception e) {
+            } catch (IOException e) {
+                if (!error) throw e;
             }
-        } // end finally
+        }
+    }
 
-        return decodedData;
-    } // end decodeFromFile
-
     /**
      * Convenience method for reading a binary file and base64-encoding it.
      * 
      * @param filename Filename for reading binary data
      * @return base64-encoded string or null if unsuccessful
-     * @since 2.1
+     * @throws IOException if there is a problem reading from the file
      */
-    public static String encodeFromFile( String filename ) {
-        String encodedData = null;
-        Base64.InputStream bis = null;
-        try {
-            // Set up some useful variables
-            java.io.File file = new java.io.File(filename);
-            byte[] buffer = new byte[Math.max((int)(file.length() * 1.4), 40)]; // Need max() for math on small files (v2.2.1)
-            int length = 0;
-            int numBytes = 0;
+    public static String encodeFromFile( String filename ) throws IOException {
+        File file = new File(filename);
+        Base64.InputStream bis = new Base64.InputStream(new BufferedInputStream(new FileInputStream(file)), Base64.ENCODE);
 
-            // Open a stream
-            bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.ENCODE);
+        // Set up some useful variables
+        byte[] buffer = new byte[Math.max((int)(file.length() * 1.4), 40)]; // Need max() for math on small files (v2.2.1)
+        int length = 0;
+        int numBytes = 0;
 
+        boolean error = false;
+        try {
             // Read until done
-            while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
+            while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
                 length += numBytes;
-
-            // Save in a variable to return
-            encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
-
-        } // end try
-        catch (IOException e) {
-            System.err.println("Error encoding from file " + filename);
-        } // end catch: IOException
-        finally {
+            }
+            return new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
+        } catch (IOException e) {
+            error = true;
+            throw e;
+        } finally {
             try {
                 bis.close();
-            } catch (Exception e) {
+            } catch (IOException e) {
+                if (!error) throw e;
             }
-        } // end finally
+        }
+    }
 
-        return encodedData;
-    } // end encodeFromFile
-
     /**
      * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
      * 
      * @param infile Input file
      * @param outfile Output file
      * @return true if the operation is successful
-     * @since 2.2
+     * @throws IOException if there is a problem reading or writing either file
      */
     public static boolean encodeFileToFile( String infile,
-                                            String outfile ) {
-        boolean success = false;
-        java.io.InputStream in = null;
-        java.io.OutputStream out = null;
+                                            String outfile ) throws IOException {
+        InputStream in = new Base64.InputStream(new BufferedInputStream(new FileInputStream(infile)), Base64.ENCODE);
+        java.io.OutputStream out = new BufferedOutputStream(new FileOutputStream(outfile));
+
+        boolean error = false;
         try {
-            in = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(infile)), Base64.ENCODE);
-            out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
             byte[] buffer = new byte[65536]; // 64K
             int read = -1;
             while ((read = in.read(buffer)) >= 0) {
                 out.write(buffer, 0, read);
-            } // end while: through file
-            success = true;
-        } catch (IOException exc) {
-            exc.printStackTrace();
+            }
+            return true;
+        } catch (IOException e) {
+            error = true;
+            throw e;
         } finally {
             try {
-                in.close();
-            } catch (Exception exc) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    if (!error) throw e;
+                }
+            } finally {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    if (!error) throw e;
+                }
             }
-            try {
-                out.close();
-            } catch (Exception exc) {
-            }
-        } // end finally
+        }
+    }
 
-        return success;
-    } // end encodeFileToFile
-
     /**
      * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
      * 
      * @param infile Input file
      * @param outfile Output file
      * @return true if the operation is successful
-     * @since 2.2
+     * @throws IOException if there is a problem reading or writing either file
      */
     public static boolean decodeFileToFile( String infile,
-                                            String outfile ) {
-        boolean success = false;
-        java.io.InputStream in = null;
-        java.io.OutputStream out = null;
+                                            String outfile ) throws IOException {
+        java.io.InputStream in = new Base64.InputStream(new BufferedInputStream(new FileInputStream(infile)), Base64.DECODE);
+        java.io.OutputStream out = new java.io.BufferedOutputStream(new FileOutputStream(outfile));
+        boolean error = false;
         try {
-            in = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(infile)), Base64.DECODE);
-            out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
             byte[] buffer = new byte[65536]; // 64K
             int read = -1;
             while ((read = in.read(buffer)) >= 0) {
                 out.write(buffer, 0, read);
-            } // end while: through file
-            success = true;
-        } catch (IOException exc) {
-            exc.printStackTrace();
+            }
+            return true;
+        } catch (IOException e) {
+            error = true;
+            throw e;
         } finally {
             try {
-                in.close();
-            } catch (Exception exc) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    if (!error) throw e;
+                }
+            } finally {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    if (!error) throw e;
+                }
             }
-            try {
-                out.close();
-            } catch (Exception exc) {
-            }
-        } // end finally
+        }
+    }
 
-        return success;
-    } // end decodeFileToFile
-
     /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
 
     /**
@@ -1188,7 +1137,6 @@
      * encode/decode to/from Base64 notation on the fly.
      * 
      * @see Base64
-     * @since 1.3
      */
     public static class InputStream extends java.io.FilterInputStream {
         private boolean encode; // Encoding or decoding
@@ -1199,7 +1147,6 @@
         private int lineLength;
         private boolean breakLines; // Break lines at less than 80 characters
         private int options; // Record options used to create the stream.
-        private byte[] alphabet; // Local copies to avoid extra method calls
         private byte[] decodabet; // Local copies to avoid extra method calls
 
         /**
@@ -1210,7 +1157,7 @@
          */
         public InputStream( java.io.InputStream in ) {
             this(in, DECODE);
-        } // end constructor
+        }
 
         /**
          * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE mode.
@@ -1231,7 +1178,6 @@
          * @see Base64#ENCODE
          * @see Base64#DECODE
          * @see Base64#DONT_BREAK_LINES
-         * @since 2.0
          */
         public InputStream( java.io.InputStream in,
                             int options ) {
@@ -1243,9 +1189,8 @@
             this.position = -1;
             this.lineLength = 0;
             this.options = options; // Record for later, mostly to determine which alphabet to use
-            this.alphabet = getAlphabet(options);
             this.decodabet = getDecodabet(options);
-        } // end constructor
+        }
 
         /**
          * Reads enough of the input stream to convert to/from Base64 and returns the next byte.
@@ -1269,25 +1214,21 @@
                             if (b >= 0) {
                                 b3[i] = (byte)b;
                                 numBinaryBytes++;
-                            } // end if: not end of stream
-
-                        } // end try: read
-                        catch (IOException e) {
+                            }
+                        } catch (IOException e) {
                             // Only a problem if we got no data at all.
                             if (i == 0) throw e;
-
-                        } // end catch
-                    } // end for: each needed input byte
-
+                        }
+                    }
                     if (numBinaryBytes > 0) {
+                        // got data
                         encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
                         position = 0;
                         numSigBytes = 4;
-                    } // end if: got data
-                    else {
+                    } else {
                         return -1;
-                    } // end else
-                } // end if: encoding
+                    }
+                }
 
                 // Else decoding
                 else {
@@ -1303,23 +1244,22 @@
                         if (b < 0) break; // Reads a -1 if end of stream
 
                         b4[i] = (byte)b;
-                    } // end for: each needed input byte
+                    }
 
                     if (i == 4) {
+                        // got four characters
                         numSigBytes = decode4to3(b4, 0, buffer, 0, options);
                         position = 0;
-                    } // end if: got four characters
-                    else if (i == 0) {
+                    } else if (i == 0) {
+                        // padded correctly
                         return -1;
-                    } // end else if: also padded correctly
-                    else {
+                    } else {
                         // Must have broken out from above.
                         throw new IOException("Improperly padded Base64 input.");
-                    } // end
+                    }
+                }
+            }
 
-                } // end else: decode
-            } // end else: get data
-
             // Got data?
             if (position >= 0) {
                 // End of relevant data?
@@ -1328,27 +1268,21 @@
                 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
                     lineLength = 0;
                     return '\n';
-                } // end if
-                else {
-                    lineLength++; // This isn't important when decoding
-                    // but throwing an extra "if" seems
-                    // just as wasteful.
+                }
+                lineLength++; // This isn't important when decoding
+                // but throwing an extra "if" seems
+                // just as wasteful.
 
-                    int b = buffer[position++];
+                int b = buffer[position++];
 
-                    if (position >= bufferLength) position = -1;
+                if (position >= bufferLength) position = -1;
 
-                    return b & 0xFF; // This is how you "cast" a byte that's
-                    // intended to be unsigned.
-                } // end else
-            } // end if: position >= 0
+                return b & 0xFF; // This is how you "cast" a byte that's intended to be unsigned.
+            }
 
-            // Else error
-            else {
-                // When JDK1.4 is more accepted, use an assertion here.
-                throw new IOException("Error in Base64 code reading stream.");
-            } // end else
-        } // end read
+            // When JDK1.4 is more accepted, use an assertion here.
+            throw new IOException("Error in Base64 code reading stream.");
+        }
 
         /**
          * Calls {@link #read()} repeatedly until the end of stream is reached or <var>len</var> bytes are read. Returns number of
@@ -1375,12 +1309,12 @@
 
                 if (b >= 0) dest[off + i] = (byte)b;
                 else if (i == 0) return -1;
-                else break; // Out of 'for' loop
-            } // end for: each byte read
+                else break;
+            }
             return i;
-        } // end read
+        }
 
-    } // end inner class InputStream
+    }
 
     /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
 
@@ -1401,7 +1335,6 @@
         private byte[] b4; // Scratch used in a few places
         private boolean suspendEncoding;
         private int options; // Record for later
-        private byte[] alphabet; // Local copies to avoid extra method calls
         private byte[] decodabet; // Local copies to avoid extra method calls
 
         /**
@@ -1447,7 +1380,6 @@
             this.suspendEncoding = false;
             this.b4 = new byte[4];
             this.options = options;
-            this.alphabet = getAlphabet(options);
             this.decodabet = getDecodabet(options);
         } // end constructor
 
@@ -1465,7 +1397,7 @@
             if (suspendEncoding) {
                 super.out.write(theByte);
                 return;
-            } // end if: supsended
+            }
 
             // Encode?
             if (encode) {
@@ -1481,27 +1413,25 @@
                     } // end if: end of line
 
                     position = 0;
-                } // end if: enough to output
-            } // end if: encoding
+                }
+            }
 
             // Else, Decoding
             else {
                 // Meaningful Base64 character?
                 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
                     buffer[position++] = (byte)theByte;
-                    if (position >= bufferLength) // Enough to output.
-                    {
+                    if (position >= bufferLength) { // Enough to output.
                         int len = Base64.decode4to3(buffer, 0, b4, 0, options);
                         out.write(b4, 0, len);
                         // out.write( Base64.decode4to3( buffer ) );
                         position = 0;
-                    } // end if: enough to output
-                } // end if: meaningful base64 character
-                else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
+                    }
+                } else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
                     throw new IOException("Invalid character in Base64 data.");
-                } // end else: not white space either
-            } // end else: decoding
-        } // end write
+                }
+            }
+        }
 
         /**
          * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are written.
@@ -1520,14 +1450,13 @@
             if (suspendEncoding) {
                 super.out.write(theBytes, off, len);
                 return;
-            } // end if: supsended
+            }
 
             for (int i = 0; i < len; i++) {
                 write(theBytes[off + i]);
-            } // end for: each byte written
+            }
+        }
 
-        } // end write
-
         /**
          * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without closing the stream.
          * 
@@ -1538,14 +1467,12 @@
                 if (encode) {
                     out.write(encode3to4(b4, buffer, position, options));
                     position = 0;
-                } // end if: encoding
-                else {
+                } else {
                     throw new IOException("Base64 input not properly padded.");
-                } // end else: decoding
-            } // end if: buffer partially full
+                }
+            }
+        }
 
-        } // end flush
-
         /**
          * Flushes and closes (I think, in the superclass) the stream.
          * 
@@ -1554,17 +1481,19 @@
          */
         @Override
         public void close() throws IOException {
-            // 1. Ensure that pending characters are written
-            flushBase64();
+            try {
+                // 1. Ensure that pending characters are written
+                flushBase64();
+            } finally {
+                // 2. Actually close the stream
+                // Base class both flushes and closes.
+                super.close();
 
-            // 2. Actually close the stream
-            // Base class both flushes and closes.
-            super.close();
+                buffer = null;
+                out = null;
+            }
+        }
 
-            buffer = null;
-            out = null;
-        } // end close
-
         /**
          * Suspends encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a stream.
          * 
@@ -1574,7 +1503,7 @@
         public void suspendEncoding() throws IOException {
             flushBase64();
             this.suspendEncoding = true;
-        } // end suspendEncoding
+        }
 
         /**
          * Resumes encoding of the stream. May be helpful if you need to embed a piece of base640-encoded data in a stream.
@@ -1583,8 +1512,6 @@
          */
         public void resumeEncoding() {
             this.suspendEncoding = false;
-        } // end resumeEncoding
-
-    } // end inner class OutputStream
-
-} // end class Base64
+        }
+    }
+}



From dna-commits at lists.jboss.org  Tue Mar 10 12:19:07 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 12:19:07 -0400
Subject: [dna-commits] DNA SVN: r766 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lh4fb-0004db-Km@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 12:19:07 -0400 (Tue, 10 Mar 2009)
New Revision: 766

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
Log:
DNA-295 TCK Tests for Session.export* Methods Fail

Corrected a line to only print to System.out when debugging.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	2009-03-10 16:18:31 UTC (rev 765)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	2009-03-10 16:19:07 UTC (rev 766)
@@ -47,10 +47,8 @@
 /**
  * Superclass of DNA JCR exporters, provides basic support for traversing through the nodes recursively (if needed), exception
  * wrapping (since {@link ItemVisitor} does not allow checked exceptions to be thrown from its visit* methods, and the ability to
- * wrap an {@link OutputStream} with a {@link ContentHandler}.
- * <p />
- * Each exporter is only intended to be used once (by calling <code>exportView</code>) and discarded. This class is <b>NOT</b>
- * thread-safe.
+ * wrap an {@link OutputStream} with a {@link ContentHandler}. <p /> Each exporter is only intended to be used once (by calling
+ * <code>exportView</code>) and discarded. This class is <b>NOT</b> thread-safe.
  * 
  * @see JcrSystemViewExporter
  * @see JcrDocumentViewExporter
@@ -318,7 +316,7 @@
             emit("</");
             emit(name);
             emit(">");
-            System.out.println();
+            if (LOG_TO_CONSOLE) System.out.println();
 
         }
 



From dna-commits at lists.jboss.org  Tue Mar 10 12:37:29 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 12:37:29 -0400
Subject: [dna-commits] DNA SVN: r767 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1Lh4xN-0002P6-GA@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 12:37:29 -0400 (Tue, 10 Mar 2009)
New Revision: 767

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
   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/JcrDocumentViewExporter.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
Log:
DNA-295 TCK Tests for Session.export* Methods Fail

Applied the "cache" patch that streamlined the conversion of DNA Name objects to strings containing the prefixed form of the Name.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	2009-03-10 16:19:07 UTC (rev 766)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrExporter.java	2009-03-10 16:37:29 UTC (rev 767)
@@ -74,6 +74,11 @@
     private final Collection<String> restrictedPrefixes;
 
     /**
+     * Cache from {@link Name}s to their rewritten version based on session uri mappings.
+     */
+    private final Map<Name, String> prefixedNames;
+
+    /**
      * Creates the exporter
      * 
      * @param session the session in which the exporter is created
@@ -83,9 +88,37 @@
                          Collection<String> restrictedPrefixes ) {
         this.session = session;
         this.restrictedPrefixes = restrictedPrefixes;
+        this.prefixedNames = new HashMap<Name, String>();
     }
 
     /**
+     * Returns the &quot;prefixed&quot; or rewritten version of <code>baseName</code> based on the URI mappings in the current
+     * session. For example:</p> If the namespace &quot;http://www.example.com/JCR/example/1.0&quot; is mapped to the prefix
+     * &quot;foo&quot; in the current session (or as a persistent mapping that has not been re-mapped in the current session),
+     * this method will return the string &quot;foo:bar&quot; when passed a {@link Name} with uri
+     * &quot;http://www.example.com/JCR/example/1.0&quot; and local name &quot;bar&quot;.</p> This method does manage and utilize
+     * a {@link Name} to {@link String} cache at the instance scope.
+     * 
+     * @param baseName the name to be re-mapped into its prefixed version
+     * @return the prefixed version of <code>baseName</code> based on the current session URI mappings (which include all
+     *         persistent URI mappings by default).
+     * @see #prefixedNames
+     * @see javax.jcr.Session#setNamespacePrefix(String, String)
+     * @see javax.jcr.Session#getNamespacePrefix(String)
+     */
+    protected String getPrefixedName( Name baseName ) {
+        String prefixedName = prefixedNames.get(baseName);
+
+        if (prefixedName == null) {
+            prefixedName = baseName.getString(session.getExecutionContext().getNamespaceRegistry());
+
+            prefixedNames.put(baseName, prefixedName);
+        }
+
+        return prefixedName;
+    }
+
+    /**
      * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by invoking SAX events on
      * <code>contentHandler</code>.
      * 
@@ -188,7 +221,7 @@
                                  Attributes atts ) throws SAXException {
         contentHandler.startElement(name.getNamespaceUri(),
                                     NAME_ENCODER.encode(name.getLocalName()),
-                                    NAME_ENCODER.encode(name.getString(session.getExecutionContext().getNamespaceRegistry())),
+                                    NAME_ENCODER.encode(getPrefixedName(name)),
                                     atts);
     }
 
@@ -204,7 +237,7 @@
                                Name name ) throws SAXException {
         contentHandler.endElement(name.getNamespaceUri(),
                                   NAME_ENCODER.encode(name.getLocalName()),
-                                  NAME_ENCODER.encode(name.getString(session.getExecutionContext().getNamespaceRegistry())));
+                                  NAME_ENCODER.encode(getPrefixedName(name)));
     }
 
     /**

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-03-10 16:19:07 UTC (rev 766)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-10 16:37:29 UTC (rev 767)
@@ -139,7 +139,7 @@
         /* Name of node type that holds xmltext from document view import (see JCR 1.0 spec section 7.3.2) */
         JcrNodeType xmlText = new JcrNodeType(
                                               session,
-                                              DnaLexicon.XML_TEXT_TYPE,
+                                              DnaLexicon.XML_TEXT,
                                               Arrays.asList(new NodeType[] {base}),
                                               NO_PRIMARY_ITEM_NAME,
                                               NO_CHILD_NODES,
@@ -166,13 +166,13 @@
                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                               session,
                                                                                                               null,
-                                                                                                              DnaLexicon.XML_TEXT,
+                                                                                                              JcrLexicon.XMLTEXT,
                                                                                                               OnParentVersionBehavior.VERSION.getJcrValue(),
                                                                                                               false,
                                                                                                               true,
                                                                                                               false,
                                                                                                               false,
-                                                                                                              DnaLexicon.XML_TEXT_TYPE,
+                                                                                                              DnaLexicon.XML_TEXT,
                                                                                                               new NodeType[] {xmlText})}),
                                                  NO_PROPERTIES, IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
 

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-10 16:19:07 UTC (rev 766)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-10 16:37:29 UTC (rev 767)
@@ -40,8 +40,8 @@
 
     /**
      * Mixin type that indicates the node contains an xmltext node which holds xmltext from document view import (see JCR 1.0
-     * specification section 7.3.2). This node has a child node named {@link DnaLexicon#XML_TEXT} of type
-     * {@link DnaLexicon#XML_TEXT_TYPE}.
+     * specification section 7.3.2). This node has a child node named {@link JcrLexicon#XMLTEXT} of type
+     * {@link DnaLexicon#XML_TEXT}.
      */
     public static final Name XML_CONTENT = new BasicName(Namespace.URI, "xmlContent");
 
@@ -49,12 +49,6 @@
      * Name of node type that holds xmltext from document view import (see JCR 1.0 specification section 7.3.2). It is defined in
      * the node type named {@link DnaLexicon#XML_CONTENT}.
      */
-    public static final Name XML_TEXT_TYPE = new BasicName(Namespace.URI, "xmlText");
+    public static final Name XML_TEXT = new BasicName(Namespace.URI, "xmlText");
 
-    /**
-     * Name of the child node that holds xmltext from document view import (see JCR 1.0 specification section 7.3.2). This is the
-     * name of the child node of the {@link DnaLexicon#XML_CONTENT} mixin type. By definition, this node has a required primary
-     * type of {@link DnaLexicon#XML_TEXT_TYPE}.
-     */
-    public static final Name XML_TEXT = new BasicName(Namespace.URI, "xmltext");
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-10 16:19:07 UTC (rev 766)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-10 16:37:29 UTC (rev 767)
@@ -97,7 +97,7 @@
 
             Name propName = ((AbstractJcrProperty)prop).getDnaProperty().getName();
 
-            String localPropName = propName.getString(executionContext.getNamespaceRegistry());
+            String localPropName = getPrefixedName(propName);
 
             Value value;
             if (prop instanceof JcrSingleValueProperty) {
@@ -151,8 +151,7 @@
     private boolean isXmlTextNode( Node node ) throws RepositoryException {
         // ./xmltext/xmlcharacters exception (see JSR-170 Spec 6.4.2.3)
 
-        ExecutionContext executionContext = session.getExecutionContext();
-        if (JcrLexicon.XMLTEXT.getString(executionContext.getNamespaceRegistry()).equals(node.getName())) {
+        if (getPrefixedName(JcrLexicon.XMLTEXT).equals(node.getName())) {
             if (node.getNodes().getSize() == 0) {
 
                 PropertyIterator properties = node.getProperties();
@@ -161,11 +160,11 @@
                 while (properties.hasNext()) {
                     Property property = properties.nextProperty();
 
-                    if (JcrLexicon.PRIMARY_TYPE.getString(executionContext.getNamespaceRegistry()).equals(property.getName())) {
+                    if (getPrefixedName(JcrLexicon.PRIMARY_TYPE).equals(property.getName())) {
                         continue;
                     }
 
-                    if (JcrLexicon.XMLCHARACTERS.getString(executionContext.getNamespaceRegistry()).equals(property.getName())) {
+                    if (getPrefixedName(JcrLexicon.XMLCHARACTERS).equals(property.getName())) {
                         xmlCharactersFound = true;
                         continue;
                     }
@@ -195,8 +194,7 @@
 
         assert isXmlTextNode(node);
         
-        ExecutionContext executionContext = session.getExecutionContext();
-        Property xmlCharacters = node.getProperty(JcrLexicon.XMLCHARACTERS.getString(executionContext.getNamespaceRegistry()));
+        Property xmlCharacters = node.getProperty(getPrefixedName(JcrLexicon.XMLCHARACTERS));
 
         assert xmlCharacters != null;
 

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-03-10 16:19:07 UTC (rev 766)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	2009-03-10 16:37:29 UTC (rev 767)
@@ -15,7 +15,6 @@
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.Base64;
 import org.jboss.dna.common.xml.XmlCharacters;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -65,13 +64,12 @@
                             ContentHandler contentHandler,
                             boolean skipBinary,
                             boolean noRecurse ) throws RepositoryException, SAXException {
-        ExecutionContext executionContext = session.getExecutionContext();
 
         // start the sv:node element for this JCR node
         AttributesImpl atts = new AttributesImpl();
         atts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
                           JcrSvLexicon.NAME.getLocalName(),
-                          JcrSvLexicon.NAME.getString(executionContext.getNamespaceRegistry()),
+                          getPrefixedName(JcrSvLexicon.NAME),
                           PropertyType.nameFromValue(PropertyType.STRING),
                           node.getName());
 
@@ -141,21 +139,20 @@
         assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
                                                          + " on non-DNA property";
 
-        ExecutionContext executionContext = session.getExecutionContext();
         AbstractJcrProperty prop = (AbstractJcrProperty)property;
 
         // first set the property sv:name attribute
         AttributesImpl propAtts = new AttributesImpl();
         propAtts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
                               JcrSvLexicon.NAME.getLocalName(),
-                              JcrSvLexicon.NAME.getString(executionContext.getNamespaceRegistry()),
+                              getPrefixedName(JcrSvLexicon.NAME),
                               PropertyType.nameFromValue(PropertyType.STRING),
                               prop.getName());
 
         // and it's sv:type attribute
         propAtts.addAttribute(JcrSvLexicon.TYPE.getNamespaceUri(),
                               JcrSvLexicon.TYPE.getLocalName(),
-                              JcrSvLexicon.TYPE.getString(executionContext.getNamespaceRegistry()),
+                              getPrefixedName(JcrSvLexicon.TYPE),
                               PropertyType.nameFromValue(PropertyType.STRING),
                               PropertyType.nameFromValue(prop.getType()));
 
@@ -224,7 +221,7 @@
             }
 
             if (allCharsAreValidXml) {
-                
+
                 startElement(contentHandler, JcrSvLexicon.VALUE, null);
                 contentHandler.characters(chars, 0, chars.length);
                 endElement(contentHandler, JcrSvLexicon.VALUE);



From dna-commits at lists.jboss.org  Tue Mar 10 12:51:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 12:51:51 -0400
Subject: [dna-commits] DNA SVN: r768 - in trunk/dna-jcr/src/test: resources
	and 1 other directory.
Message-ID: <E1Lh5BH-0006uo-45@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 12:51:50 -0400 (Tue, 10 Mar 2009)
New Revision: 768

Added:
   trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
Removed:
   trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml
Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
Renamed the file containing the repository content for our TCK unit tests.

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-03-10 16:37:29 UTC (rev 767)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-10 16:51:50 UTC (rev 768)
@@ -48,7 +48,8 @@
 import org.jboss.dna.graph.property.Path;
 
 /**
- * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests.
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests. Note that technically these are not the
+ * actual TCK, but these are unit tests that happen to be similar to (or provided the basis for) a subset of the TCK.
  */
 public class JcrTckTest {
 
@@ -287,7 +288,7 @@
                 Path destinationPath = executionContext.getValueFactories().getPathFactory().create("/");
                 GraphImporter importer = new GraphImporter(graph);
 
-                URI xmlContent = new File("src/test/resources/repositoryJackrabbitTck.xml").toURI();
+                URI xmlContent = new File("src/test/resources/repositoryForTckTests.xml").toURI();
                 Graph.Batch batch = importer.importXml(xmlContent, Location.create(destinationPath));
                 batch.execute();
 

Copied: trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml (from rev 764, trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml)
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	                        (rev 0)
+++ trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-03-10 16:51:50 UTC (rev 768)
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss DNA (http://www.jboss.org/dna)
+  ~
+  ~ See the COPYRIGHT.txt file distributed with this work for information
+  ~ regarding copyright ownership.  Some portions may be licensed
+  ~ to Red Hat, Inc. under one or more contributor license agreements.
+  ~ See the AUTHORS.txt file in the distribution for a full listing of 
+  ~ individual contributors.
+  ~
+  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+  ~ is licensed to you under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ JBoss DNA is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+  ~ for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public License
+  ~ along with this distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
+            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;" >
+	<!--  This stanza checks for the jcr:xmltext special case for document export.  DO NOT change this element. -->
+	   <dna:xmlText jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+	</nt:unstructured>
+	<nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
+	<nt:unstructured jcr:name="node3" />
+	<nt:unstructured jcr:name="node4" />
+</testroot>
\ No newline at end of file

Deleted: trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	2009-03-10 16:37:29 UTC (rev 767)
+++ trunk/dna-jcr/src/test/resources/repositoryJackrabbitTck.xml	2009-03-10 16:51:50 UTC (rev 768)
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-  ~ JBoss DNA (http://www.jboss.org/dna)
-  ~
-  ~ See the COPYRIGHT.txt file distributed with this work for information
-  ~ regarding copyright ownership.  Some portions may be licensed
-  ~ to Red Hat, Inc. under one or more contributor license agreements.
-  ~ See the AUTHORS.txt file in the distribution for a full listing of 
-  ~ individual contributors.
-  ~
-  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
-  ~ is licensed to you under the terms of the GNU Lesser General Public License as
-  ~ published by the Free Software Foundation; either version 2.1 of
-  ~ the License, or (at your option) any later version.
-  ~
-  ~ JBoss DNA is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-  ~ for more details.
-  ~
-  ~ You should have received a copy of the GNU Lesser General Public License
-  ~ along with this distribution; if not, write to:
-  ~ Free Software Foundation, Inc.
-  ~ 51 Franklin Street, Fifth Floor
-  ~ Boston, MA  02110-1301  USA
-  -->
-<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
-            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;" >
-	<!--  This stanza checks for the jcr:xmltext special case for document export.  DO NOT change this element. -->
-	   <dna:xmlText jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
-	</nt:unstructured>
-	<nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
-	<nt:unstructured jcr:name="node3" />
-	<nt:unstructured jcr:name="node4" />
-</testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Tue Mar 10 15:43:15 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 10 Mar 2009 15:43:15 -0400
Subject: [dna-commits] DNA SVN: r769 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/property/basic
	and 3 other directories.
Message-ID: <E1Lh7r9-0001cV-Nn@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-10 15:43:15 -0400 (Tue, 10 Mar 2009)
New Revision: 769

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathFactory.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/PathValueFactory.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/RootPath.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/AbstractPathTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/RootPathTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
Log:
DNA-194 Implement update JCR capability

Small refactoring to make the loading of nodes more efficient.  This includes the addition of a new method on Path to obtain an Iterator<Path> from the root node to that instance.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -495,6 +495,14 @@
     public Iterator<Segment> iterator();
 
     /**
+     * Return an iterator that walks the paths from the root path down to this path. This method always returns at least one path
+     * (the root returns an iterator containing itself).
+     * 
+     * @return the path iterator; never null
+     */
+    public Iterator<Path> pathsFromRoot();
+
+    /**
      * Obtain a copy of the segments in this path. None of the segments are encoded.
      * 
      * @return the array of segments as a copy

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathFactory.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathFactory.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -133,6 +133,19 @@
                  int index );
 
     /**
+     * Create a path by appending the supplied names to the parent path.
+     * 
+     * @param parentPath the path that is to provide the basis for the new path
+     * @param segmentName the name of the segment to be appended to the parent path
+     * @param index the index for the new segment
+     * @return the new path
+     * @throws IllegalArgumentException if the parent path reference or the segment name is null, or if the index is invalid
+     */
+    Path create( Path parentPath,
+                 String segmentName,
+                 int index );
+
+    /**
      * Create a path by appending the supplied names to the parent path. If no names are appended, the parent path is returned.
      * 
      * @param parentPath the path that is to provide the basis for the new path

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -29,6 +29,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.CommonI18n;
 import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.common.util.CheckArg;
@@ -83,6 +84,45 @@
         }
     };
 
+    @NotThreadSafe
+    protected static class SingleIterator<T> implements Iterator<T> {
+        private T value;
+
+        protected SingleIterator( T value ) {
+            this.value = value;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return value != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            if (value == null) throw new NoSuchElementException();
+            T next = value;
+            value = null;
+            return next;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
     private transient int hc = 0;
 
     protected boolean isNormalized( List<Segment> segments ) {
@@ -368,7 +408,23 @@
 
     /**
      * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.property.Path#pathsFromRoot()
      */
+    public Iterator<Path> pathsFromRoot() {
+        LinkedList<Path> paths = new LinkedList<Path>();
+        Path path = this;
+        while (path != null) {
+            paths.addFirst(path);
+            if (path.isRoot()) break;
+            path = path.getParent();
+        }
+        return paths.iterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public Path relativeTo( Path startingPath ) {
         CheckArg.isNotNull(startingPath, "to");
         if (!this.isAbsolute()) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/PathValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/PathValueFactory.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/PathValueFactory.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -401,8 +401,7 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.property.PathFactory#create(org.jboss.dna.graph.property.Path,
-     *      org.jboss.dna.graph.property.Path)
+     * @see org.jboss.dna.graph.property.PathFactory#create(org.jboss.dna.graph.property.Path, org.jboss.dna.graph.property.Path)
      */
     public Path create( Path parentPath,
                         Path childPath ) {
@@ -438,8 +437,19 @@
 
     /**
      * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.property.PathFactory#create(org.jboss.dna.graph.property.Path, java.lang.String, int)
      */
     public Path create( Path parentPath,
+                        String segmentName,
+                        int index ) {
+        return create(parentPath, nameValueFactory.create(segmentName), index);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Path create( Path parentPath,
                         Name... segmentNames ) {
         CheckArg.isNotNull(parentPath, "parent path");
         if (segmentNames == null || segmentNames.length == 0) return parentPath;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/RootPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/RootPath.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/RootPath.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -342,6 +342,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.property.Path#pathsFromRoot()
+     */
+    @Override
+    public Iterator<Path> pathsFromRoot() {
+        return new SingleIterator<Path>(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.property.Path#size()
      */
     public int size() {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/AbstractPathTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/AbstractPathTest.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/AbstractPathTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -30,7 +30,9 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.graph.property.InvalidPathException;
 import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -231,4 +233,18 @@
         assertThat(path.toString(), is(notNullValue()));
     }
 
+    @Test
+    public void shouldGetPathsFromRoot() {
+        Iterator<Path> iter = path.pathsFromRoot();
+        List<Path.Segment> segments = path.getSegmentsList();
+        List<Path.Segment> lastSegments = new ArrayList<Path.Segment>();
+        while (iter.hasNext()) {
+            Path next = iter.next();
+            assertThat(next, is(notNullValue()));
+            if (!next.isRoot()) lastSegments.add(next.getLastSegment());
+        }
+        assertThat(lastSegments.size(), is(path.size()));
+        assertThat(lastSegments, is(segments));
+    }
+
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/RootPathTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/RootPathTest.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/RootPathTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -30,15 +30,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.jboss.dna.common.text.TextEncoder;
 import org.jboss.dna.graph.property.InvalidPathException;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.basic.BasicName;
-import org.jboss.dna.graph.property.basic.BasicPath;
-import org.jboss.dna.graph.property.basic.BasicPathSegment;
-import org.jboss.dna.graph.property.basic.RootPath;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -253,4 +250,12 @@
         assertThat(root.equals(root), is(true));
     }
 
+    @Test
+    public void shouldReturnIteratorWithRootPathFromPathsFromRoot() {
+        Iterator<Path> iter = root.pathsFromRoot();
+        assertThat(iter.hasNext(), is(true));
+        assertThat(iter.next(), is(root));
+        assertThat(iter.hasNext(), is(false));
+    }
+
 }

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-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -58,7 +58,6 @@
 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.graph.property.Path.Segment;
 
 /**
  * @author jverhaeg
@@ -72,7 +71,7 @@
     private final NodeDefinition definition;
     protected Location location;
     private Map<Name, Property> properties;
-    private List<Path.Segment> children;
+    private List<Location> children;
 
     AbstractJcrNode( JcrSession session,
                      Location location,
@@ -114,7 +113,7 @@
         location = location.with(uuid);
     }
 
-    final void setChildren( List<Segment> children ) {
+    final void setChildren( List<Location> children ) {
         assert children != null;
         this.children = children;
     }
@@ -418,8 +417,9 @@
                                               .getValueFactories()
                                               .getPathFactory()
                                               .createSegment(relativePath);
-                for (Path.Segment child : children) {
-                    if (child.equals(segment)) return true;
+                for (Location child : children) {
+                    Path.Segment childSegment = child.getPath().getLastSegment();
+                    if (childSegment.equals(segment)) return true;
                 }
             } catch (ValueFormatException e) {
                 throw new RepositoryException(JcrI18n.invalidRelativePath.text(relativePath));
@@ -462,7 +462,7 @@
         if (children == null) {
             return new JcrEmptyNodeIterator();
         }
-        return new JcrChildNodeIterator(this, namespaces(), children);
+        return new JcrChildNodeIterator(this, children);
     }
 
     /**
@@ -479,11 +479,11 @@
         List<Object> patterns = createPatternsFor(namePattern);
 
         // Implementing exact-matching only for now to prototype types as properties
-        List<Path.Segment> matchingChildren = new LinkedList<Path.Segment>();
+        List<Location> matchingChildren = new LinkedList<Location>();
         NamespaceRegistry registry = namespaces();
         boolean foundMatch = false;
-        for (Path.Segment child : children) {
-            String childName = child.getName().getString(registry);
+        for (Location child : children) {
+            String childName = child.getPath().getLastSegment().getName().getString(registry);
             for (Object patternOrMatch : patterns) {
                 if (patternOrMatch instanceof Pattern) {
                     Pattern pattern = (Pattern)patternOrMatch;
@@ -499,7 +499,7 @@
                 }
             }
         }
-        return new JcrChildNodeIterator(this, registry, matchingChildren);
+        return new JcrChildNodeIterator(this, matchingChildren);
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -30,8 +30,7 @@
 import javax.jcr.RepositoryException;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.property.NamespaceRegistry;
-import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.Location;
 
 /**
  * @author jverhaeg
@@ -39,20 +38,18 @@
 @Immutable
 final class JcrChildNodeIterator implements NodeIterator {
 
-    private final NamespaceRegistry registry;
-    private final Node parent;
-    private final Iterator<Path.Segment> iterator;
+    private final AbstractJcrNode parent;
+    private final Iterator<Location> iterator;
+    private final JcrSession session;
     private int ndx;
     private int size;
 
-    JcrChildNodeIterator( Node parent,
-                          NamespaceRegistry registry,
-                          List<Path.Segment> children ) {
+    JcrChildNodeIterator( AbstractJcrNode parent,
+                          List<Location> children ) {
         assert parent != null;
-        assert registry != null;
         assert children != null;
-        this.registry = registry;
         this.parent = parent;
+        this.session = parent.session();
         iterator = children.iterator();
         size = children.size();
     }
@@ -99,11 +96,10 @@
      * @see javax.jcr.NodeIterator#nextNode()
      */
     public Node nextNode() {
-        Path.Segment childSegment = iterator.next();
+        Location location = iterator.next();
         ndx++;
-        String childName = childSegment.getString(registry);
         try {
-            return parent.getNode(childName);
+            return session.getChild(parent, location);
         } catch (RepositoryException error) {
             throw new RuntimeException(error);
         }

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-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -123,10 +123,10 @@
      */
     private final Map<String, Object> sessionAttributes;
 
-    private final ReferenceMap<UUID, Node> nodesByUuid;
-    private final ReferenceMap<String, Node> nodesByJcrUuid;
+    private final ReferenceMap<UUID, AbstractJcrNode> nodesByUuid;
+    private final ReferenceMap<String, AbstractJcrNode> nodesByJcrUuid;
     private boolean isLive;
-    private AbstractJcrNode rootNode;
+    private JcrRootNode rootNode;
     private PropertyDefinition anyMultiplePropertyDefinition;
     private transient org.jboss.dna.graph.property.Property defaultPrimaryType;
 
@@ -153,8 +153,8 @@
                                   this.repository.getConnectionFactory(),
                                   this.executionContext);
 
-        this.nodesByUuid = new ReferenceMap<UUID, Node>(ReferenceType.STRONG, ReferenceType.SOFT);
-        this.nodesByJcrUuid = new ReferenceMap<String, Node>(ReferenceType.STRONG, ReferenceType.SOFT);
+        this.nodesByUuid = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
+        this.nodesByJcrUuid = new ReferenceMap<String, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
         this.isLive = true;
 
         assert this.repository != null;
@@ -182,7 +182,7 @@
      * 
      * @return nodesByUuid
      */
-    Map<UUID, Node> getNodesByUuid() {
+    Map<UUID, AbstractJcrNode> getNodesByUuid() {
         return Collections.unmodifiableMap(nodesByUuid);
     }
 
@@ -394,26 +394,16 @@
         }
         // Since we don't know whether path refers to a node or a property, look to see if we can tell it's a node ...
         if (path.getLastSegment().hasIndex()) {
-            try {
-                return getNode(path);
-            } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-                // If the node isn't found, throw a PathNotFoundException
-                throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
-            }
+            return getNode(path);
         }
         // We can't tell from the name, so try a node first ...
         try {
             return getNode(path);
-        } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            // A node was not found, so treat look for a node using the parent as the node's path ...
+        } catch (PathNotFoundException e) {
+            // A node was not found, so look for a node using the parent as the node's path ...
             Path parentPath = path.getParent();
             Name propertyName = path.getLastSegment().getName();
-            try {
-                return getNode(parentPath).getProperty(propertyName.getString(namespaces()));
-            } catch (org.jboss.dna.graph.property.PathNotFoundException e2) {
-                // If the node isn't found, throw a PathNotFoundException
-                throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
-            }
+            return getNode(parentPath).getProperty(propertyName.getString(namespaces()));
         }
     }
 
@@ -427,19 +417,65 @@
         throw new UnsupportedOperationException();
     }
 
+    Node getChild( AbstractJcrNode parent,
+                   Location location ) throws RepositoryException {
+        Node child = null;
+        UUID uuid = location.getUuid();
+        if (uuid != null) {
+            // The location has a UUID, so look up the node by this UUID in the session ...
+            child = getNode(uuid);
+        }
+        if (child == null) {
+            // The child was not found in the session's cache, so create the node using its path ...
+            child = loadNode(parent, location.getPath());
+        }
+        return child;
+    }
+
     /**
      * Find or create a JCR Node for the given path. This method works for the root node, too.
      * 
      * @param path the path; may not be null
      * @return the JCR node instance for the given path; never null
+     * @throws PathNotFoundException if the path could not be found
      * @throws RepositoryException if there is a problem
      */
-    private Node getNode( Path path ) throws RepositoryException {
+    Node getNode( Path path ) throws RepositoryException, PathNotFoundException {
+        if (path.isRoot()) return rootNode();
+
+        // Start at the root and walk down the path ...
+        AbstractJcrNode parent = rootNode();
+        AbstractJcrNode node = null;
+        Iterator<Path> pathIter = path.pathsFromRoot();
+        while (pathIter.hasNext()) {
+            node = loadNode(parent, pathIter.next()); // should throw PathNotFoundException if not there
+            parent = node;
+        }
+        return node;
+    }
+
+    /**
+     * Find or create a JCR Node for the given path. This method works for the root node, too.
+     * 
+     * @param parent the parent of the node, if known; null if the parent is not known
+     * @param path the path; may not be null
+     * @return the JCR node instance for the given path; never null
+     * @throws PathNotFoundException if the path could not be found
+     * @throws RepositoryException if there is a problem
+     */
+    private AbstractJcrNode loadNode( AbstractJcrNode parent,
+                                      Path path ) throws RepositoryException, PathNotFoundException {
         boolean isRoot = path.isRoot();
         if (isRoot && rootNode != null) return rootNode;
 
         // Get node from source and get it's UUID ...
-        org.jboss.dna.graph.Node graphNode = graph.getNodeAt(path);
+        org.jboss.dna.graph.Node graphNode = null;
+        try {
+            graphNode = graph.getNodeAt(path);
+        } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+            // If the node isn't found, throw a PathNotFoundException
+            throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
+        }
 
         // Now get the DNA node's UUID ...
         Location location = graphNode.getLocation();
@@ -447,6 +483,10 @@
         UUID uuid = location.getUuid();
         org.jboss.dna.graph.property.Property uuidProperty = null;
         if (uuid != null) {
+            // Check for an existing node at this UUID ...
+            AbstractJcrNode existing = nodesByUuid.get(uuid);
+            if (existing != null) return existing;
+
             // Considered an identification property ...
             uuidProperty = location.getIdProperty(JcrLexicon.UUID);
             if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
@@ -485,7 +525,7 @@
 
         // See if there is already a JCR node object for this UUID ...
         if (uuid != null && !isRoot) {
-            Node node = getNode(uuid);
+            AbstractJcrNode node = getNode(uuid);
             if (node != null) return node;
         }
 
@@ -538,7 +578,10 @@
             node = new JcrRootNode(this, location, definition);
         } else {
             // Find the parent ...
-            AbstractJcrNode parent = (AbstractJcrNode)getNode(path.getParent());
+            if (parent == null) {
+                parent = (AbstractJcrNode)getNode(path.getParent());
+            }
+            assert parent != null;
 
             // Find the node definition for this node ...
             if (definition == null) {
@@ -558,11 +601,11 @@
 
         // Now populate the node and add to the cache ...
         populateNode(node, graphNode, uuid, uuidProperty, primaryTypeProperty);
-        if (isRoot) rootNode = node;
+        if (isRoot) rootNode = (JcrRootNode)node;
         return node;
     }
 
-    Node getNode( UUID uuid ) {
+    AbstractJcrNode getNode( UUID uuid ) {
         return nodesByUuid.get(uuid);
     }
 
@@ -586,17 +629,24 @@
         return result;
     }
 
+    JcrRootNode rootNode() throws RepositoryException {
+        // Return cached root node if available
+        if (rootNode != null) {
+            return rootNode;
+        }
+        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
+        loadNode(null, rootPath); // sets the root node
+        assert rootNode != null;
+        return rootNode;
+    }
+
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Session#getRootNode()
      */
     public Node getRootNode() throws RepositoryException {
-        // Return cached root node if available
-        if (rootNode != null) {
-            return rootNode;
-        }
-        return getNode(executionContext.getValueFactories().getPathFactory().createRootPath());
+        return rootNode();
     }
 
     /**
@@ -806,7 +856,7 @@
         // --------------------------------------------------
         // Create JCR children for corresponding DNA children
         // --------------------------------------------------
-        node.setChildren(graphNode.getChildrenSegments());
+        node.setChildren(graphNode.getChildren());
 
         // ------------------------------------------------------
         // Create JCR properties for corresponding DNA properties

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-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -52,7 +52,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -67,13 +67,18 @@
     static MockAbstractJcrNode createChild( JcrSession session,
                                             String name,
                                             int index,
-                                            List<Segment> children,
-                                            Node parent ) throws Exception {
+                                            List<Location> children,
+                                            AbstractJcrNode parent ) throws Exception {
         MockAbstractJcrNode child = new MockAbstractJcrNode(session, name, parent);
-        Segment seg = session.getExecutionContext().getValueFactories().getPathFactory().createSegment(name, index);
-        children.add(seg);
+        String parentPath = parent.getPath();
+        String childPath = parentPath + "/" + name + "[" + index + "]";
+        Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(childPath);
+        Location location = Location.create(path, UUID.randomUUID());
+        children.add(location);
         // Stub the session to return this node ...
-        String absolutePath = parent.getPath() + "/" + seg.getString(session.getExecutionContext().getNamespaceRegistry());
+        String absolutePath = path.getString(session.getExecutionContext().getNamespaceRegistry());
+        stub(session.getChild(parent, location)).toReturn(child);
+        stub(session.getNode(location.getUuid())).toReturn(child);
         stub(session.getItem(absolutePath)).toReturn(child);
         return child;
     }
@@ -120,7 +125,7 @@
     private Workspace workspace;
     @Mock
     private Repository repository;
-    private List<Segment> children;
+    private List<Location> children;
     private Map<Name, Property> properties;
     private ExecutionContext context;
 
@@ -129,7 +134,7 @@
         MockitoAnnotations.initMocks(this);
         context = new ExecutionContext();
         stub(session.getExecutionContext()).toReturn(context);
-        children = new ArrayList<Segment>();
+        children = new ArrayList<Location>();
         properties = new HashMap<Name, Property>();
         node = new MockAbstractJcrNode(session, "node", null);
         node.setProperties(properties);
@@ -397,8 +402,8 @@
         stub(prop.getName()).toReturn("prop");
         properties.put(name(prop.getName()), prop);
         assertThat(node.hasNode("prop"), is(false));
-        Node child = createChild(session, "child", 1, children, node);
-        Node child2 = createChild(session, "child2", 1, children, child);
+        AbstractJcrNode child = createChild(session, "child", 1, children, node);
+        AbstractJcrNode child2 = createChild(session, "child2", 1, children, child);
         node.setChildren(children);
         assertThat(node.hasNode("child"), is(true));
         stub(session.getItem("/node/child/{}child2")).toReturn(child2);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -32,7 +32,7 @@
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
 import org.junit.Before;
 import org.junit.Test;
@@ -47,14 +47,14 @@
     private AbstractJcrNode node;
     @Mock
     private JcrSession session;
-    private List<Segment> children;
+    private List<Location> children;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         ExecutionContext context = new ExecutionContext();
         stub(session.getExecutionContext()).toReturn(context);
-        children = new ArrayList<Segment>();
+        children = new ArrayList<Location>();
         node = new MockAbstractJcrNode(session, "node", null);
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -30,14 +30,12 @@
 import java.util.HashMap;
 import java.util.UUID;
 import javax.jcr.ItemNotFoundException;
-import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.Path.Segment;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -49,7 +47,7 @@
 public class JcrNodeTest {
 
     private JcrNode node;
-    private Node root;
+    private AbstractJcrNode root;
     @Mock
     private JcrSession session;
     @Mock
@@ -73,7 +71,7 @@
         stub(session.getNode(rootUuid)).toReturn(root);
         stub(session.getNode(uuid)).toReturn(node);
         node.setProperties(new HashMap<Name, Property>());
-        node.setChildren(new ArrayList<Segment>());
+        node.setChildren(new ArrayList<Location>());
     }
 
     @Test( expected = ItemNotFoundException.class )

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-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -281,7 +281,7 @@
 
     @Test
     public void shouldProvideRootNode() throws Exception {
-        Map<UUID, Node> nodesByUuid = session.getNodesByUuid();
+        Map<UUID, AbstractJcrNode> nodesByUuid = session.getNodesByUuid();
         assertThat(nodesByUuid.isEmpty(), is(true));
         Node root = session.getRootNode();
         assertThat(root, notNullValue());

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-10 16:51:50 UTC (rev 768)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-10 19:43:15 UTC (rev 769)
@@ -111,10 +111,10 @@
     public void shouldProvideNode() throws Exception {
         UUID referencedUuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
-        Node referencedNode = mock(Node.class);
+        AbstractJcrNode referencedNode = mock(AbstractJcrNode.class);
         stub(session.getNode(referencedUuid)).toReturn(referencedNode);
         prop = new JcrSingleValueProperty(node, definition, PropertyType.REFERENCE, dnaProperty);
-        assertThat(prop.getNode(), is(referencedNode));
+        assertThat(prop.getNode(), is((Node)referencedNode));
         assertThat(prop.getType(), is(PropertyType.REFERENCE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }



From dna-commits at lists.jboss.org  Wed Mar 11 10:44:39 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 11 Mar 2009 10:44:39 -0400
Subject: [dna-commits] DNA SVN: r770 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LhPfj-0007sq-Sq@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-11 10:44:39 -0400 (Wed, 11 Mar 2009)
New Revision: 770

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
Log:
DNA-194 Implement update JCR capability

Applied the "...move_value_cast" patch, which moves the JcrNodeType logic determining whether a value can be cast to another type into the JcrValue class where it can be used to actually convert the value to a value of another type.  Also made the "createValueFormatException(...)" methods private to increase likelihood they'll be inlined. All tests pass.

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-03-10 19:43:15 UTC (rev 769)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-11 14:44:39 UTC (rev 770)
@@ -29,7 +29,6 @@
 import java.util.List;
 import java.util.Set;
 import java.util.Stack;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
@@ -38,10 +37,6 @@
 import net.jcip.annotations.Immutable;
 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.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.graph.property.Path.Segment;
 
 /**
  * DNA implementation of JCR {@link NodeType}s.
@@ -329,100 +324,16 @@
         if (value == null) {
             return !property.isMandatory();
         }
-
-        return canCastValueToType(value, property.getRequiredType());
-    }
-
-    /**
-     * Internal method to validate that a value can be cast to a given JCR property type. The values are set according to the
-     * following rules:
-     * <ol>
-     * <li>If <code>property.getRequiredType()</code> is {@link PropertyType#UNDEFINED}, return <code>true</code></li>
-     * <li>Compare the type of the given value to the required type and see if they are compatible based on the rules in the JCR
-     * 1.0 spec.</li>
-     * </ol>
-     * 
-     * @param jcrPropertyType a value from the {@link PropertyType} constants to which this value MAY be able to be casted.
-     * @param value the value to set (may not be <code>null</code>)
-     * @return whether the value can be cast to the given property type
-     */
-    private boolean canCastValueToType( Value value,
-                                        int jcrPropertyType ) {
-        assert value != null;
-
-        int valueType = value.getType();
-
-        // Trivial case - no cast required
-        if (valueType == jcrPropertyType) {
-            return true;
-        }
-
+        
         try {
-            switch (jcrPropertyType) {
-                case PropertyType.BOOLEAN:
-                    if (valueType == PropertyType.STRING) {
-                        return true;
-                    }
-
-                    if (valueType == PropertyType.BINARY) {
-                        // If the binary can be converted to a UTF-8 string, it can be set onto a boolean property
-                        value.getString();
-                        return true;
-                    }
-                    return false;
-
-                case PropertyType.DATE:
-                    if (valueType == PropertyType.DOUBLE || valueType == PropertyType.LONG) {
-                        return true;
-                    }
-
-                    if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
-                        // If the binary can be converted to a date, it can be set onto a date property
-                        value.getDate();
-                        return true;
-                    }
-                    return false;
-
-                case PropertyType.NAME:
-                    ValueFactories valueFactories = session.getExecutionContext().getValueFactories();
-                    if (valueType == PropertyType.STRING || valueType == PropertyType.BINARY) {
-                        valueFactories.getNameFactory().create(value.getString());
-                        return true;
-                    }
-
-                    if (valueType == PropertyType.PATH) {
-                        Path path = valueFactories.getPathFactory().create(value.getString());
-
-                        Segment[] segments = path.getSegmentsArray();
-                        return !path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex();
-                    }
-
-                    return false;
-
-                case PropertyType.PATH:
-                    return value.getType() == PropertyType.STRING;
-
-                    // Nothing can be converted to these types (except themselves)
-                case PropertyType.REFERENCE:
-                case PropertyType.DOUBLE:
-                case PropertyType.LONG:
-                    return false;
-
-                    // Anything can be converted to these types
-                case PropertyType.BINARY:
-                case PropertyType.STRING:
-                case PropertyType.UNDEFINED:
-                    return true;
-                default:
-                    assert false : "Unexpected JCR property type " + jcrPropertyType;
-                    // This should still throw an exception even if assertions are turned off
-                    throw new IllegalStateException("Invalid property type " + jcrPropertyType);
-            }
-        } catch (RepositoryException re) {
+            assert value instanceof JcrValue : "Illegal implementation of Value interface";
+            ((JcrValue) value).asType(property.getRequiredType());
+        }
+        catch (javax.jcr.ValueFormatException vfe) {
+            // Cast failed
             return false;
-        } catch (ValueFormatException vfe) {
-            return false;
         }
+        return true;
     }
 
     /**
@@ -455,7 +366,12 @@
 
         for (int i = 0; i < values.length; i++) {
             if (values[i] != null) {
-                if (!canCastValueToType(values[i], property.getRequiredType())) {
+                try {
+                    assert values[i] instanceof JcrValue : "Illegal implementation of Value interface";
+                    ((JcrValue) values[i]).asType(property.getRequiredType());
+                }
+                catch (javax.jcr.ValueFormatException vfe) {
+                    // Cast failed
                     return false;
                 }
             }

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-03-10 19:43:15 UTC (rev 769)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-11 14:44:39 UTC (rev 770)
@@ -25,13 +25,17 @@
 
 import java.io.InputStream;
 import java.util.Calendar;
+import javax.jcr.Node;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.Path.Segment;
 
 /**
  * @author jverhaeg
@@ -58,10 +62,14 @@
 
     private State state = State.NEVER_CONSUMED;
 
-    ValueFormatException createValueFormatException( Class<?> type ) {
+    private ValueFormatException createValueFormatException( Class<?> type ) {
         return new ValueFormatException(JcrI18n.cannotConvertValue.text(value.getClass().getSimpleName(), type.getSimpleName()));
     }
 
+    private ValueFormatException createValueFormatException( org.jboss.dna.graph.property.ValueFormatException vfe ) {
+        return new ValueFormatException(vfe);
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -177,6 +185,112 @@
         return type;
     }
 
+    /**
+     * Returns a copy of the current {@link JcrValue} cast to the JCR type specified by the <code>type</code> argument. If the
+     * value cannot be converted base don the JCR type conversion rules, a {@link ValueFormatException} will be thrown.
+     * 
+     * @param type the JCR type from {@link PropertyType} that the new {@link JcrValue} should have.
+     * @return a new {@link JcrValue} with the given JCR type and an equivalent value.
+     * @throws ValueFormatException if the value contained by this {@link JcrValue} cannot be converted to the desired type.
+     * @see PropertyType
+     */
+    JcrValue asType( int type ) throws ValueFormatException {
+
+        if (type == this.type) {
+            return new JcrValue(this.valueFactories, this.type, this.value);
+        }
+
+        switch (type) {
+            case PropertyType.BOOLEAN:
+                try {
+                    if (this.type == PropertyType.STRING || this.type == PropertyType.BINARY) {
+                        return new JcrValue(this.valueFactories, type, valueFactories.getBooleanFactory().create(value));
+                    }
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+                throw createValueFormatException(boolean.class);
+
+            case PropertyType.DATE:
+                try {
+                    if (this.type == PropertyType.DOUBLE || this.type == PropertyType.LONG || this.type == PropertyType.STRING
+                        || this.type == PropertyType.BINARY) {
+                        return new JcrValue(this.valueFactories, type, valueFactories.getDateFactory().create(value));
+                    }
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+
+                throw createValueFormatException(Calendar.class);
+
+            case PropertyType.NAME:
+                try {
+                    if (this.type == PropertyType.STRING) {
+                        return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(value));
+                    }
+
+                    String valueAsString = this.valueFactories.getStringFactory().create(value);
+                    if (this.type == PropertyType.BINARY) {
+                        return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(valueAsString));
+
+                    }
+
+                    if (this.type == PropertyType.PATH) {
+                        Path path = valueFactories.getPathFactory().create(valueAsString);
+
+                        Segment[] segments = path.getSegmentsArray();
+                        if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
+                            return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory()
+                                                                                              .create(valueAsString));
+
+                        }
+                    }
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+
+                throw createValueFormatException(Name.class);
+
+            case PropertyType.PATH:
+                try {
+                    if (this.type == PropertyType.STRING) {
+                        return new JcrValue(this.valueFactories, type, this.valueFactories.getPathFactory().create(value));
+                    }
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+                throw createValueFormatException(Path.class);
+
+                // Nothing can be converted to these types (except themselves)
+            case PropertyType.REFERENCE:
+                throw createValueFormatException(Node.class);
+            case PropertyType.DOUBLE:
+                throw createValueFormatException(double.class);
+            case PropertyType.LONG:
+                throw createValueFormatException(long.class);
+
+                // Anything can be converted to these types
+            case PropertyType.BINARY:
+                try {
+                    return new JcrValue(this.valueFactories, type, valueFactories.getBinaryFactory().create(value));
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+            case PropertyType.STRING:
+                try {
+                    return new JcrValue(this.valueFactories, type, valueFactories.getStringFactory().create(value));
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
+            case PropertyType.UNDEFINED:
+                return new JcrValue(this.valueFactories, this.type, this.value);
+            default:
+                assert false : "Unexpected JCR property type " + type;
+                // This should still throw an exception even if assertions are turned off
+                throw new IllegalStateException("Invalid property type " + type);
+        }
+    }
+
     void nonInputStreamConsumed() {
         if (state == State.INPUT_STREAM_CONSUMED) {
             throw new IllegalStateException(JcrI18n.inputStreamConsumed.text());



From dna-commits at lists.jboss.org  Wed Mar 11 15:13:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 11 Mar 2009 15:13:08 -0400
Subject: [dna-commits] DNA SVN: r771 - in trunk/dna-jcr/src: test/resources
	and 1 other directory.
Message-ID: <E1LhTrY-0007o7-5z@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-11 15:13:08 -0400 (Wed, 11 Mar 2009)
New Revision: 771

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/JcrSession.java
   trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
Log:
DNA-296 JcrSession Sometimes Eschews Single-Valued Variants of PropertyDefinitions

Applied the patch that corrects the assignment of the PropertyDefinition to each Property instance.

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-03-11 14:44:39 UTC (rev 770)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-11 19:13:08 UTC (rev 771)
@@ -136,49 +136,10 @@
 
                                            }), NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        /* Name of node type that holds xmltext from document view import (see JCR 1.0 spec section 7.3.2) */
-        JcrNodeType xmlText = new JcrNodeType(
-                                              session,
-                                              DnaLexicon.XML_TEXT,
-                                              Arrays.asList(new NodeType[] {base}),
-                                              NO_PRIMARY_ITEM_NAME,
-                                              NO_CHILD_NODES,
-                                              Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                   session,
-                                                                                                                   null,
-                                                                                                                   JcrLexicon.XMLCHARACTERS,
-                                                                                                                   OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                                                                   true,
-                                                                                                                   true,
-                                                                                                                   true,
-                                                                                                                   NO_DEFAULT_VALUES,
-                                                                                                                   PropertyType.STRING,
-                                                                                                                   NO_CONSTRAINTS,
-                                                                                                                   false)}),
-                                              NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+ 
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace, }));
+        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] { }));
 
-        /* Mixin type that indicates the node contains an xmltext node which holds xmltext from document view import (see JCR 1.0 spec section 7.3.2) */
-        JcrNodeType xmlContent = new JcrNodeType(
-                                                 session,
-                                                 DnaLexicon.XML_CONTENT,
-                                                 Arrays.asList(new NodeType[] {base}),
-                                                 NO_PRIMARY_ITEM_NAME,
-                                                 Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                              session,
-                                                                                                              null,
-                                                                                                              JcrLexicon.XMLTEXT,
-                                                                                                              OnParentVersionBehavior.VERSION.getJcrValue(),
-                                                                                                              false,
-                                                                                                              true,
-                                                                                                              false,
-                                                                                                              false,
-                                                                                                              DnaLexicon.XML_TEXT,
-                                                                                                              new NodeType[] {xmlText})}),
-                                                 NO_PROPERTIES, IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
-
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace, xmlText,}));
-        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {xmlContent}));
-
     }
 
     /**

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-11 14:44:39 UTC (rev 770)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-03-11 19:13:08 UTC (rev 771)
@@ -38,17 +38,5 @@
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
     public static final Name URI = new BasicName(Namespace.URI, "uri");
 
-    /**
-     * Mixin type that indicates the node contains an xmltext node which holds xmltext from document view import (see JCR 1.0
-     * specification section 7.3.2). This node has a child node named {@link JcrLexicon#XMLTEXT} of type
-     * {@link DnaLexicon#XML_TEXT}.
-     */
-    public static final Name XML_CONTENT = new BasicName(Namespace.URI, "xmlContent");
 
-    /**
-     * Name of node type that holds xmltext from document view import (see JCR 1.0 specification section 7.3.2). It is defined in
-     * the node type named {@link DnaLexicon#XML_CONTENT}.
-     */
-    public static final Name XML_TEXT = new BasicName(Namespace.URI, "xmlText");
-
 }

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-03-11 14:44:39 UTC (rev 770)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-11 19:13:08 UTC (rev 771)
@@ -862,7 +862,12 @@
         // Create JCR properties for corresponding DNA properties
         // ------------------------------------------------------
         // First get the property type for each property, based upon the primary type and mixins ...
-        Map<Name, PropertyDefinition> propertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+        // The map with single-valued properties...
+        Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+        // ... and the map with multi-valued properties
+        Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+        
+        
         boolean referenceable = false;
 
         NamespaceRegistry registry = namespaces();
@@ -881,8 +886,15 @@
                 continue;
             }
             Name name = nameFactory.create(nameString);
-            PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
-            if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+            
+            if (propertyDefn.isMultiple()) {
+                PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+            }
+            else {
+                PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+            }
         }
         // The process the mixin types ...
         org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
@@ -899,8 +911,14 @@
                         continue;
                     }
                     Name name = nameFactory.create(nameString);
-                    PropertyDefinition prev = propertyDefinitionsByPropertyName.put(name, propertyDefn);
-                    if (prev != null) propertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+                    if (propertyDefn.isMultiple()) {
+                        PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                        if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+                    }
+                    else {
+                        PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                        if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+                    }
                 }
             }
         }
@@ -908,7 +926,8 @@
         // Now create the JCR property object wrapper around the "jcr:uuid" property ...
         Map<Name, Property> properties = new HashMap<Name, Property>();
         if (referenceable) {
-            PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
+            // We know that this property is single-valued
+            PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
             properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, propertyDefinition, PropertyType.STRING,
                                                                        uuidProperty));
         }
@@ -921,25 +940,35 @@
             if (JcrLexicon.UUID.equals(name) || DnaLexicon.UUID.equals(name)) continue;
 
             // Figure out the JCR property type for this property ...
-            PropertyDefinition propertyDefinition = propertyDefinitionsByPropertyName.get(name);
+            PropertyDefinition propertyDefinition;
+            
+            if (dnaProp.isMultiple()) {
+                propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+            }
+            else {
+                propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);       
+                
+                // If the property has only one value, dnaProp.isMultiple() will return false, but the
+                // property may actually be a multi-valued property that happens to have one property set.
+                if (propertyDefinition == null) {
+                    propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+                }
+            }
 
             // If no property type was found for this property, see if there is a wildcard property ...
-            if (propertyDefinition == null && !anyPropertyDefinitions.isEmpty()) {
-                Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator();
-                propertyDefinition = iter.next(); // use the first one by default ...
-                if (iter.hasNext()) {
-                    // Look for a better one if not multiple but the property has multiple values ...
-                    if (!propertyDefinition.isMultiple() && dnaProp.isMultiple()) {
-                        while (iter.hasNext()) {
-                            PropertyDefinition next = iter.next();
-                            if (next.isMultiple()) {
-                                propertyDefinition = next;
-                                break;
-                            }
-                        }
+            if (propertyDefinition == null) {
+                for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext(); ) {
+                    PropertyDefinition nextDef = iter.next();
+                    
+                    // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
+                    if ((nextDef.isMultiple() && dnaProp.isMultiple())
+                        || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
+                        propertyDefinition = nextDef;
+                        break;
                     }
                 }
             }
+            
 
             // If there still is no property type defined ...
             if (propertyDefinition == null) {

Modified: trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-03-11 14:44:39 UTC (rev 770)
+++ trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-03-11 19:13:08 UTC (rev 771)
@@ -30,7 +30,7 @@
     jcr:primaryType="nt:unstructured">
 	<nt:unstructured jcr:name="node1" prop1="&lt;foo&amp;foo&gt;" >
 	<!--  This stanza checks for the jcr:xmltext special case for document export.  DO NOT change this element. -->
-	   <dna:xmlText jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+	   <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
 	</nt:unstructured>
 	<nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
 	<nt:unstructured jcr:name="node3" />



From dna-commits at lists.jboss.org  Thu Mar 12 12:10:03 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 12 Mar 2009 12:10:03 -0400
Subject: [dna-commits] DNA SVN: r772 -
	trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn.
Message-ID: <E1LhnTv-0001nI-Gc@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-12 12:10:03 -0400 (Thu, 12 Mar 2009)
New Revision: 772

Modified:
   trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java
Log:
DNA-298 SVNRepositoryTest Fails on Windows

Applied the patch and verified the patch does not change the behavior on OS-X.  So, I'm committing.

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-03-11 19:13:08 UTC (rev 771)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositorySource.java	2009-03-12 16:10:03 UTC (rev 772)
@@ -276,7 +276,7 @@
         if (this.svnRepository == null) {
             try {
                 svnURL = SVNURL.parseURIDecoded(getSVNURL());
-                String usedProtocol = this.getSVNURL().substring(0, this.getSVNURL().lastIndexOf(":"));
+                String usedProtocol = this.getSVNURL().substring(0, this.getSVNURL().indexOf(":"));
                 if (usedProtocol.equals(SVNProtocol.SVN.value()) || usedProtocol.equals(SVNProtocol.SVN_SSH.value())) {
                     SVNRepositoryFactoryImpl.setup();
                     this.svnRepository = SVNRepositoryFactory.create(svnURL);



From dna-commits at lists.jboss.org  Thu Mar 12 15:19:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 12 Mar 2009 15:19:34 -0400
Subject: [dna-commits] DNA SVN: r773 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/property/basic and 1 other directory.
Message-ID: <E1LhqRK-00025P-Nn@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-12 15:19:34 -0400 (Thu, 12 Mar 2009)
New Revision: 773

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/ChildPathTest.java
Log:
DNA-299 Path.isNormalized() is incorrect if the path begins with parent reference(s)

Added the tests to verify it was failing, then corrected the logic in all Path implementations (although RootPath was correct, since it always considered itself normalized).

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java	2009-03-12 16:10:03 UTC (rev 772)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/AbstractPath.java	2009-03-12 19:19:34 UTC (rev 773)
@@ -126,8 +126,16 @@
     private transient int hc = 0;
 
     protected boolean isNormalized( List<Segment> segments ) {
+        boolean nonParentReference = false;
+        boolean first = isAbsolute(); // only care about first one when it's absolute
         for (Segment segment : segments) {
-            if (segment.isSelfReference() || segment.isParentReference()) return false;
+            if (segment.isSelfReference()) return false;
+            if (segment.isParentReference()) {
+                if (nonParentReference || first) return false;
+            } else {
+                nonParentReference = true;
+            }
+            first = false;
         }
         return true;
     }

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-03-12 16:10:03 UTC (rev 772)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/ChildPath.java	2009-03-12 19:19:34 UTC (rev 773)
@@ -187,8 +187,15 @@
      * @see org.jboss.dna.graph.property.Path#isNormalized()
      */
     public boolean isNormalized() {
-        if (child.isParentReference() || child.isSelfReference()) return false;
-        return parent.isNormalized();
+        if (child.isSelfReference()) return false;
+        if (!parent.isNormalized()) return false;
+        // Otherwise, the parent is normalized, so this child will be normalized if this child is not a parent reference ...
+        if (!child.isParentReference()) return true;
+        // The path ends with a parent reference. It is normalized only if all other path segments are parent references ...
+        for (Path.Segment segment : parent) {
+            if (!segment.isParentReference()) return false;
+        }
+        return true;
     }
 
     /**

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java	2009-03-12 16:10:03 UTC (rev 772)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicPathTest.java	2009-03-12 19:19:34 UTC (rev 773)
@@ -102,7 +102,7 @@
     }
 
     @Test
-    public void shouldCreateAbsolutePathWithParentSegment() {
+    public void shouldConsiderAsNotNormalizedAnAbsolutePathWithParentSegmentAtEnd() {
         validSegmentsList.add(Path.PARENT_SEGMENT);
         path = new BasicPath(validSegmentsList, true);
         assertThat(path.isAbsolute(), is(true));
@@ -112,7 +112,7 @@
     }
 
     @Test
-    public void shouldCreateRelativePathWithParentSegment() {
+    public void shouldConsiderAsNotNormalizedARelativePathWithParentSegmentAtEnd() {
         validSegmentsList.add(Path.PARENT_SEGMENT);
         path = new BasicPath(validSegmentsList, false);
         assertThat(path.isAbsolute(), is(false));
@@ -122,6 +122,82 @@
     }
 
     @Test
+    public void shouldConsiderAsNotNormalizedAnAbsolutePathWithParentSegmentAtFront() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        segments.add(Path.PARENT_SEGMENT);
+        segments.addAll(validSegmentsList);
+        path = new BasicPath(segments, true);
+        assertThat(path.isAbsolute(), is(true));
+        assertThat(path.isNormalized(), is(false));
+        assertThat(path.getSegmentsList(), is(segments));
+        assertThat(path.size(), is(segments.size()));
+    }
+
+    @Test
+    public void shouldConsiderAsNormalizedARelativePathWithParentSegmentAtFront() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        segments.add(Path.PARENT_SEGMENT);
+        segments.addAll(validSegmentsList);
+        path = new BasicPath(segments, false);
+        assertThat(path.isAbsolute(), is(false));
+        assertThat(path.isNormalized(), is(true));
+        assertThat(path.getSegmentsList(), is(segments));
+        assertThat(path.size(), is(segments.size()));
+    }
+
+    @Test
+    public void shouldConsiderAsNotNormalizedAnAbsolutePathWithAllParentReferences() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        for (int i = 0; i != 10; ++i) {
+            segments.add(Path.PARENT_SEGMENT);
+        }
+        path = new BasicPath(segments, true);
+        assertThat(path.isAbsolute(), is(true));
+        assertThat(path.isNormalized(), is(false));
+        assertThat(path.getSegmentsList(), is(segments));
+        assertThat(path.size(), is(segments.size()));
+    }
+
+    @Test
+    public void shouldConsiderAsNormalizedARelativePathWithAllParentReferences() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        for (int i = 0; i != 10; ++i) {
+            segments.add(Path.PARENT_SEGMENT);
+        }
+        path = new BasicPath(segments, false);
+        assertThat(path.isAbsolute(), is(false));
+        assertThat(path.isNormalized(), is(true));
+        assertThat(path.getSegmentsList(), is(segments));
+        assertThat(path.size(), is(segments.size()));
+    }
+
+    @Test
+    public void shouldConsiderAsNotNormalizedPathWithMostParentReferencesAndOneNonParentReferenceInMiddle() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        segments.add(Path.PARENT_SEGMENT);
+        segments.add(Path.PARENT_SEGMENT);
+        segments.add(pathFactory.createSegment("nonParentSegment"));
+        segments.add(Path.PARENT_SEGMENT);
+        segments.add(Path.PARENT_SEGMENT);
+        path = new BasicPath(segments, true);
+        assertThat(path.isAbsolute(), is(true));
+        assertThat(path.isNormalized(), is(false));
+        assertThat(path.getSegmentsList(), is(segments));
+        assertThat(path.size(), is(segments.size()));
+    }
+
+    @Test
+    public void shouldConsiderAsNotNormalizedAnAbsolutePathThatBeginsWithParentReference() {
+        List<Path.Segment> segments = new ArrayList<Path.Segment>();
+        segments.add(Path.PARENT_SEGMENT);
+        segments.add(pathFactory.createSegment("nonParentSegment"));
+        segments.add(pathFactory.createSegment("nonParentSegment2"));
+        path = new BasicPath(segments, true);
+        assertThat(path.isAbsolute(), is(true));
+        assertThat(path.isNormalized(), is(false));
+    }
+
+    @Test
     public void shouldCreateAbsolutePathWithSelfSegment() {
         validSegmentsList.add(Path.SELF_SEGMENT);
         path = new BasicPath(validSegmentsList, true);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/ChildPathTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/ChildPathTest.java	2009-03-12 16:10:03 UTC (rev 772)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/ChildPathTest.java	2009-03-12 19:19:34 UTC (rev 773)
@@ -35,11 +35,6 @@
 import java.util.List;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.basic.BasicName;
-import org.jboss.dna.graph.property.basic.BasicPath;
-import org.jboss.dna.graph.property.basic.BasicPathSegment;
-import org.jboss.dna.graph.property.basic.ChildPath;
-import org.jboss.dna.graph.property.basic.RootPath;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -163,6 +158,41 @@
         assertThat(path.getParent(), is(sameInstance(parent)));
     }
 
+    @Test
+    public void shouldConsiderAsNotNormalizedAPathWithParentSegmentAtEnd() {
+        path = new ChildPath(parent, Path.PARENT_SEGMENT);
+        assertThat(path.isAbsolute(), is(parent.isAbsolute()));
+        assertThat(path.isNormalized(), is(false));
+    }
+
+    @Test
+    public void shouldConsiderAsNormalizedARelativePathWithParentSegmentAtFront() {
+        parent = path("../../a/b/c/d");
+        path = new ChildPath(parent, segment("e"));
+        assertThat(path.isNormalized(), is(true));
+    }
+
+    @Test
+    public void shouldConsiderAsNormalizedAnAbsolutePathWithParentSegmentAtFront() {
+        parent = path("/../a/b");
+        path = new ChildPath(parent, segment("c"));
+        assertThat(path.isNormalized(), is(false));
+    }
+
+    @Test
+    public void shouldConsiderAsNormalizedPathWithAllParentReferences() {
+        parent = path("../../../../..");
+        path = new ChildPath(parent, Path.PARENT_SEGMENT);
+        assertThat(path.isNormalized(), is(true));
+    }
+
+    @Test
+    public void shouldConsiderAsNotNormalizedPathWithMostParentReferencesAndOneNonParentReferenceInMiddle() {
+        parent = path("../../a/b/../..");
+        path = new ChildPath(parent, Path.PARENT_SEGMENT);
+        assertThat(path.isNormalized(), is(false));
+    }
+
     // @Test
     // public void shouldReturnRootForLowestCommonAncestorWithAnyNodePath() {
     // Path other = mock(Path.class);



From dna-commits at lists.jboss.org  Thu Mar 12 16:14:20 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 12 Mar 2009 16:14:20 -0400
Subject: [dna-commits] DNA SVN: r774 - in trunk/dna-graph/src/main:
	java/org/jboss/dna/graph/property and 1 other directories.
Message-ID: <E1LhrIK-0002Ex-JT@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-12 16:14:20 -0400 (Thu, 12 Mar 2009)
New Revision: 774

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathNotFoundException.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
Log:
DNA-300 The DNA PathNotFoundException message text does not include the lowest existing path that was found

Added the lowest existing path to the message (and localized message) of the PathNotFoundException in the DNA Graph API.  Of course, this is only added when there is a lowest existing path.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-03-12 19:19:34 UTC (rev 773)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-03-12 20:14:20 UTC (rev 774)
@@ -62,6 +62,7 @@
     public static I18n pathExpressionHasInvalidMatch;
     public static I18n messageDigestNotFound;
     public static I18n unableToAccessResourceFileFromClassLoader;
+    public static I18n pathNotFoundExceptionLowestExistingLocationFound;
 
     public static I18n executingRequest;
     public static I18n executedRequest;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathNotFoundException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathNotFoundException.java	2009-03-12 19:19:34 UTC (rev 773)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/PathNotFoundException.java	2009-03-12 20:14:20 UTC (rev 774)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.graph.property;
 
+import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
 
 /**
@@ -97,6 +98,20 @@
     }
 
     /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Throwable#getMessage()
+     */
+    @Override
+    public String getMessage() {
+        if (this.lowestAncestorThatDoesExist != null) {
+            return GraphI18n.pathNotFoundExceptionLowestExistingLocationFound.text(super.getMessage(),
+                                                                                   this.lowestAncestorThatDoesExist);
+        }
+        return super.getMessage();
+    }
+
+    /**
      * Get the path that was not found
      * 
      * @return the path that was not found

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-03-12 19:19:34 UTC (rev 773)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-03-12 20:14:20 UTC (rev 774)
@@ -48,7 +48,8 @@
 pathExpressionHasInvalidSelect = Invalid select expression "{0}" in the path expression "{1}"
 pathExpressionHasInvalidMatch = Invalid match expression "{0}" in the path expression "{1}"
 messageDigestNotFound = The "{0}" message digest algorithm could not be found
-unableToAccessResourceFileFromClassLoader = Unable to access "{0}" resource from the class loader 
+unableToAccessResourceFileFromClassLoader = Unable to access "{0}" resource from the class loader
+pathNotFoundExceptionLowestExistingLocationFound = {0}; lowest existing path is {1}
 
 executingRequest = Executing {0}
 executedRequest = Executed {0}



From dna-commits at lists.jboss.org  Thu Mar 12 16:40:20 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 12 Mar 2009 16:40:20 -0400
Subject: [dna-commits] DNA SVN: r775 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic.
Message-ID: <E1LhrhU-00035B-Gf@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-12 16:40:20 -0400 (Thu, 12 Mar 2009)
New Revision: 775

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
Log:
DNA-194 Implement update JCR capability

Small optimization in the NameValueFactory that maintains a single static/shared instance for Name objects that have no namespace URI and a local name of "*".  These are use pretty prevelently in the JCR type manager to represent residual child and property definitions.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-12 20:14:20 UTC (rev 774)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-12 20:40:20 UTC (rev 775)
@@ -66,6 +66,9 @@
     private static final String PREFIXED_NAME_PATTERN_STRING = "(([^:/]*):)?(.*)";
     private static final Pattern PREFIXED_NAME_PATTERN = Pattern.compile(PREFIXED_NAME_PATTERN_STRING);
 
+    private static Name BLANK_NAME;
+    private static Name ANY_NAME;
+
     private final NamespaceRegistry namespaceRegistry;
 
     public NameValueFactory( NamespaceRegistry namespaceRegistry,
@@ -92,9 +95,15 @@
         if (decoder == null) decoder = getDecoder();
         try {
             if (value.length() == 0) {
-                return new BasicName("", "");
+                if (BLANK_NAME == null) BLANK_NAME = new BasicName("", "");
+                return BLANK_NAME;
             }
-            if (value.charAt(0) != '{') {
+            char firstChar = value.charAt(0);
+            if (value.length() == 1 && firstChar == '*') {
+                if (ANY_NAME == null) ANY_NAME = new BasicName("", "*");
+                return ANY_NAME;
+            }
+            if (firstChar != '{') {
                 // First, see whether the value fits the prefixed name pattern ...
                 Matcher matcher = PREFIXED_NAME_PATTERN.matcher(value);
                 if (matcher.matches()) {



From dna-commits at lists.jboss.org  Thu Mar 12 16:54:55 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 12 Mar 2009 16:54:55 -0400
Subject: [dna-commits] DNA SVN: r776 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1Lhrvb-0006Th-Bk@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-12 16:54:55 -0400 (Thu, 12 Mar 2009)
New Revision: 776

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
Log:
DNA-297 JcrSession.exportDocumentView Does Not Export Carriage Returns, Line Feeds, and Tabs Correctly

Applied the "redux" patch, and verified that all tests do pass.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-12 20:54:55 UTC (rev 776)
@@ -24,7 +24,11 @@
 package org.jboss.dna.jcr;
 
 import java.io.OutputStream;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.Property;
@@ -33,6 +37,8 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import net.jcip.annotations.NotThreadSafe;
+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.property.Name;
 import org.jboss.dna.graph.property.ValueFactories;
@@ -50,6 +56,8 @@
 @NotThreadSafe
 class JcrDocumentViewExporter extends AbstractJcrExporter {
 
+    private static final TextEncoder VALUE_ENCODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
+    
     JcrDocumentViewExporter( JcrSession session ) {
         super(session, Collections.<String>emptyList());
     }
@@ -111,7 +119,7 @@
                               propName.getLocalName(),
                               localPropName,
                               PropertyType.nameFromValue(prop.getType()),
-                              value.getString());
+                              VALUE_ENCODER.encode(value.getString()));
         }
 
         Name name;
@@ -211,4 +219,74 @@
         return xmlCharacters.getValue().getString();
     }
 
+    /**
+     * Special {@link TextEncoder} that implements the subset of XML name encoding suggested by section 6.4.4 of the JCR 1.0.1
+     * specification.  This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
+     * underscore characters that might otherwise suggest an encoding, as defined in {@link XmlNameEncoder}.
+     *
+     */
+    protected static class JcrDocumentViewPropertyEncoder extends XmlNameEncoder {
+        private static final Set<Character> MAPPED_CHARACTERS;
+        
+        static {
+            MAPPED_CHARACTERS = new HashSet<Character>();
+            MAPPED_CHARACTERS.add(' ');
+            MAPPED_CHARACTERS.add('\r');
+            MAPPED_CHARACTERS.add('\n');
+            MAPPED_CHARACTERS.add('\t');
+            
+        }
+        
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TextEncoder#encode(java.lang.String)
+         */
+        // See section 6.4.4 of the JCR 1.0.1 spec for why these hoops must be jumped through
+        @Override
+        public String encode( String text ) {
+            if (text == null) return null;
+            if (text.length() == 0) return text;
+            StringBuilder sb = new StringBuilder();
+            String hex = null;
+            CharacterIterator iter = new StringCharacterIterator(text);
+            for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
+                if (c == '_') {
+                    // Read the next character (if there is one) ...
+                    char next = iter.next();
+                    if (next == CharacterIterator.DONE) {
+                        sb.append(c);
+                        break;
+                    }
+                    // If the next character is not 'x', then these are just regular characters ...
+                    if (next != 'x') {
+                        sb.append(c).append(next);
+                        continue;
+                    }
+                    // The next character is 'x', so write out the '_' character in encoded form ...
+                    sb.append("_x005f_");
+                    // And then write out the next character ...
+                    sb.append(next);
+                } else if (!MAPPED_CHARACTERS.contains(c)) {
+                    // Legal characters for an XML Name ...
+                    sb.append(c);
+                } else {
+                    // All other characters must be escaped with '_xHHHH_' where 'HHHH' is the hex string for the code point
+                    hex = Integer.toHexString(c);
+                    // The hex string excludes the leading '0's, so check the character values so we know how many to prepend
+                    if (c >= '\u0000' && c <= '\u000f') {
+                        sb.append("_x000").append(hex);
+                    } else if (c >= '\u0010' && c <= '\u00ff') {
+                        sb.append("_x00").append(hex);
+                    } else if (c >= '\u0100' && c <= '\u0fff') {
+                        sb.append("_x0").append(hex);
+                    } else {
+                        sb.append("_x").append(hex);
+                    }
+                    sb.append('_');
+                }
+            }
+            return sb.toString();
+        }
+    }
 }

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-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-12 20:54:55 UTC (rev 776)
@@ -1,5 +1,5 @@
 /*
- * JBoss DNA (http://www.jboss.org/dna)
+* 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.
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import static org.junit.Assert.assertTrue;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.IsNot.not;
@@ -31,7 +32,9 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
+import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.security.AccessControlException;
 import java.security.Principal;
 import java.util.Calendar;
@@ -67,6 +70,8 @@
  */
 public class JcrSessionTest {
 
+    private static final String MULTI_LINE_VALUE = "Line\t1\nLine 2\rLine 3\r\nLine 4";
+    
     private String workspaceName;
     private ExecutionContext context;
     private InMemoryRepositorySource source;
@@ -96,7 +101,8 @@
         graph.create("/a").and().create("/a/b").and().create("/a/b/c");
         graph.set("booleanProperty").on("/a/b").to(true);
         graph.set("stringProperty").on("/a/b/c").to("value");
-
+        graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
+        
         // Make sure the path to the namespaces exists ...
         graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
 
@@ -388,4 +394,14 @@
         assertThat(session.getNamespaceURI("sv"), is("http://www.jcp.org/jcr/sv/1.0"));
         // assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
     }
+    
+    @Test
+    public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
+        OutputStream os = new ByteArrayOutputStream();
+        
+        session.exportSystemView("/a/b/c", os, false, true);
+        
+        String fileContents = os.toString();
+        assertTrue(fileContents.contains(MULTI_LINE_VALUE)); 
+    }
 }

Modified: trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml	2009-03-12 20:54:55 UTC (rev 776)
@@ -25,14 +25,27 @@
   ~ Boston, MA  02110-1301  USA
   -->
 <testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
-            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;" >
-	<!--  This stanza checks for the jcr:xmltext special case for document export.  DO NOT change this element. -->
-	   <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
-	</nt:unstructured>
-	<nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
-	<nt:unstructured jcr:name="node3" />
-	<nt:unstructured jcr:name="node4" />
+          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;" >
+       <!--  
+           This stanza checks for the jcr:xmltext special case for export as per JCR 1.0.1 section 6.4.2.3
+         -->
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+    </nt:unstructured>
+    <nt:unstructured jcr:name="node2 has a multi-word name" multi-valued-property="bar baz blah">
+       <!--  
+           This stanza checks that consecutive jcr:xmltext elements will be merged in the export as 
+           per JCR 1.0.1 section 6.4.2.3
+         -->
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;other&quot; text!" />
+    </nt:unstructured>
+    <nt:unstructured jcr:name="node3" />
+    <!--  
+       Test of unprintable character exports.
+       Cannot test \r character here on Windows as TCK XML Parser will replace it with \n and fail the comparison 
+      -->
+    <nt:unstructured jcr:name="node4" multi-line-property="Line&#009;1&#010;Line 2"/>
 </testroot>
\ No newline at end of file



From dna-commits at lists.jboss.org  Mon Mar 16 18:50:09 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 16 Mar 2009 18:50:09 -0400
Subject: [dna-commits] DNA SVN: r777 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 2 other directories.
Message-ID: <E1LjLdJ-0000Y7-Ke@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-16 18:50:09 -0400 (Mon, 16 Mar 2009)
New Revision: 777

Added:
   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-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
   trunk/dna-jcr/src/test/resources/vehicles.xml
Removed:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.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/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.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/JcrNodeDefinition.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/JcrPropertyDefinition.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.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/JcrSystemViewExporter.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.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/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
   trunk/dna-jcr/src/test/resources/cars.xml
Log:
DNA-194 Implement update JCR capability

Applied the patch file (just recently attached to the issue) that captures the refactoring to pull the management of the session's cached and updated state out of the individual Node and Property implementations and into a new "SessionCache" class. 

This class does a couple of different things. First, it centralizes all of the information about the nodes and properties that have been loaded from the repository source, making it easier to track the cached information as well as track/manage the changes made within the session. Remember that some activities, such as "refresh" or "save" can be performed on one node but could potentially affect many other objects. By centralizing all the cached and transient state, it's much easier to manage all this information. It also makes it much easier to alter the data structure used to manage the cache (for example to make it thread-safe), should that be required at a later time. 

Second, it manages the Node and Property implementation objects (which can be held onto by clients), ensuring that the same Node and Property instances are always returned for the same logical instance. These instances are managed by ReferenceMaps, allowing the garbage collector to remove entries when clients no longer hold onto the Node and Property objects (with normal references). The implementation classes of Node and Property (e.g., AbstractJcrNode and its concrete subclasses, and AbstractJcrProperty and its concrete subclasses) no longer directly hold onto the detailed node and property information being cached in SessionCache, but rather hold identifiers and dynamically look up the information when necessary. This ensures that a particular Node implementation object obtained for a node will always present the most up-to-date information about the node, even if that information is changed by the session or other objects. 

(Note that it would be very easy for each Node and Property implementation to locally cache the reference to the SessionCache's node information and/or property information, saving some repeated lookups using the SessionCache's methods, but since the SessionCache knows about the implementations it could invalidate these local references when needed. This could be done at a later time when we've proven that the current simple approach needs to be improved.)

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -26,26 +26,62 @@
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
 
 /**
  * @author jverhaeg
  */
 abstract class AbstractJcrItem implements Item {
 
-    protected final String getPath( String absolutePath,
-                                    String relativePath ) {
-        assert absolutePath != null;
-        assert absolutePath.length() > 0;
-        assert relativePath != null;
-        if (absolutePath.charAt(absolutePath.length() - 1) == '/') {
-            return absolutePath + relativePath;
-        }
-        return absolutePath + '/' + relativePath;
+    protected final SessionCache cache;
+
+    protected AbstractJcrItem( SessionCache cache ) {
+        assert cache != null;
+        this.cache = cache;
     }
 
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Item#getSession()
+     */
+    public Session getSession() {
+        return cache.session();
+    }
+
+    final JcrSession session() {
+        return cache.session();
+    }
+
+    final ExecutionContext context() {
+        return cache.context();
+    }
+
+    final Name nameFrom( String name ) {
+        return context().getValueFactories().getNameFactory().create(name);
+    }
+
+    final Path pathFrom( String path ) {
+        return context().getValueFactories().getPathFactory().create(path);
+    }
+
+    final Path.Segment segmentFrom( String segment ) {
+        return context().getValueFactories().getPathFactory().createSegment(segment);
+    }
+
+    final NamespaceRegistry namespaces() {
+        return context().getNamespaceRegistry();
+    }
+
+    abstract Path path() throws RepositoryException;
+
+    /**
+     * {@inheritDoc}
+     * 
      * @return <code>false</code>
      * @see javax.jcr.Item#isModified()
      */
@@ -96,7 +132,7 @@
      * 
      * @see javax.jcr.Item#getAncestor(int)
      */
-    public final Item getAncestor( int depth ) throws RepositoryException {
+    public Item getAncestor( int depth ) throws RepositoryException {
         if (depth < 0) {
             throw new ItemNotFoundException(JcrI18n.noNegativeDepth.text(depth));
         }
@@ -127,7 +163,7 @@
      * @see javax.jcr.Item#getDepth()
      */
     public int getDepth() throws RepositoryException {
-        return getParent().getDepth() + 1;
+        return path().size();
     }
 
     /**

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -25,11 +25,10 @@
 
 import java.io.InputStream;
 import java.util.Calendar;
-import java.util.HashSet;
+import java.util.Collection;
+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.regex.Pattern;
 import javax.jcr.Item;
@@ -42,7 +41,6 @@
 import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.lock.Lock;
@@ -51,93 +49,51 @@
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionHistory;
-import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.Location;
 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.ValueFormatException;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
 
 /**
- * @author jverhaeg
+ * An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
+ * {@link SessionCache}. Each instance references the {@link SessionCache.NodeInfo node information} also managed by the
+ * SessionCache, and finds and operates against this information with each method call.
  */
- at NotThreadSafe
+ at Immutable
 abstract class AbstractJcrNode extends AbstractJcrItem implements Node {
 
     private static final NodeType[] EMPTY_NODE_TYPES = new NodeType[] {};
 
-    private final JcrSession session;
-    private final NodeDefinition definition;
-    protected Location location;
-    private Map<Name, Property> properties;
-    private List<Location> children;
+    protected final UUID nodeUuid;
 
-    AbstractJcrNode( JcrSession session,
-                     Location location,
-                     NodeDefinition definition ) {
-        assert session != null;
-        assert definition != null;
-        assert location != null;
-        this.location = location;
-        this.session = session;
-        this.definition = definition;
+    AbstractJcrNode( SessionCache cache,
+                     UUID nodeUuid ) {
+        super(cache);
+        this.nodeUuid = nodeUuid;
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @see javax.jcr.Item#getSession()
-     */
-    public Session getSession() {
-        return session;
-    }
+    abstract boolean isRoot();
 
-    final JcrSession session() {
-        return session;
-    }
-
     final UUID internalUuid() {
-        return location.getUuid();
+        return nodeUuid;
     }
 
-    /**
-     * Change the internal UUID. Technically, it is possible for this to change, but only because a new node may be assigned a
-     * UUID when it is created within the session and (according to the spec) this UUID can change when the new node is persisted
-     * upon {@link #save()}.
-     * 
-     * @param uuid the new UUID; may not be null
-     */
-    final void setInternalUuid( UUID uuid ) {
-        assert uuid != null;
-        location = location.with(uuid);
+    final NodeInfo nodeInfo() throws ItemNotFoundException, RepositoryException {
+        return cache.findNodeInfo(nodeUuid);
     }
 
-    final void setChildren( List<Location> children ) {
-        assert children != null;
-        this.children = children;
+    @Override
+    Path path() throws RepositoryException {
+        return cache.getPathFor(nodeInfo());
     }
 
-    final void setProperties( Map<Name, Property> properties ) {
-        assert properties != null;
-        this.properties = properties;
-    }
-
     /**
-     * Utility mehtod to create a {@link Name} from the supplied string.
-     * 
-     * @param name the string
-     * @return the name, or null if the supplied string is null
-     */
-    final Name nameFrom( String name ) {
-        return session.getExecutionContext().getValueFactories().getNameFactory().create(name);
-    }
-
-    final NamespaceRegistry namespaces() {
-        return session.getExecutionContext().getNamespaceRegistry();
-    }
-
-    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Node#getUUID()
@@ -148,7 +104,7 @@
         if (mixinsProp != null) {
             String referenceableMixinName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
             for (Value value : mixinsProp.getValues()) {
-                if (referenceableMixinName.equals(value.getString())) return getProperty(JcrLexicon.UUID).getString();
+                if (referenceableMixinName.equals(value.getString())) return nodeUuid.toString();
             }
         }
         throw new UnsupportedRepositoryOperationException();
@@ -192,8 +148,9 @@
      * 
      * @see javax.jcr.Node#getDefinition()
      */
-    public NodeDefinition getDefinition() {
-        return definition;
+    public NodeDefinition getDefinition() throws RepositoryException {
+        NodeDefinitionId definitionId = nodeInfo().getDefinitionId();
+        return session().nodeTypeManager().getNodeDefinition(definitionId);
     }
 
     /**
@@ -203,8 +160,8 @@
      * @see javax.jcr.Node#getPrimaryNodeType()
      */
     public NodeType getPrimaryNodeType() throws RepositoryException {
-        String nodeTypeName = getProperty(JcrLexicon.PRIMARY_TYPE).getString();
-        return session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
+        Name primaryTypeName = nodeInfo().getPrimaryTypeName();
+        return session().nodeTypeManager().getNodeType(primaryTypeName);
     }
 
     /**
@@ -214,7 +171,7 @@
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
     public NodeType[] getMixinNodeTypes() throws RepositoryException {
-        NodeTypeManager nodeTypeManager = session.getWorkspace().getNodeTypeManager();
+        NodeTypeManager nodeTypeManager = session().getWorkspace().getNodeTypeManager();
         Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
         if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
         List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();
@@ -252,12 +209,35 @@
      * @see javax.jcr.Node#getPrimaryItem()
      */
     public final Item getPrimaryItem() throws RepositoryException {
-        // TODO: Check if declared in the node type first
+        // Get the primary item name from this node's type ...
+        NodeType primaryType = getPrimaryNodeType();
+        String primaryItemNameString = primaryType.getPrimaryItemName();
+        if (primaryItemNameString == null) {
+            I18n msg = JcrI18n.noPrimaryItemNameDefinedOnPrimaryType;
+            throw new ItemNotFoundException(msg.text(primaryType.getName(), getPath(), cache.workspaceName()));
+        }
         try {
-            Property primaryItemProp = getProperty("jcr:primaryItemName");
-            return session.getItem(getPath(getPath(), primaryItemProp.getString()));
+            Path primaryItemPath = context().getValueFactories().getPathFactory().create(primaryItemNameString);
+            if (primaryItemPath.size() != 1 || primaryItemPath.isAbsolute()) {
+                I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
+                throw new ItemNotFoundException(msg.text(primaryType.getName(),
+                                                         primaryItemNameString,
+                                                         getPath(),
+                                                         cache.workspaceName()));
+            }
+            return cache.findJcrItem(nodeUuid, primaryItemPath);
+        } catch (ValueFormatException error) {
+            I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
+            throw new ItemNotFoundException(msg.text(primaryType.getName(),
+                                                     primaryItemNameString,
+                                                     getPath(),
+                                                     cache.workspaceName()));
         } catch (PathNotFoundException error) {
-            throw new ItemNotFoundException(error);
+            I18n msg = JcrI18n.primaryItemDoesNotExist;
+            throw new ItemNotFoundException(msg.text(primaryType.getName(),
+                                                     primaryItemNameString,
+                                                     getPath(),
+                                                     cache.workspaceName()));
         }
     }
 
@@ -268,7 +248,7 @@
      * @see javax.jcr.Item#isSame(javax.jcr.Item)
      */
     @Override
-    public final boolean isSame( Item otherItem ) throws RepositoryException {
+    public boolean isSame( Item otherItem ) throws RepositoryException {
         CheckArg.isNotNull(otherItem, "otherItem");
         if (super.isSame(otherItem) && otherItem instanceof Node) {
             if (otherItem instanceof AbstractJcrNode) {
@@ -285,9 +265,8 @@
      * 
      * @see javax.jcr.Node#hasProperties()
      */
-    public final boolean hasProperties() {
-        assert properties != null;
-        return !properties.isEmpty();
+    public final boolean hasProperties() throws RepositoryException {
+        return nodeInfo().getProperties().size() > 0;
     }
 
     /**
@@ -299,10 +278,17 @@
     public final boolean hasProperty( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
         if (relativePath.indexOf('/') >= 0) {
-            return (getProperty(relativePath) != null);
+            try {
+                getProperty(relativePath);
+                return true;
+            } catch (PathNotFoundException e) {
+                return false;
+            }
         }
-        assert properties != null;
-        return properties.containsKey(nameFrom(relativePath));
+        if (relativePath.equals(".")) return false;
+        if (relativePath.equals("..")) return false;
+        // Otherwise it should be a property on this node ...
+        return cache.findPropertyInfo(new PropertyId(nodeUuid, nameFrom(relativePath))) != null;
     }
 
     /**
@@ -310,8 +296,8 @@
      * 
      * @see javax.jcr.Node#getProperties()
      */
-    public final PropertyIterator getProperties() {
-        return new JcrPropertyIterator(properties.values());
+    public final PropertyIterator getProperties() throws RepositoryException {
+        return new JcrPropertyIterator(cache.findJcrPropertiesFor(nodeUuid));
     }
 
     /**
@@ -323,40 +309,67 @@
         CheckArg.isNotNull(namePattern, "namePattern");
         namePattern = namePattern.trim();
         if (namePattern.length() == 0) return new JcrEmptyPropertyIterator();
-        if ("*".equals(namePattern)) return new JcrPropertyIterator(properties.values());
+        Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeUuid);
+        if ("*".equals(namePattern)) return new JcrPropertyIterator(properties);
+
+        // Figure out the patterns for each of the different disjunctions in the supplied pattern ...
         List<Object> patterns = createPatternsFor(namePattern);
 
-        // Implementing exact-matching only for now to prototype types as properties
-        Set<Property> matchingProps = new HashSet<Property>();
-        boolean foundMatch = false;
-        for (Property property : properties.values()) {
+        // Go through the properties and remove any property that doesn't match a pattern ...
+        boolean foundMatch = true;
+        Collection<AbstractJcrProperty> matchingProperties = new LinkedList<AbstractJcrProperty>();
+        Iterator<AbstractJcrProperty> iter = properties.iterator();
+        while (iter.hasNext()) {
+            AbstractJcrProperty property = iter.next();
             String propName = property.getName();
+            assert foundMatch == true;
             for (Object patternOrMatch : patterns) {
                 if (patternOrMatch instanceof Pattern) {
                     Pattern pattern = (Pattern)patternOrMatch;
-                    if (pattern.matcher(propName).matches()) foundMatch = true;
+                    if (pattern.matcher(propName).matches()) break;
                 } else {
                     String match = (String)patternOrMatch;
-                    if (propName.equals(match)) foundMatch = true;
+                    if (propName.equals(match)) break;
                 }
-                if (foundMatch) {
-                    foundMatch = false;
-                    matchingProps.add(property);
-                    break;
-                }
+                // No pattern matched ...
+                foundMatch = false;
             }
+            if (foundMatch) {
+                matchingProperties.add(property);
+                foundMatch = true; // for the next iteration ..
+            }
         }
-        return new JcrPropertyIterator(matchingProps);
+        return new JcrPropertyIterator(matchingProperties);
     }
 
     /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Node#getReferences()
+     */
+    public final PropertyIterator getReferences() throws RepositoryException {
+        // Iterate through the properties to see which ones have a REFERENCE type ...
+        Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeUuid);
+        Collection<AbstractJcrProperty> references = new LinkedList<AbstractJcrProperty>();
+        Iterator<AbstractJcrProperty> iter = properties.iterator();
+        while (iter.hasNext()) {
+            AbstractJcrProperty property = iter.next();
+            if (property.getType() == PropertyType.REFERENCE) references.add(property);
+        }
+        if (references.isEmpty()) return new JcrEmptyPropertyIterator();
+        return new JcrPropertyIterator(references);
+    }
+
+    /**
      * A non-standard method to obtain a property given the {@link Name DNA Name} object. This method is faster
      * 
      * @param propertyName the property name
      * @return the JCR property with the supplied name, or null if the property doesn't exist
+     * @throws RepositoryException if there is an error finding the property with the supplied name
      */
-    public final Property getProperty( Name propertyName ) {
-        return properties.get(propertyName);
+    public final Property getProperty( Name propertyName ) throws RepositoryException {
+        return cache.findJcrProperty(new PropertyId(nodeUuid, propertyName));
     }
 
     /**
@@ -367,19 +380,23 @@
      */
     public final Property getProperty( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
-        if (relativePath.indexOf('/') >= 0) {
-            Item item = session.getItem(getPath(getPath(), relativePath));
+        int indexOfFirstSlash = relativePath.indexOf('/');
+        if (indexOfFirstSlash == 0) {
+            // Not a relative path ...
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+        }
+        if (indexOfFirstSlash != -1) {
+            // We know it's a relative path with more than one segment ...
+            Path path = pathFrom(relativePath).getNormalizedPath();
+            AbstractJcrItem item = cache.findJcrItem(nodeUuid, path);
             if (item instanceof Property) {
                 return (Property)item;
             }
-            // The item must be a node.
-            assert item instanceof Node;
-            // Since session.getItem() gives precedence to nodes over properties, try explicitly looking for the property with the
-            // same name as the found node using the returned node's parent.
-            return ((Node)item).getParent().getProperty(item.getName());
+            I18n msg = JcrI18n.propertyNotFoundAtPathRelativeToReferenceNode;
+            throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
         }
-        assert properties != null;
-        Property property = properties.get(nameFrom(relativePath));
+        // It's just a name, so look for it directly ...
+        Property property = getProperty(nameFrom(relativePath));
         if (property != null) return property;
         throw new PathNotFoundException();
     }
@@ -387,45 +404,31 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Node#getReferences()
-     */
-    public final PropertyIterator getReferences() throws RepositoryException {
-        // Iterate through the properties to see which ones have a REFERENCE type ...
-        Set<Property> references = new HashSet<Property>();
-        for (Property property : properties.values()) {
-            if (property.getType() == PropertyType.REFERENCE) references.add(property);
-        }
-        if (references.isEmpty()) return new JcrEmptyPropertyIterator();
-        return new JcrPropertyIterator(references);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
      * @see javax.jcr.Node#hasNode(java.lang.String)
      */
     public final boolean hasNode( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
-        if (relativePath.indexOf('/') >= 0) {
-            return (getNode(relativePath) != null);
+        if (relativePath.equals(".")) return true;
+        if (relativePath.equals("..")) return isRoot() ? false : true;
+        int indexOfFirstSlash = relativePath.indexOf('/');
+        if (indexOfFirstSlash == 0) {
+            // Not a relative path ...
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
         }
-        if (children != null) {
+        if (indexOfFirstSlash != -1) {
+            Path path = pathFrom(relativePath).getNormalizedPath();
             try {
-                Path.Segment segment = session.getExecutionContext()
-                                              .getValueFactories()
-                                              .getPathFactory()
-                                              .createSegment(relativePath);
-                for (Location child : children) {
-                    Path.Segment childSegment = child.getPath().getLastSegment();
-                    if (childSegment.equals(segment)) return true;
-                }
-            } catch (ValueFormatException e) {
-                throw new RepositoryException(JcrI18n.invalidRelativePath.text(relativePath));
+                AbstractJcrNode item = cache.findJcrNode(nodeUuid, path);
+                return item != null;
+            } catch (PathNotFoundException e) {
+                return false;
             }
         }
-        return false;
+        // It's just a name, so look for a child ...
+        Path.Segment segment = segmentFrom(relativePath);
+        ChildNode child = nodeInfo().getChildren().getChild(segment);
+        return child != null;
     }
 
     /**
@@ -433,8 +436,8 @@
      * 
      * @see javax.jcr.Node#hasNodes()
      */
-    public final boolean hasNodes() {
-        return (children != null && !children.isEmpty());
+    public final boolean hasNodes() throws RepositoryException {
+        return nodeInfo().getChildren().size() > 0;
     }
 
     /**
@@ -445,12 +448,31 @@
      */
     public final Node getNode( String relativePath ) throws RepositoryException {
         CheckArg.isNotEmpty(relativePath, "relativePath");
-        String path = getPath(getPath(), relativePath);
-        Item item = getSession().getItem(path);
-        if (item instanceof Node) {
-            return (Node)item;
+        if (relativePath.equals(".")) return this;
+        if (relativePath.equals("..")) return this.getParent();
+        int indexOfFirstSlash = relativePath.indexOf('/');
+        if (indexOfFirstSlash == 0) {
+            // Not a relative path ...
+            throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
         }
-        throw new PathNotFoundException();
+        if (indexOfFirstSlash != -1) {
+            // We know it's a relative path with more than one segment ...
+            Path path = pathFrom(relativePath).getNormalizedPath();
+            AbstractJcrItem item = cache.findJcrItem(nodeUuid, path);
+            if (item instanceof Node) {
+                return (Node)item;
+            }
+            I18n msg = JcrI18n.nodeNotFoundAtPathRelativeToReferenceNode;
+            throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
+        }
+        // It's just a name, so look for a child ...
+        Path.Segment segment = segmentFrom(relativePath);
+        ChildNode child = nodeInfo().getChildren().getChild(segment);
+        if (child != null) {
+            return cache.findJcrNode(child.getUuid());
+        }
+        String msg = JcrI18n.childNotFoundUnderNode.text(segment, getPath(), cache.workspaceName());
+        throw new PathNotFoundException(msg);
     }
 
     /**
@@ -458,11 +480,12 @@
      * 
      * @see javax.jcr.Node#getNodes()
      */
-    public final NodeIterator getNodes() {
-        if (children == null) {
+    public final NodeIterator getNodes() throws RepositoryException {
+        Children children = nodeInfo().getChildren();
+        if (children.size() == 0) {
             return new JcrEmptyNodeIterator();
         }
-        return new JcrChildNodeIterator(this, children);
+        return new JcrChildNodeIterator(cache, children, children.size());
     }
 
     /**
@@ -479,11 +502,12 @@
         List<Object> patterns = createPatternsFor(namePattern);
 
         // Implementing exact-matching only for now to prototype types as properties
-        List<Location> matchingChildren = new LinkedList<Location>();
+        Children children = nodeInfo().getChildren();
+        List<ChildNode> matchingChildren = new LinkedList<ChildNode>();
         NamespaceRegistry registry = namespaces();
         boolean foundMatch = false;
-        for (Location child : children) {
-            String childName = child.getPath().getLastSegment().getName().getString(registry);
+        for (ChildNode child : children) {
+            String childName = child.getName().getString(registry);
             for (Object patternOrMatch : patterns) {
                 if (patternOrMatch instanceof Pattern) {
                     Pattern pattern = (Pattern)patternOrMatch;
@@ -499,7 +523,7 @@
                 }
             }
         }
-        return new JcrChildNodeIterator(this, matchingChildren);
+        return new JcrChildNodeIterator(cache, matchingChildren, matchingChildren.size());
     }
 
     /**

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -44,9 +44,9 @@
     protected static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
     protected static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
 
-    // Indicates that the node type has no primary item name - added for readability
+    /** Indicates that the node type has no primary item name - added for readability */
     protected static final Name NO_PRIMARY_ITEM_NAME = null;
-    // Indicates that the definition should apply to all property definition or child node definitions - added for readability
+    /** Indicates that the definition should apply to all property definition or child node definitions - added for readability */
     protected static final Name ALL_NODES = null;
 
     // Indicates whether or not the node type is a mixin - added for readability

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -28,14 +28,16 @@
 import javax.jcr.Item;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
 
 /**
  * @author jverhaeg
@@ -43,22 +45,13 @@
 @NotThreadSafe
 abstract class AbstractJcrProperty extends AbstractJcrItem implements Property {
 
-    private final AbstractJcrNode node;
-    private final org.jboss.dna.graph.property.Property dnaProperty;
-    private final PropertyDefinition jcrPropertyDefinition;
-    private final int propertyType;
+    private final PropertyId propertyId;
 
-    AbstractJcrProperty( AbstractJcrNode node,
-                         PropertyDefinition definition,
-                         int propertyType,
-                         org.jboss.dna.graph.property.Property dnaProperty ) {
-        assert node != null;
-        assert dnaProperty != null;
-        assert definition != null;
-        this.node = node;
-        this.dnaProperty = dnaProperty;
-        this.jcrPropertyDefinition = definition;
-        this.propertyType = propertyType;
+    AbstractJcrProperty( SessionCache cache,
+                         PropertyId propertyId ) {
+        super(cache);
+        assert propertyId != null;
+        this.propertyId = propertyId;
     }
 
     /**
@@ -72,25 +65,34 @@
         visitor.visit(this);
     }
 
-    JcrValue createValue( Object value ) {
-        return new JcrValue(getExecutionContext().getValueFactories(), getType(), value);
+    final PropertyInfo propertyInfo() throws PathNotFoundException, RepositoryException {
+        return cache.findPropertyInfo(propertyId);
     }
 
-    final ExecutionContext getExecutionContext() {
-        return node.session().getExecutionContext();
+    final Name name() throws RepositoryException {
+        return propertyInfo().getPropertyName();
     }
 
-    final org.jboss.dna.graph.property.Property getDnaProperty() {
-        return dnaProperty;
+    final org.jboss.dna.graph.property.Property property() throws RepositoryException {
+        return propertyInfo().getProperty();
     }
 
+    JcrValue createValue( Object value ) throws RepositoryException {
+        return new JcrValue(context().getValueFactories(), propertyInfo().getPropertyType(), value);
+    }
+
+    @Override
+    Path path() throws RepositoryException {
+        return cache.getPathFor(propertyInfo());
+    }
+
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Property#getType()
      */
-    public final int getType() {
-        return propertyType;
+    public int getType() throws RepositoryException {
+        return propertyInfo().getPropertyType();
     }
 
     /**
@@ -98,8 +100,11 @@
      * 
      * @see javax.jcr.Property#getDefinition()
      */
-    public final PropertyDefinition getDefinition() {
-        return jcrPropertyDefinition;
+    public final PropertyDefinition getDefinition() throws RepositoryException {
+        PropertyInfo info = propertyInfo();
+        PropertyDefinitionId definitionId = info.getDefinitionId();
+        boolean multiValued = info.isMultiValued();
+        return cache.session().nodeTypeManager().getPropertyDefinition(definitionId, multiValued);
     }
 
     /**
@@ -111,8 +116,8 @@
      * 
      * @see javax.jcr.Item#getName()
      */
-    public final String getName() {
-        return dnaProperty.getName().getString(node.namespaces());
+    public final String getName() throws RepositoryException {
+        return propertyInfo().getPropertyName().getString(namespaces());
     }
 
     /**
@@ -120,8 +125,8 @@
      * 
      * @see javax.jcr.Item#getParent()
      */
-    public final Node getParent() {
-        return node;
+    public final Node getParent() throws RepositoryException {
+        return cache.findJcrNode(propertyId.getNodeId());
     }
 
     /**
@@ -130,21 +135,12 @@
      * @see javax.jcr.Item#getPath()
      */
     public final String getPath() throws RepositoryException {
-        return getPath(node.getPath(), getName());
+        return cache.getPathFor(propertyInfo()).getString(namespaces());
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see javax.jcr.Item#getSession()
-     */
-    public final Session getSession() {
-        return node.getSession();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
      * @return false
      * @see javax.jcr.Item#isNode()
      */

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -127,18 +127,25 @@
                                            NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
                                                new JcrNodeDefinition(session, null, JcrLexicon.SYSTEM,
                                                                      OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
-                                                                     true, false, DnaLexicon.NAMESPACES,
-                                                                     new NodeType[] {namespaces}),
-                                               new JcrNodeDefinition(session, null, null,
+                                                                     true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
+                                               new JcrNodeDefinition(session, null, ALL_NODES,
                                                                      OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
-                                                                     false, true, DnaLexicon.NAMESPACES,
-                                                                     new NodeType[] {namespaces}),
+                                                                     false, true, JcrNtLexicon.UNSTRUCTURED,
+                                                                     new NodeType[] {base}),
 
-                                           }), NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+                                           }), Arrays.asList(new JcrPropertyDefinition[] {
+                                               new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                         OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                         false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
+                                                                         NO_CONSTRAINTS, false),
+                                               new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                                         OnParentVersionBehavior.COPY.getJcrValue(), false,
+                                                                         false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
+                                                                         NO_CONSTRAINTS, true),}), NOT_MIXIN,
+                                           ORDERABLE_CHILD_NODES);
 
- 
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace, }));
-        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] { }));
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
+        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
 
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -24,34 +24,31 @@
 package org.jboss.dna.jcr;
 
 import java.util.Iterator;
-import java.util.List;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.Location;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
 
 /**
- * @author jverhaeg
  */
 @Immutable
 final class JcrChildNodeIterator implements NodeIterator {
 
-    private final AbstractJcrNode parent;
-    private final Iterator<Location> iterator;
-    private final JcrSession session;
+    private final SessionCache cache;
+    private final Iterator<ChildNode> iterator;
     private int ndx;
     private int size;
 
-    JcrChildNodeIterator( AbstractJcrNode parent,
-                          List<Location> children ) {
-        assert parent != null;
+    JcrChildNodeIterator( SessionCache cache,
+                          Iterable<ChildNode> children,
+                          int size ) {
+        assert cache != null;
         assert children != null;
-        this.parent = parent;
-        this.session = parent.session();
+        this.cache = cache;
         iterator = children.iterator();
-        size = children.size();
+        this.size = size;
     }
 
     /**
@@ -96,10 +93,10 @@
      * @see javax.jcr.NodeIterator#nextNode()
      */
     public Node nextNode() {
-        Location location = iterator.next();
+        ChildNode child = iterator.next();
         ndx++;
         try {
-            return session.getChild(parent, location);
+            return cache.findJcrNode(child.getUuid());
         } catch (RepositoryException error) {
             throw new RuntimeException(error);
         }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -57,7 +57,7 @@
 class JcrDocumentViewExporter extends AbstractJcrExporter {
 
     private static final TextEncoder VALUE_ENCODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
-    
+
     JcrDocumentViewExporter( JcrSession session ) {
         super(session, Collections.<String>emptyList());
     }
@@ -103,7 +103,7 @@
                 continue;
             }
 
-            Name propName = ((AbstractJcrProperty)prop).getDnaProperty().getName();
+            Name propName = ((AbstractJcrProperty)prop).name();
 
             String localPropName = getPrefixedName(propName);
 
@@ -141,16 +141,15 @@
         }
 
         endElement(contentHandler, name);
-    
+
     }
 
     /**
-     * Indicates whether the current node is an XML text node as per section 6.4.2.3 of the JCR 1.0 specification.
-     * XML text nodes are nodes that have the name &quot;jcr:xmltext&quot; and only one property (besides the mandatory
-     * &quot;jcr:primaryType&quot;).  The property must have a property name of &quot;jcr:xmlcharacters&quot;, a type of <code>String</code>,
-     * and does not have multiple values.<p/>
-     * In practice, this is handled in DNA by making XML text nodes have a type of &quot;dna:xmltext&quot;, which
-     * enforces these property characteristics.
+     * Indicates whether the current node is an XML text node as per section 6.4.2.3 of the JCR 1.0 specification. XML text nodes
+     * are nodes that have the name &quot;jcr:xmltext&quot; and only one property (besides the mandatory
+     * &quot;jcr:primaryType&quot;). The property must have a property name of &quot;jcr:xmlcharacters&quot;, a type of
+     * <code>String</code>, and does not have multiple values.<p/> In practice, this is handled in DNA by making XML text nodes
+     * have a type of &quot;dna:xmltext&quot;, which enforces these property characteristics.
      * 
      * @param node the node to test
      * @return whether this node is a special xml text node
@@ -190,8 +189,8 @@
     }
 
     /**
-     * Returns the XML characters for the given node. 
-     * The node must be an XML text node, as defined in {@link #isXmlTextNode(Node)}.
+     * Returns the XML characters for the given node. The node must be an XML text node, as defined in
+     * {@link #isXmlTextNode(Node)}.
      * 
      * @param node the node for which XML characters will be retrieved.
      * @return the xml characters for this node
@@ -201,7 +200,7 @@
         // ./xmltext/xmlcharacters exception (see JSR-170 Spec 6.4.2.3)
 
         assert isXmlTextNode(node);
-        
+
         Property xmlCharacters = node.getProperty(getPrefixedName(JcrLexicon.XMLCHARACTERS));
 
         assert xmlCharacters != null;
@@ -221,22 +220,21 @@
 
     /**
      * Special {@link TextEncoder} that implements the subset of XML name encoding suggested by section 6.4.4 of the JCR 1.0.1
-     * specification.  This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
+     * specification. This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
      * underscore characters that might otherwise suggest an encoding, as defined in {@link XmlNameEncoder}.
-     *
      */
     protected static class JcrDocumentViewPropertyEncoder extends XmlNameEncoder {
         private static final Set<Character> MAPPED_CHARACTERS;
-        
+
         static {
             MAPPED_CHARACTERS = new HashSet<Character>();
             MAPPED_CHARACTERS.add(' ');
             MAPPED_CHARACTERS.add('\r');
             MAPPED_CHARACTERS.add('\n');
             MAPPED_CHARACTERS.add('\t');
-            
+
         }
-        
+
         /**
          * {@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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -39,6 +39,7 @@
     public static I18n inputStreamConsumed;
     public static I18n nonInputStreamConsumed;
     public static I18n pathNotFound;
+    public static I18n pathNotFoundRelativeTo;
     public static I18n permissionDenied;
     public static I18n repositoryMustBeConfigured;
     public static I18n sourceInUse;
@@ -61,9 +62,19 @@
     public static I18n invalidRelativePath;
     public static I18n invalidPathParameter;
     public static I18n invalidNamePattern;
+    public static I18n noPrimaryItemNameDefinedOnPrimaryType;
+    public static I18n primaryItemNameForPrimaryTypeIsNotValid;
+    public static I18n primaryItemDoesNotExist;
     public static I18n itemNotFoundWithUuid;
+    public static I18n itemNotFoundAtPath;
+    public static I18n itemNotFoundAtPathRelativeToReferenceNode;
+    public static I18n propertyNotFoundAtPathRelativeToReferenceNode;
+    public static I18n nodeNotFoundAtPathRelativeToReferenceNode;
+    public static I18n childNotFoundUnderNode;
     public static I18n errorWhileFindingNodeWithUuid;
+    public static I18n errorWhileFindingNodeWithPath;
     public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
+    public static I18n missingNodeTypeForExistingNode;
 
     public static I18n typeNotFound;
     public static I18n supertypeNotFound;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -37,7 +37,7 @@
 
     protected final JcrSession session;
 
-    private final NodeType declaringNodeType;
+    protected final JcrNodeType declaringNodeType;
     protected final Name name;
     private final int onParentVersion;
     private final boolean autoCreated;
@@ -45,7 +45,7 @@
     private final boolean protectedItem;
 
     JcrItemDefinition( JcrSession session,
-                       NodeType declaringNodeType,
+                       JcrNodeType declaringNodeType,
                        Name name,
                        int onParentVersion,
                        boolean autoCreated,
@@ -54,13 +54,20 @@
         super();
         this.session = session;
         this.declaringNodeType = declaringNodeType;
-        this.name = name;
+        this.name = name != null ? name : session.getExecutionContext()
+                                                 .getValueFactories()
+                                                 .getNameFactory()
+                                                 .create(JcrNodeType.RESIDUAL_ITEM_NAME);
         this.onParentVersion = onParentVersion;
         this.autoCreated = autoCreated;
         this.mandatory = mandatory;
         this.protectedItem = protectedItem;
     }
 
+    final Name getInternalName() {
+        return name;
+    }
+
     /**
      * {@inheritDoc}
      * 

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -30,7 +30,6 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.property.Property;
 
@@ -40,11 +39,9 @@
 @NotThreadSafe
 final class JcrMultiValueProperty extends AbstractJcrProperty {
 
-    JcrMultiValueProperty( AbstractJcrNode node,
-                           PropertyDefinition definition,
-                           int propertyType,
-                           Property dnaProperty ) {
-        super(node, definition, propertyType, dnaProperty);
+    JcrMultiValueProperty( SessionCache cache,
+                           PropertyId propertyId ) {
+        super(cache, propertyId);
     }
 
     /**
@@ -102,7 +99,7 @@
      * @see javax.jcr.Property#getLengths()
      */
     public long[] getLengths() throws RepositoryException {
-        Property dnaProperty = getDnaProperty();
+        Property dnaProperty = propertyInfo().getProperty();
         long[] lengths = new long[dnaProperty.size()];
         Iterator<?> iter = dnaProperty.iterator();
         for (int ndx = 0; iter.hasNext(); ndx++) {
@@ -161,8 +158,8 @@
      * 
      * @see javax.jcr.Property#getValues()
      */
-    public Value[] getValues() {
-        Property dnaProperty = getDnaProperty();
+    public Value[] getValues() throws RepositoryException {
+        Property dnaProperty = propertyInfo().getProperty();
         Value[] values = new JcrValue[dnaProperty.size()];
         Iterator<?> iter = dnaProperty.iterator();
         for (int ndx = 0; iter.hasNext(); ndx++) {

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -27,10 +27,7 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeDefinition;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Path;
 
 /**
  * @author jverhaeg
@@ -38,19 +35,19 @@
 @NotThreadSafe
 final class JcrNode extends AbstractJcrNode {
 
-    private final UUID parentUuid;
-
-    JcrNode( JcrSession session,
-             UUID parentUuid,
-             Location location,
-             NodeDefinition nodeDefinition ) {
-        super(session, location, nodeDefinition);
-        assert parentUuid != null;
-        this.parentUuid = parentUuid;
+    JcrNode( SessionCache cache,
+             UUID nodeUuid ) {
+        super(cache, nodeUuid);
     }
 
-    final Path.Segment segment() {
-        return location.getPath().getLastSegment();
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.AbstractJcrNode#isRoot()
+     */
+    @Override
+    boolean isRoot() {
+        return false;
     }
 
     /**
@@ -58,8 +55,8 @@
      * 
      * @see javax.jcr.Node#getIndex()
      */
-    public int getIndex() {
-        return segment().getIndex();
+    public int getIndex() throws RepositoryException {
+        return cache.getSnsIndexOf(nodeUuid);
     }
 
     /**
@@ -67,8 +64,8 @@
      * 
      * @see javax.jcr.Item#getName()
      */
-    public String getName() {
-        return segment().getName().getString(((JcrSession)getSession()).getExecutionContext().getNamespaceRegistry());
+    public String getName() throws RepositoryException {
+        return cache.getNameOf(nodeUuid).getString(namespaces());
     }
 
     /**
@@ -76,12 +73,8 @@
      * 
      * @see javax.jcr.Item#getParent()
      */
-    public Node getParent() throws ItemNotFoundException {
-        Node node = session().getNode(parentUuid);
-        if (node == null) {
-            throw new ItemNotFoundException();
-        }
-        return node;
+    public Node getParent() throws ItemNotFoundException, RepositoryException {
+        return cache.findJcrNode(nodeInfo().getParent());
     }
 
     /**
@@ -90,20 +83,6 @@
      * @see javax.jcr.Item#getPath()
      */
     public String getPath() throws RepositoryException {
-        Node parent = getParent();
-        StringBuilder builder = new StringBuilder(parent.getPath());
-        assert builder.length() > 0;
-        if (builder.charAt(builder.length() - 1) != '/') {
-            builder.append('/');
-        }
-        String name = getName();
-        builder.append(name);
-        int ndx = getIndex();
-        if (ndx > 1) {
-            builder.append('[');
-            builder.append(ndx);
-            builder.append(']');
-        }
-        return builder.toString();
+        return cache.getPathFor(nodeUuid).getString(namespaces());
     }
 }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -47,8 +47,11 @@
     /** @see NodeDefinition#getRequiredPrimaryTypes() */
     private final NodeType[] requiredPrimaryTypes;
 
+    /** A durable identifier for this node definition. */
+    private NodeDefinitionId id;
+
     JcrNodeDefinition( JcrSession session,
-                       NodeType declaringNodeType,
+                       JcrNodeType declaringNodeType,
                        Name name,
                        int onParentVersion,
                        boolean autoCreated,
@@ -64,6 +67,18 @@
     }
 
     /**
+     * Get the durable identifier for this node definition.
+     * 
+     * @return the node definition ID; never null
+     */
+    public NodeDefinitionId getId() {
+        if (id == null) {
+            id = new NodeDefinitionId(declaringNodeType.getInternalName(), name);
+        }
+        return id;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings()
@@ -117,7 +132,7 @@
      * @return a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
      *         <code>declaringNodeType</code>.
      */
-    JcrNodeDefinition with( NodeType declaringNodeType ) {
+    JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
         return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), isAutoCreated(), isMandatory(),
                                      isProtected(), allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
     }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -102,43 +102,113 @@
      * 
      * @param propertyName the name of the property for which the definition should be retrieved. Use
      *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
+     * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
+     *        is preferred
      * @return the property definition for the given name or <code>null</code> if no such definition exists.
      * @see JcrNodeType#RESIDUAL_ITEM_NAME
      */
-    JcrPropertyDefinition getPropertyDefinition( String propertyName ) {
+    JcrPropertyDefinition getPropertyDefinition( String propertyName,
+                                                 boolean preferMultiValued ) {
+        JcrPropertyDefinition result = null;
         for (JcrPropertyDefinition property : propertyDefinitions) {
             if (propertyName.equals(property.getName())) {
-                return property;
+                result = property;
+                if (property.isMultiple() == preferMultiValued) return result;
+                // Otherwise, keep looking for a better match ...
             }
         }
 
         for (NodeType nodeType : declaredSupertypes) {
-            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName);
+            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
+            if (definition != null) {
+                if (definition.isMultiple() == preferMultiValued) return definition;
+                if (result == null) result = definition;
+            }
+        }
+        return result; // may be null
+    }
+
+    /**
+     * Returns the property definition with the given name. This method first checks the property definitions declared within this
+     * type to see if any property definitions have the given name. If no matches are found, this method initiates a recursive
+     * depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the supertypes of
+     * the supertypes).
+     * 
+     * @param propertyName the name of the property for which the definition should be retrieved. Use
+     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
+     * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
+     *        is preferred
+     * @return the property definition for the given name or <code>null</code> if no such definition exists.
+     * @see JcrNodeType#RESIDUAL_ITEM_NAME
+     */
+    JcrPropertyDefinition getPropertyDefinition( Name propertyName,
+                                                 boolean preferMultiValued ) {
+        JcrPropertyDefinition result = null;
+        for (JcrPropertyDefinition property : propertyDefinitions) {
+            if (propertyName.equals(property.getInternalName())) {
+                result = property;
+                if (property.isMultiple() == preferMultiValued) return result;
+                // Otherwise, keep looking for a better match ...
+            }
+        }
+
+        for (NodeType nodeType : declaredSupertypes) {
+            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
+            if (definition != null) {
+                if (definition.isMultiple() == preferMultiValued) return definition;
+                if (result == null) result = definition;
+            }
+        }
+        return result; // may be null
+    }
+
+    /**
+     * Returns the child node definition with the given name. This method first checks the child node definitions declared within
+     * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
+     * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
+     * supertypes of the supertypes).
+     * 
+     * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
+     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
+     * @return the child node definition with the given name or <code>null</code> if no such definition exists.
+     * @see JcrNodeType#RESIDUAL_ITEM_NAME
+     * @see #getChildNodeDefinition(Name)
+     */
+    JcrNodeDefinition getChildNodeDefinition( String childDefinitionName ) {
+        for (JcrNodeDefinition childNode : childNodeDefinitions) {
+            if (childDefinitionName.equals(childNode.getName())) {
+                return childNode;
+            }
+        }
+
+        for (NodeType nodeType : declaredSupertypes) {
+            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
             if (definition != null) return definition;
         }
         return null;
     }
 
     /**
-     * Returns the node definition for the child node with the given name. This method first checks the child node definitions
-     * declared within this type to see if any child node definitions have the given name. If no matches are found, this method
-     * initiates a recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or
-     * one the supertypes of the supertypes).
+     * Returns the child node definition with the given name. This method first checks the child node definitions declared within
+     * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
+     * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
+     * supertypes of the supertypes).
      * 
-     * @param childNodeName the name of the child node for which the definition should be retrieved. Use
-     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual child node definition (if any).
+     * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
+     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
      * @return the child node definition with the given name or <code>null</code> if no such definition exists.
      * @see JcrNodeType#RESIDUAL_ITEM_NAME
+     * @see #getChildNodeDefinition(String)
      */
-    JcrNodeDefinition getChildNodeDefinition( String childNodeName ) {
+    JcrNodeDefinition getChildNodeDefinition( Name childDefinitionName ) {
         for (JcrNodeDefinition childNode : childNodeDefinitions) {
-            if (childNodeName.equals(childNode.getName())) {
+            if (childDefinitionName.equals(childNode.name)) {
                 return childNode;
             }
         }
 
         for (NodeType nodeType : declaredSupertypes) {
-            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childNodeName);
+            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
             if (definition != null) return definition;
         }
         return null;
@@ -153,6 +223,7 @@
      * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
      *         are not allowed given this node type
      */
+    @Deprecated
     JcrNodeDefinition findBestNodeDefinitionForChild( String childName,
                                                       String primaryNodeTypeName ) {
         // First, try to find a child node definition with the given name
@@ -173,6 +244,46 @@
     }
 
     /**
+     * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
+     * primary type is not supplied, then only the name is considered when finding a best match.
+     * 
+     * @param childName the name of the child
+     * @param primaryNodeTypeName the name of the primary node type for the child
+     * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
+     *         are not allowed given this node type
+     */
+    JcrNodeDefinition findBestNodeDefinitionForChild( Name childName,
+                                                      Name primaryNodeTypeName ) {
+        // First, try to find a child node definition with the given name
+        JcrNodeDefinition childNode = getChildNodeDefinition(childName);
+
+        // If there are no named definitions in the type hierarchy, try to find a residual node definition
+        boolean checkResidual = true;
+        if (childNode == null) {
+            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
+            checkResidual = false;
+        }
+
+        // Check if the node can be added with the named child node definition
+        if (childNode != null && primaryNodeTypeName != null) {
+            NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+            if (primaryNodeType == null) return null;
+            if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) {
+                if (checkResidual) {
+                    // Find a residual child definition ...
+                    childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
+                    if (childNode != null) {
+                        // Check the residual child definition ...
+                        if (checkTypeAgainstDefinition(primaryNodeType, childNode)) return childNode;
+                    }
+                }
+                return null;
+            }
+        }
+        return childNode;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
@@ -211,6 +322,10 @@
         }
     }
 
+    protected final NodeType getPrimaryNodeType( Name primaryNodeTypeName ) {
+        return session.nodeTypeManager().getNodeType(primaryNodeTypeName);
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -306,9 +421,9 @@
                                    Value value ) {
         CheckArg.isNotNull(propertyName, "propertyName");
 
-        JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+        JcrPropertyDefinition property = getPropertyDefinition(propertyName, false);
         if (property == null) {
-            property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+            property = getPropertyDefinition(RESIDUAL_ITEM_NAME, false);
         }
 
         if (property == null) {
@@ -324,12 +439,11 @@
         if (value == null) {
             return !property.isMandatory();
         }
-        
+
         try {
             assert value instanceof JcrValue : "Illegal implementation of Value interface";
-            ((JcrValue) value).asType(property.getRequiredType());
-        }
-        catch (javax.jcr.ValueFormatException vfe) {
+            ((JcrValue)value).asType(property.getRequiredType());
+        } catch (javax.jcr.ValueFormatException vfe) {
             // Cast failed
             return false;
         }
@@ -345,9 +459,9 @@
                                    Value[] values ) {
         CheckArg.isNotNull(propertyName, "propertyName");
 
-        JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+        JcrPropertyDefinition property = getPropertyDefinition(propertyName, true);
         if (property == null) {
-            property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+            property = getPropertyDefinition(RESIDUAL_ITEM_NAME, true);
         }
 
         if (property == null) {
@@ -368,9 +482,8 @@
             if (values[i] != null) {
                 try {
                     assert values[i] instanceof JcrValue : "Illegal implementation of Value interface";
-                    ((JcrValue) values[i]).asType(property.getRequiredType());
-                }
-                catch (javax.jcr.ValueFormatException vfe) {
+                    ((JcrValue)values[i]).asType(property.getRequiredType());
+                } catch (javax.jcr.ValueFormatException vfe) {
                     // Cast failed
                     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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -96,7 +96,7 @@
         return new JcrNodeTypeIterator(mixinNodeTypes.values());
     }
 
-    final JcrNodeType getNodeType( Name nodeTypeName ) {
+    JcrNodeType getNodeType( Name nodeTypeName ) {
 
         JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
         if (nodeType == null) {
@@ -141,4 +141,32 @@
         return null;
     }
 
+    /**
+     * Get the node definition given the supplied identifier.
+     * 
+     * @param definitionId the identifier of the node definition
+     * @return the node definition, or null if there is no such definition (or if the ID was null)
+     */
+    JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
+        if (definitionId == null) return null;
+        Name nodeTypeName = definitionId.getNodeTypeName();
+        JcrNodeType nodeType = getNodeType(nodeTypeName);
+        return nodeType.getChildNodeDefinition(definitionId.getChildDefinitionName());
+    }
+
+    /**
+     * Get the property definition given the supplied identifier.
+     * 
+     * @param definitionId the identifier of the node definition
+     * @param prefersMultiValued true if the property should be a multi-valued, or false if it should be single-valued
+     * @return the node definition, or null if there is no such definition (or if the ID was null)
+     */
+    JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId,
+                                                 boolean prefersMultiValued ) {
+        if (definitionId == null) return null;
+        Name nodeTypeName = definitionId.getNodeTypeName();
+        JcrNodeType nodeType = getNodeType(nodeTypeName);
+        return nodeType.getPropertyDefinition(definitionId.getPropertyDefinitionName(), prefersMultiValued);
+    }
+
 }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -24,15 +24,13 @@
 package org.jboss.dna.jcr;
 
 import javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.property.Name;
 
-
 /**
- * DNA implementation of the {@link PropertyDefinition} interface.  This implementation is immutable and has all fields initialized
- * through its constructor. 
+ * DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
+ * through its constructor.
  */
 @Immutable
 class JcrPropertyDefinition extends JcrItemDefinition implements PropertyDefinition {
@@ -41,9 +39,10 @@
     private final int requiredType;
     private final String[] valueConstraints;
     private final boolean multiple;
+    private PropertyDefinitionId id;
 
     JcrPropertyDefinition( JcrSession session,
-                           NodeType declaringNodeType,
+                           JcrNodeType declaringNodeType,
                            Name name,
                            int onParentVersion,
                            boolean autoCreated,
@@ -61,6 +60,18 @@
     }
 
     /**
+     * Get the durable identifier for this property definition.
+     * 
+     * @return the property definition ID; never null
+     */
+    public PropertyDefinitionId getId() {
+        if (id == null) {
+            id = new PropertyDefinitionId(declaringNodeType.getInternalName(), name);
+        }
+        return id;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues()
@@ -104,7 +115,7 @@
      * @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
      *         <code>declaringNodeType</code>.
      */
-    JcrPropertyDefinition with( NodeType declaringNodeType ) {
+    JcrPropertyDefinition with( JcrNodeType declaringNodeType ) {
         return new JcrPropertyDefinition(this.session, declaringNodeType, this.name, this.getOnParentVersion(),
                                          this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
                                          this.getRequiredType(), this.getValueConstraints(), this.isMultiple());

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -31,16 +31,15 @@
 import org.jboss.dna.common.util.CheckArg;
 
 /**
- * @author jverhaeg
  */
 @Immutable
 final class JcrPropertyIterator implements PropertyIterator {
 
-    private final Iterator<Property> iterator;
+    private final Iterator<? extends Property> iterator;
     private int ndx;
     private int size;
 
-    JcrPropertyIterator( Collection<Property> properties ) {
+    JcrPropertyIterator( Collection<? extends Property> properties ) {
         assert properties != null;
         iterator = properties.iterator();
         size = properties.size();
@@ -88,9 +87,9 @@
      * @see javax.jcr.PropertyIterator#nextProperty()
      */
     public Property nextProperty() {
-        Property property = iterator.next();
+        Property next = iterator.next();
         ndx++;
-        return property;
+        return next;
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -23,11 +23,12 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.UUID;
+import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
-import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.RepositoryException;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.Location;
 
 /**
  * @author jverhaeg
@@ -35,15 +36,24 @@
 @NotThreadSafe
 final class JcrRootNode extends AbstractJcrNode {
 
-    JcrRootNode( JcrSession session,
-                 Location location,
-                 NodeDefinition nodeDefinition ) {
-        super(session, location, nodeDefinition);
+    JcrRootNode( SessionCache cache,
+                 UUID nodeUuid ) {
+        super(cache, nodeUuid);
     }
 
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.AbstractJcrNode#isRoot()
+     */
+    @Override
+    boolean isRoot() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @return 0;
      * @see javax.jcr.Item#getDepth()
      */
@@ -91,4 +101,18 @@
     public String getPath() {
         return "/";
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.AbstractJcrItem#getAncestor(int)
+     */
+    @Override
+    public final Item getAncestor( int depth ) throws RepositoryException {
+        if (depth == 0) return this;
+        if (depth < 0) {
+            throw new ItemNotFoundException(JcrI18n.noNegativeDepth.text(depth));
+        }
+        throw new ItemNotFoundException(JcrI18n.tooDeep.text(depth));
+    }
 }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -28,11 +28,6 @@
 import java.security.AccessControlException;
 import java.security.Principal;
 import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -42,7 +37,6 @@
 import javax.jcr.NamespaceException;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
@@ -51,10 +45,6 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.PropertyDefinition;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
@@ -62,20 +52,14 @@
 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.RepositorySourceException;
 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.ValueFactories;
-import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
-import com.google.common.base.ReferenceType;
-import com.google.common.collect.ReferenceMap;
 
 /**
  * @author John Verhaeg
@@ -84,12 +68,6 @@
 @NotThreadSafe
 class JcrSession implements Session {
 
-    /**
-     * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be
-     * included anyway. This is currently {@value} .
-     */
-    private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
-
     private static final String[] NO_ATTRIBUTES_NAMES = new String[] {};
 
     /**
@@ -114,21 +92,23 @@
     protected final ExecutionContext executionContext;
 
     /**
+     * The session-specific attributes that came from the {@link SimpleCredentials}' {@link SimpleCredentials#getAttributeNames()}
+     */
+    private final Map<String, Object> sessionAttributes;
+
+    /**
      * The graph representing this session, which uses the {@link #graph session's graph}.
      */
     private final Graph graph;
 
+    private final SessionCache cache;
+
     /**
-     * The session-specific attributes that came from the {@link SimpleCredentials}' {@link SimpleCredentials#getAttributeNames()}
+     * A cached instance of the root path.
      */
-    private final Map<String, Object> sessionAttributes;
+    private final Path rootPath;
 
-    private final ReferenceMap<UUID, AbstractJcrNode> nodesByUuid;
-    private final ReferenceMap<String, AbstractJcrNode> nodesByJcrUuid;
     private boolean isLive;
-    private JcrRootNode rootNode;
-    private PropertyDefinition anyMultiplePropertyDefinition;
-    private transient org.jboss.dna.graph.property.Property defaultPrimaryType;
 
     JcrSession( JcrRepository repository,
                 JcrWorkspace workspace,
@@ -147,14 +127,15 @@
         NamespaceRegistry local = new LocalNamespaceRegistry(workspaceRegistry);
         this.executionContext = workspaceContext.with(local);
         this.sessionRegistry = new JcrNamespaceRegistry(Behavior.JSR170_SESSION, local, workspaceRegistry);
+        this.rootPath = this.executionContext.getValueFactories().getPathFactory().createRootPath();
 
         // Set up the graph to use for this session (which uses the session's namespace registry and context) ...
         this.graph = Graph.create(this.repository.getRepositorySourceName(),
                                   this.repository.getConnectionFactory(),
                                   this.executionContext);
 
-        this.nodesByUuid = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
-        this.nodesByJcrUuid = new ReferenceMap<String, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
+        this.cache = new SessionCache(this, workspace.getName(), this.executionContext, this.workspace.nodeTypeManager(),
+                                      this.graph);
         this.isLive = true;
 
         assert this.repository != null;
@@ -169,24 +150,15 @@
         return this.executionContext;
     }
 
-    final JcrNodeTypeManager nodeTypeManager() {
+    JcrNodeTypeManager nodeTypeManager() {
         return this.workspace.nodeTypeManager();
     }
 
-    final NamespaceRegistry namespaces() {
+    NamespaceRegistry namespaces() {
         return this.executionContext.getNamespaceRegistry();
     }
 
     /**
-     * Return an unmodifiable map of nodes given then UUID.
-     * 
-     * @return nodesByUuid
-     */
-    Map<UUID, AbstractJcrNode> getNodesByUuid() {
-        return Collections.unmodifiableMap(nodesByUuid);
-    }
-
-    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Session#getWorkspace()
@@ -417,21 +389,6 @@
         throw new UnsupportedOperationException();
     }
 
-    Node getChild( AbstractJcrNode parent,
-                   Location location ) throws RepositoryException {
-        Node child = null;
-        UUID uuid = location.getUuid();
-        if (uuid != null) {
-            // The location has a UUID, so look up the node by this UUID in the session ...
-            child = getNode(uuid);
-        }
-        if (child == null) {
-            // The child was not found in the session's cache, so create the node using its path ...
-            child = loadNode(parent, location.getPath());
-        }
-        return child;
-    }
-
     /**
      * Find or create a JCR Node for the given path. This method works for the root node, too.
      * 
@@ -441,212 +398,25 @@
      * @throws RepositoryException if there is a problem
      */
     Node getNode( Path path ) throws RepositoryException, PathNotFoundException {
-        if (path.isRoot()) return rootNode();
-
-        // Start at the root and walk down the path ...
-        AbstractJcrNode parent = rootNode();
-        AbstractJcrNode node = null;
-        Iterator<Path> pathIter = path.pathsFromRoot();
-        while (pathIter.hasNext()) {
-            node = loadNode(parent, pathIter.next()); // should throw PathNotFoundException if not there
-            parent = node;
-        }
-        return node;
+        return cache.findJcrNode(null, path.relativeTo(rootPath));
     }
 
     /**
-     * Find or create a JCR Node for the given path. This method works for the root node, too.
-     * 
-     * @param parent the parent of the node, if known; null if the parent is not known
-     * @param path the path; may not be null
-     * @return the JCR node instance for the given path; never null
-     * @throws PathNotFoundException if the path could not be found
-     * @throws RepositoryException if there is a problem
-     */
-    private AbstractJcrNode loadNode( AbstractJcrNode parent,
-                                      Path path ) throws RepositoryException, PathNotFoundException {
-        boolean isRoot = path.isRoot();
-        if (isRoot && rootNode != null) return rootNode;
-
-        // Get node from source and get it's UUID ...
-        org.jboss.dna.graph.Node graphNode = null;
-        try {
-            graphNode = graph.getNodeAt(path);
-        } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
-            // If the node isn't found, throw a PathNotFoundException
-            throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
-        }
-
-        // Now get the DNA node's UUID ...
-        Location location = graphNode.getLocation();
-        ValueFactories factories = executionContext.getValueFactories();
-        UUID uuid = location.getUuid();
-        org.jboss.dna.graph.property.Property uuidProperty = null;
-        if (uuid != null) {
-            // Check for an existing node at this UUID ...
-            AbstractJcrNode existing = nodesByUuid.get(uuid);
-            if (existing != null) return existing;
-
-            // Considered an identification property ...
-            uuidProperty = location.getIdProperty(JcrLexicon.UUID);
-            if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
-        }
-        if (uuidProperty == null) {
-            uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
-            if (uuidProperty != null) {
-                // Grab the first 'good' UUID value ...
-                for (Object uuidValue : uuidProperty) {
-                    try {
-                        uuid = factories.getUuidFactory().create(uuidValue);
-                        break;
-                    } catch (ValueFormatException e) {
-                        // Ignore; just continue with the next property value
-                    }
-                }
-            }
-            if (uuid == null) {
-                // Look for the DNA UUID property ...
-                org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
-                if (dnaUuidProperty != null) {
-                    // Grab the first 'good' UUID value ...
-                    for (Object uuidValue : dnaUuidProperty) {
-                        try {
-                            uuid = factories.getUuidFactory().create(uuidValue);
-                            break;
-                        } catch (ValueFormatException e) {
-                            // Ignore; just continue with the next property value
-                        }
-                    }
-                }
-            }
-        }
-        if (uuid == null) uuid = UUID.randomUUID();
-        if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
-
-        // See if there is already a JCR node object for this UUID ...
-        if (uuid != null && !isRoot) {
-            AbstractJcrNode node = getNode(uuid);
-            if (node != null) return node;
-        }
-
-        // Either the UUID is not known, or there was no node. Either way, we have to create the node ...
-        if (uuid == null) uuid = UUID.randomUUID();
-
-        // Look for the primary type of the node ...
-        String primaryTypeNameString = null;
-        NamespaceRegistry namespaces = namespaces();
-        org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
-        if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
-            Name primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
-            primaryTypeNameString = primaryTypeName.getString(namespaces);
-        } else {
-            // We have to have a primary type, so use the default ...
-            if (defaultPrimaryType == null) {
-                defaultPrimaryType = executionContext.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE,
-                                                                                  JcrNtLexicon.UNSTRUCTURED);
-            }
-            primaryTypeProperty = defaultPrimaryType;
-            // We have to add this property to the graph node...
-            graphNode.getPropertiesByName().put(primaryTypeProperty.getName(), primaryTypeProperty);
-        }
-        assert primaryTypeProperty.isEmpty() == false;
-
-        // Look for a node definition stored on the node ...
-        NodeDefinition definition = null;
-        org.jboss.dna.graph.property.Property nodeDefnProperty = graphNode.getProperty(DnaLexicon.NODE_DEFINITON);
-        if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
-            Path nodeDefnPath = factories.getPathFactory().create(nodeDefnProperty.getFirstValue());
-            // Look up the node definition ...
-            Name nodeTypeName = nodeDefnPath.getSegment(0).getName();
-            JcrNodeType nodeType = nodeTypeManager().getNodeType(nodeTypeName);
-            if (nodeType != null && nodeDefnPath.size() > 1) {
-                // Look up the definition for the child name rule (in the second segment of the relative path) ...
-                String childNameRule = nodeDefnPath.getSegment(1).getString(namespaces);
-                definition = nodeType.findBestNodeDefinitionForChild(childNameRule, primaryTypeNameString);
-            }
-        }
-
-        AbstractJcrNode node = null;
-        if (isRoot) {
-            // The node definition should be set ...
-            if (definition == null) {
-                definition = nodeTypeManager().getRootNodeDefinition();
-                assert definition != null;
-            }
-
-            // Create the new node ...
-            node = new JcrRootNode(this, location, definition);
-        } else {
-            // Find the parent ...
-            if (parent == null) {
-                parent = (AbstractJcrNode)getNode(path.getParent());
-            }
-            assert parent != null;
-
-            // Find the node definition for this node ...
-            if (definition == null) {
-                // Look for the parent's node type, and look for a node definition based upon the name ...
-                JcrNodeType nodeType = (JcrNodeType)parent.getPrimaryNodeType();
-                String childName = path.getLastSegment().getName().getString(namespaces);
-                definition = nodeType.findBestNodeDefinitionForChild(childName, primaryTypeNameString);
-                if (definition == null) {
-                    String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspace.getName());
-                    throw new RepositorySourceException(msg);
-                }
-            }
-
-            // Now create the node object ...
-            node = new JcrNode(this, parent.internalUuid(), location, definition);
-        }
-
-        // Now populate the node and add to the cache ...
-        populateNode(node, graphNode, uuid, uuidProperty, primaryTypeProperty);
-        if (isRoot) rootNode = (JcrRootNode)node;
-        return node;
-    }
-
-    AbstractJcrNode getNode( UUID uuid ) {
-        return nodesByUuid.get(uuid);
-    }
-
-    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Session#getNodeByUUID(java.lang.String)
      */
     public Node getNodeByUUID( String uuid ) throws ItemNotFoundException, RepositoryException {
-        Node result = null;
-        try {
-            result = nodesByUuid.get(UUID.fromString(uuid));
-        } catch (IllegalArgumentException e) {
-            throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
-        } catch (Throwable e) {
-            throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithUuid.text(uuid, workspace.getName()));
-        }
-        if (result == null) {
-            throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
-        }
-        return result;
+        return cache.findJcrNode(UUID.fromString(uuid));
     }
 
-    JcrRootNode rootNode() throws RepositoryException {
-        // Return cached root node if available
-        if (rootNode != null) {
-            return rootNode;
-        }
-        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-        loadNode(null, rootPath); // sets the root node
-        assert rootNode != null;
-        return rootNode;
-    }
-
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Session#getRootNode()
      */
     public Node getRootNode() throws RepositoryException {
-        return rootNode();
+        return cache.findJcrRootNode();
     }
 
     /**
@@ -842,187 +612,6 @@
         return PropertyType.UNDEFINED;
     }
 
-    final void populateNode( AbstractJcrNode node,
-                             org.jboss.dna.graph.Node graphNode,
-                             UUID uuid,
-                             org.jboss.dna.graph.property.Property uuidProperty,
-                             org.jboss.dna.graph.property.Property primaryTypeProperty ) throws RepositoryException {
-        assert node != null;
-        assert graphNode != null;
-        assert uuid != null;
-        assert uuidProperty != null;
-        assert primaryTypeProperty != null;
-
-        // --------------------------------------------------
-        // Create JCR children for corresponding DNA children
-        // --------------------------------------------------
-        node.setChildren(graphNode.getChildren());
-
-        // ------------------------------------------------------
-        // Create JCR properties for corresponding DNA properties
-        // ------------------------------------------------------
-        // First get the property type for each property, based upon the primary type and mixins ...
-        // The map with single-valued properties...
-        Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
-        // ... and the map with multi-valued properties
-        Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
-        
-        
-        boolean referenceable = false;
-
-        NamespaceRegistry registry = namespaces();
-        ValueFactories factories = executionContext.getValueFactories();
-        NameFactory nameFactory = factories.getNameFactory();
-        NodeTypeManager nodeTypeManager = getWorkspace().getNodeTypeManager();
-        List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
-        // Start with the primary type ...
-        Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
-        String primaryTypeNameString = primaryTypeName.getString(registry);
-        NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
-        for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
-            String nameString = propertyDefn.getName();
-            if ("*".equals(nameString)) {
-                anyPropertyDefinitions.add(propertyDefn);
-                continue;
-            }
-            Name name = nameFactory.create(nameString);
-            
-            if (propertyDefn.isMultiple()) {
-                PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-            }
-            else {
-                PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-            }
-        }
-        // The process the mixin types ...
-        org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
-        if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
-            for (Object mixinTypeValue : mixinTypesProperty) {
-                Name mixinTypeName = nameFactory.create(mixinTypeValue);
-                if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
-                String mixinTypeNameString = mixinTypeName.getString(registry);
-                NodeType mixinType = nodeTypeManager.getNodeType(mixinTypeNameString);
-                for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
-                    String nameString = propertyDefn.getName();
-                    if ("*".equals(nameString)) {
-                        anyPropertyDefinitions.add(propertyDefn);
-                        continue;
-                    }
-                    Name name = nameFactory.create(nameString);
-                    if (propertyDefn.isMultiple()) {
-                        PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                        if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-                    }
-                    else {
-                        PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                        if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-                    }
-                }
-            }
-        }
-
-        // Now create the JCR property object wrapper around the "jcr:uuid" property ...
-        Map<Name, Property> properties = new HashMap<Name, Property>();
-        if (referenceable) {
-            // We know that this property is single-valued
-            PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
-            properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, propertyDefinition, PropertyType.STRING,
-                                                                       uuidProperty));
-        }
-
-        // Now create the JCR property object wrappers around the other properties ...
-        for (org.jboss.dna.graph.property.Property dnaProp : graphNode.getProperties()) {
-            Name name = dnaProp.getName();
-
-            // Skip the JCR and DNA UUID properties (using the EXACT Name instances on the Property) ...
-            if (JcrLexicon.UUID.equals(name) || DnaLexicon.UUID.equals(name)) continue;
-
-            // Figure out the JCR property type for this property ...
-            PropertyDefinition propertyDefinition;
-            
-            if (dnaProp.isMultiple()) {
-                propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
-            }
-            else {
-                propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);       
-                
-                // If the property has only one value, dnaProp.isMultiple() will return false, but the
-                // property may actually be a multi-valued property that happens to have one property set.
-                if (propertyDefinition == null) {
-                    propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
-                }
-            }
-
-            // If no property type was found for this property, see if there is a wildcard property ...
-            if (propertyDefinition == null) {
-                for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext(); ) {
-                    PropertyDefinition nextDef = iter.next();
-                    
-                    // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
-                    if ((nextDef.isMultiple() && dnaProp.isMultiple())
-                        || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
-                        propertyDefinition = nextDef;
-                        break;
-                    }
-                }
-            }
-            
-
-            // If there still is no property type defined ...
-            if (propertyDefinition == null) {
-                assert anyPropertyDefinitions.isEmpty();
-                if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
-                    // We can use the "nt:unstructured" property definitions for any property ...
-                    if (anyMultiplePropertyDefinition == null) {
-                        String unstructuredName = JcrNtLexicon.UNSTRUCTURED.getString(registry);
-                        NodeType unstructured = nodeTypeManager.getNodeType(unstructuredName);
-                        for (PropertyDefinition definition : unstructured.getDeclaredPropertyDefinitions()) {
-                            if (definition.isMultiple()) {
-                                anyMultiplePropertyDefinition = definition;
-                            }
-                        }
-                    }
-                    propertyDefinition = anyMultiplePropertyDefinition;
-                }
-            }
-            if (propertyDefinition == null) {
-                // We're supposed to skip this property (since we don't have a definition for it) ...
-                continue;
-            }
-
-            // Figure out if this is a multi-valued property ...
-            boolean isMultiple = propertyDefinition.isMultiple();
-            if (!isMultiple && dnaProp.isEmpty()) {
-                // Only multi-valued properties can have no values; so if not multi-valued, then skip ...
-                continue;
-            }
-
-            // Figure out the property type ...
-            int propertyType = propertyDefinition.getRequiredType();
-            if (propertyType == PropertyType.UNDEFINED) {
-                propertyType = jcrPropertyTypeFor(dnaProp);
-            }
-
-            // Create the appropriate JCR property wrapper ...
-            if (isMultiple) {
-                properties.put(name, new JcrMultiValueProperty(node, propertyDefinition, propertyType, dnaProp));
-            } else {
-                properties.put(name, new JcrSingleValueProperty(node, propertyDefinition, propertyType, dnaProp));
-            }
-        }
-
-        // Now set the properties on the node ...
-        node.setProperties(properties);
-
-        // Set node's UUID, creating one if necessary
-        nodesByJcrUuid.put(uuid.toString(), node);
-        node.setInternalUuid(uuid);
-        // Setup node to be retrieved by DNA UUID
-        nodesByUuid.put(uuid, node);
-    }
-
     /**
      * {@inheritDoc}
      * 

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -30,9 +30,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFactories;
 
@@ -41,11 +39,9 @@
  */
 final class JcrSingleValueProperty extends AbstractJcrProperty {
 
-    JcrSingleValueProperty( AbstractJcrNode node,
-                            PropertyDefinition definition,
-                            int propertyType,
-                            Property dnaProperty ) {
-        super(node, definition, propertyType, dnaProperty);
+    JcrSingleValueProperty( SessionCache cache,
+                            PropertyId propertyId ) {
+        super(cache, propertyId);
     }
 
     /**
@@ -55,7 +51,7 @@
      */
     public boolean getBoolean() throws RepositoryException {
         try {
-            return getExecutionContext().getValueFactories().getBooleanFactory().create(getDnaProperty().getFirstValue());
+            return context().getValueFactories().getBooleanFactory().create(property().getFirstValue());
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -68,10 +64,7 @@
      */
     public Calendar getDate() throws RepositoryException {
         try {
-            return getExecutionContext().getValueFactories()
-                                        .getDateFactory()
-                                        .create(getDnaProperty().getFirstValue())
-                                        .toCalendar();
+            return context().getValueFactories().getDateFactory().create(property().getFirstValue()).toCalendar();
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -84,7 +77,7 @@
      */
     public double getDouble() throws RepositoryException {
         try {
-            return getExecutionContext().getValueFactories().getDoubleFactory().create(getDnaProperty().getFirstValue());
+            return context().getValueFactories().getDoubleFactory().create(property().getFirstValue());
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -96,7 +89,7 @@
      * @see javax.jcr.Property#getLength()
      */
     public long getLength() throws RepositoryException {
-        return createValue(getDnaProperty().getFirstValue()).getLength();
+        return createValue(property().getFirstValue()).getLength();
     }
 
     /**
@@ -116,7 +109,7 @@
      */
     public long getLong() throws RepositoryException {
         try {
-            return getExecutionContext().getValueFactories().getLongFactory().create(getDnaProperty().getFirstValue());
+            return context().getValueFactories().getLongFactory().create(property().getFirstValue());
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -129,10 +122,10 @@
      */
     public final Node getNode() throws RepositoryException {
         try {
-            ValueFactories factories = getExecutionContext().getValueFactories();
-            Reference dnaReference = factories.getReferenceFactory().create(getDnaProperty().getFirstValue());
+            ValueFactories factories = context().getValueFactories();
+            Reference dnaReference = factories.getReferenceFactory().create(property().getFirstValue());
             UUID uuid = factories.getUuidFactory().create(dnaReference);
-            return ((JcrSession)getSession()).getNode(uuid);
+            return cache.findJcrNode(uuid);
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -145,7 +138,7 @@
      */
     public InputStream getStream() throws RepositoryException {
         try {
-            Binary binary = getExecutionContext().getValueFactories().getBinaryFactory().create(getDnaProperty().getFirstValue());
+            Binary binary = context().getValueFactories().getBinaryFactory().create(property().getFirstValue());
             return new SelfClosingInputStream(binary);
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
@@ -159,7 +152,7 @@
      */
     public String getString() throws RepositoryException {
         try {
-            return getExecutionContext().getValueFactories().getStringFactory().create(getDnaProperty().getFirstValue());
+            return context().getValueFactories().getStringFactory().create(property().getFirstValue());
         } catch (org.jboss.dna.graph.property.ValueFormatException e) {
             throw new ValueFormatException(e.getMessage(), e);
         }
@@ -170,8 +163,8 @@
      * 
      * @see javax.jcr.Property#getValue()
      */
-    public Value getValue() {
-        return createValue(getDnaProperty().getFirstValue());
+    public Value getValue() throws RepositoryException {
+        return createValue(property().getFirstValue());
     }
 
     /**

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -114,7 +114,7 @@
 
         AbstractJcrProperty prop = (AbstractJcrProperty)property;
 
-        Name propertyName = prop.getDnaProperty().getName();
+        Name propertyName = prop.name();
         if (SPECIAL_PROPERTY_NAMES.contains(propertyName)) {
             return;
         }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -148,10 +148,10 @@
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
 
         // This must be initialized after the session
-        this.nodeTypeManager = new JcrNodeTypeManager(this.session,
-                                                      new DnaBuiltinNodeTypeSource(this.session,
-                                                                                   new JcrBuiltinNodeTypeSource(this.session)));
-
+        JcrNodeTypeSource source = null;
+        source = new JcrBuiltinNodeTypeSource(this.session);
+        source = new DnaBuiltinNodeTypeSource(this.session, source);
+        this.nodeTypeManager = new JcrNodeTypeManager(this.session, source);
     }
 
     final String getSourceName() {

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-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,164 @@
+/*
+ * 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 net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+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.
+ */
+ 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 int hc;
+
+    /**
+     * 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
+     */
+    public NodeDefinitionId( Name nodeTypeName,
+                             Name childDefinitionName ) {
+        assert nodeTypeName != null;
+        assert childDefinitionName != null;
+        this.nodeTypeName = nodeTypeName;
+        this.childDefinitionName = childDefinitionName;
+        this.hc = HashCode.compute(this.nodeTypeName, this.childDefinitionName);
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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.nodeTypeName.getString() + '/' + this.childDefinitionName.getString();
+    }
+
+    /**
+     * 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 ) {
+        int index = definition.indexOf('/');
+        String nodeTypeNameString = definition.substring(0, index);
+        String childDefinitionNameString = definition.substring(index + 1);
+        Name nodeTypeName = factory.create(nodeTypeNameString);
+        Name childDefinitionName = factory.create(childDefinitionNameString);
+        return new NodeDefinitionId(nodeTypeName, childDefinitionName);
+    }
+
+    /**
+     * {@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 NodeDefinitionId) {
+            NodeDefinitionId that = (NodeDefinitionId)obj;
+            if (this.hc != that.hc) return false;
+            if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
+            return this.childDefinitionName.equals(that.childDefinitionName);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return this.nodeTypeName.toString() + '/' + this.childDefinitionName.toString();
+    }
+
+}


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

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-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,162 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * 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 net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+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.
+ */
+ 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 hc;
+
+    /**
+     * Create a new identifier for a propety 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
+     */
+    public PropertyDefinitionId( Name nodeTypeName,
+                                 Name propertyDefinitionName ) {
+        this.nodeTypeName = nodeTypeName;
+        this.propertyDefinitionName = propertyDefinitionName;
+        this.hc = HashCode.compute(this.nodeTypeName, this.propertyDefinitionName);
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString();
+    }
+
+    /**
+     * 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 ) {
+        int index = definition.indexOf('/');
+        String nodeTypeNameString = definition.substring(0, index);
+        String propertyDefinitionNameString = definition.substring(index + 1);
+        Name nodeTypeName = factory.create(nodeTypeNameString);
+        Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
+        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName);
+    }
+
+    /**
+     * {@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 PropertyDefinitionId) {
+            PropertyDefinitionId that = (PropertyDefinitionId)obj;
+            if (this.hc != that.hc) return false;
+            if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
+            return this.propertyDefinitionName.equals(that.propertyDefinitionName);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return this.nodeTypeName.toString() + '/' + this.propertyDefinitionName.toString();
+    }
+
+}


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

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-03-16 22:50:09 UTC (rev 777)
@@ -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();
+    }
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,1583 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Results;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+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.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import com.google.common.base.ReferenceType;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ReferenceMap;
+
+/**
+ * The class that manages the session's information that has been locally-cached after reading from the underlying {@link Graph
+ * repository} or modified by the session but not yet saved or commited to the repository.
+ * <p>
+ * The cached information is broken into several different categories that are each described below.
+ * </p>
+ * <h3>JCR API objects</h3>
+ * <p>
+ * Clients using the DNA JCR implementation obtain a {@link JcrSession JCR Session} (which generally owns this cache instance) as
+ * well as the JCR {@link JcrNode Node} and {@link AbstractJcrProperty Property} instances. This cache ensures that the same JCR
+ * Node or Property objects are always returned for the same item in the repository, ensuring that the "==" operator always holds
+ * true for the same item. However, as soon as all (client) references to these objects are garbage collected, this class is free
+ * to also release those objects and, when needed, recreate new implementation objects.
+ * </p>
+ * <p>
+ * This approach helps reduce memory utilization since any unused items are available for garbage collection, but it also
+ * guarantees that once a client maintains a reference to an item, the same Java object will always be used for any references to
+ * that item.
+ * </p>
+ * <h3>Cached nodes</h3>
+ * <p>
+ * The session cache is also responsible for maintaining a local cache of node information retrieved from the underlying
+ * repository, reducing the need to request information any more than necessary. This information includes that obtained directly
+ * from the repository store, including node properties, children, and references to the parent. It also includes computed
+ * information, such as the NodeDefinition for a node, the name of the primary type and mixin types, and the original
+ * {@link Location} of the node in the repository.
+ * </p>
+ * <h3>Transient changes</h3>
+ * <p>
+ * Any time content is changed in the session, those changes are held within the session until they are saved either by
+ * {@link Session#save() saving the session} or {@link Item#save() saving an individual item} (which includes any content below
+ * that item). This cache maintains all these transient changes, and when requested will send the change requests down the
+ * repository. At any point, these transient changes may be rolled back (or "released"), again either for the
+ * {@link Session#refresh(boolean) whole session} or for {@link Item#refresh(boolean) individual items}.
+ * </p>
+ */
+ at ThreadSafe
+public class SessionCache {
+
+    /**
+     * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be
+     * included anyway. This is currently {@value} .
+     */
+    private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
+
+    private final JcrSession session;
+    private final String workspaceName;
+    protected final ExecutionContext context;
+    protected final PathFactory pathFactory;
+    private final NameFactory nameFactory;
+    private final ValueFactory<String> stringFactory;
+    private final NamespaceRegistry namespaces;
+    private final PropertyFactory propertyFactory;
+    private final Graph store;
+    private final Name defaultPrimaryTypeName;
+    private final Property defaultPrimaryTypeProperty;
+    private final Path rootPath;
+
+    private UUID root;
+    private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
+    private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
+
+    private final HashMap<UUID, NodeInfo> cachedNodes;
+    private final HashMap<UUID, NodeInfo> changedNodes;
+
+    public SessionCache( JcrSession session,
+                         String workspaceName,
+                         ExecutionContext context,
+                         JcrNodeTypeManager nodeTypes,
+                         Graph store ) {
+        assert session != null;
+        assert workspaceName != null;
+        assert context != null;
+        assert store != null;
+        this.session = session;
+        this.workspaceName = workspaceName;
+        this.store = store;
+        this.context = context;
+        this.pathFactory = context.getValueFactories().getPathFactory();
+        this.nameFactory = context.getValueFactories().getNameFactory();
+        this.stringFactory = context.getValueFactories().getStringFactory();
+        this.namespaces = context.getNamespaceRegistry();
+        this.propertyFactory = context.getPropertyFactory();
+        this.defaultPrimaryTypeName = JcrNtLexicon.UNSTRUCTURED;
+        this.defaultPrimaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, this.defaultPrimaryTypeName);
+        this.rootPath = pathFactory.createRootPath();
+
+        this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
+        this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
+
+        this.cachedNodes = new HashMap<UUID, NodeInfo>();
+        this.changedNodes = new HashMap<UUID, NodeInfo>();
+    }
+
+    JcrSession session() {
+        return session;
+    }
+
+    ExecutionContext context() {
+        return context;
+    }
+
+    String workspaceName() {
+        return workspaceName;
+    }
+
+    JcrNodeTypeManager nodeTypes() {
+        return session.nodeTypeManager();
+    }
+
+    public JcrRootNode findJcrRootNode() throws RepositoryException {
+        return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
+    }
+
+    /**
+     * Find the JCR {@link JcrNode Node implementation} for the given UUID.
+     * 
+     * @param uuid the node's UUID
+     * @return the existing node implementation
+     * @throws ItemNotFoundException if a node with the supplied UUID could not be found
+     * @throws RepositoryException if an error resulting in finding this node in the repository
+     */
+    public AbstractJcrNode findJcrNode( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+        AbstractJcrNode node = jcrNodes.get(uuid);
+        if (node != null) return node;
+
+        // An existing JCR Node object was not found, so we'll have to create it by finding the underlying
+        // NodeInfo for the node (from the changed state or the cache) ...
+        NodeInfo info = findNodeInfo(uuid);
+        assert info != null;
+
+        // Create the appropriate JCR Node object ...
+        return createAndCacheJcrNodeFor(info);
+    }
+
+    /**
+     * Find the JCR {@link AbstractJcrNode Node implementation} for the node given by the UUID of a reference node and a relative
+     * path from the reference node. The relative path should already have been {@link Path#getNormalizedPath() normalized}.
+     * 
+     * @param uuidOfReferenceNode the UUID of the reference node; may be null if the root node is to be used as the reference
+     * @param relativePath the relative (but normalized) path from the reference node, but which may be an absolute (and
+     *        normalized) path only when the reference node is the root node; may not be null
+     * @return the information for the referenced node; never null
+     * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
+     * @throws PathNotFoundException if the node given by the relative path does not exist
+     * @throws RepositoryException if any other error occurs while reading information from the repository
+     * @see #findNodeInfoForRoot()
+     */
+    public AbstractJcrNode findJcrNode( UUID uuidOfReferenceNode,
+                                        Path relativePath ) throws PathNotFoundException, RepositoryException {
+        // An existing JCR Node object was not found, so we'll have to create it by finding the underlying
+        // NodeInfo for the node (from the changed state or the cache) ...
+        NodeInfo info = findNodeInfo(uuidOfReferenceNode, relativePath);
+        assert info != null;
+
+        // Look for an existing node ...
+        AbstractJcrNode node = jcrNodes.get(info.getUuid());
+        if (node != null) return node;
+
+        // Create the appropriate JCR Node object ...
+        return createAndCacheJcrNodeFor(info);
+    }
+
+    public AbstractJcrProperty findJcrProperty( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+        AbstractJcrProperty property = jcrProperties.get(propertyId);
+        if (property != null) return property;
+
+        // An existing JCR Property object was not found, so we'll have to create it by finding the underlying
+        // NodeInfo for the property's parent (from the changed state or the cache) ...
+        PropertyInfo info = findPropertyInfo(propertyId); // throws PathNotFoundException if node not there
+        if (info == null) return null; // no such property on this node
+
+        // Now create the appropriate JCR Property object ...
+        return createAndCacheJcrPropertyFor(info);
+    }
+
+    public Collection<AbstractJcrProperty> findJcrPropertiesFor( UUID nodeUuid )
+        throws ItemNotFoundException, RepositoryException {
+        NodeInfo info = findNodeInfo(nodeUuid);
+        Map<Name, PropertyInfo> properties = info.getProperties();
+        Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(properties.size());
+        for (PropertyInfo propertyInfo : properties.values()) {
+            result.add(findJcrProperty(propertyInfo.getPropertyId()));
+        }
+        return result;
+    }
+
+    /**
+     * Find the JCR {@link AbstractJcrItem Item implementation} for the node or property given by the UUID of a reference node and
+     * a relative path from the reference node to the desired item. The relative path should already have been
+     * {@link Path#getNormalizedPath() normalized}.
+     * 
+     * @param uuidOfReferenceNode the UUID of the reference node; may be null if the root node is to be used as the reference
+     * @param relativePath the relative (but normalized) path from the reference node to the desired item, but which may be an
+     *        absolute (and normalized) path only when the reference node is the root node; may not be null
+     * @return the information for the referenced item; never null
+     * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist, or if an item given by the
+     *         supplied relative path does not exist
+     * @throws RepositoryException if any other error occurs while reading information from the repository
+     * @see #findNodeInfoForRoot()
+     */
+    public AbstractJcrItem findJcrItem( UUID uuidOfReferenceNode,
+                                        Path relativePath ) throws ItemNotFoundException, RepositoryException {
+        // A pathological case is an empty relative path ...
+        if (relativePath.size() == 0) {
+            return findJcrNode(uuidOfReferenceNode);
+        }
+        if (relativePath.size() == 1) {
+            Path.Segment segment = relativePath.getLastSegment();
+            if (segment.isSelfReference()) return findJcrNode(uuidOfReferenceNode);
+            if (segment.isParentReference()) {
+                NodeInfo referencedNode = findNodeInfo(uuidOfReferenceNode);
+                return findJcrNode(referencedNode.getParent());
+            }
+        }
+
+        // Peek into the last segment of the path to see whether it uses a SNS index (and it's > 1) ...
+        Path.Segment lastSegment = relativePath.getLastSegment();
+        if (lastSegment.getIndex() > 1) {
+            // Only nodes can have SNS index (but an index of 1 is the default)...
+            return findJcrNode(uuidOfReferenceNode);
+        }
+
+        NodeInfo parent = null;
+        if (relativePath.size() == 1) {
+            // The referenced node must be the parent ...
+            parent = findNodeInfo(uuidOfReferenceNode);
+        } else {
+            // We know that the parent of the referenced item should be a node (if the path is right) ...
+            parent = findNodeInfo(uuidOfReferenceNode, relativePath.getParent());
+        }
+
+        // JSR-170 doesn't allow children and proeprties to have the same name, but this is relaxed in JSR-283.
+        // But JSR-283 Section 3.3.4 states "The method Session.getItem will return the item at the specified path
+        // if there is only one such item, if there is both a node and a property at the specified path, getItem
+        // will return the node." Therefore, look for a child first ...
+        ChildNode child = parent.getChildren().getChild(lastSegment);
+        if (child != null) {
+            return findJcrNode(child.getUuid());
+        }
+
+        // Otherwise it should be a property ...
+        PropertyInfo propertyInfo = parent.getProperty(lastSegment.getName());
+        if (propertyInfo != null) {
+            return findJcrProperty(propertyInfo.getPropertyId());
+        }
+
+        // It was not found, so prepare a good exception message ...
+        String msg = null;
+        if (findNodeInfoForRoot().getUuid().equals(uuidOfReferenceNode)) {
+            // The reference node was the root, so use this fact to convert the path to an absolute path in the message
+            Path absolutePath = rootPath.resolve(relativePath);
+            msg = JcrI18n.itemNotFoundAtPath.text(absolutePath, workspaceName);
+        } else {
+            // Find the path of the reference node ...
+            Path referenceNodePath = getPathFor(uuidOfReferenceNode);
+            msg = JcrI18n.itemNotFoundAtPathRelativeToReferenceNode.text(relativePath, referenceNodePath, workspaceName);
+        }
+        throw new ItemNotFoundException(msg);
+    }
+
+    /**
+     * Utility method that creates and caches the appropriate kind of AbstractJcrNode implementation for node given by the
+     * supplied information.
+     * 
+     * @param info the information for the node; may not be null
+     * @return the <i>new</i> instance of the {@link Node}; never null
+     */
+    private AbstractJcrNode createAndCacheJcrNodeFor( NodeInfo info ) {
+        UUID uuid = info.getUuid();
+        Location location = info.getOriginalLocation();
+        NodeDefinitionId nodeDefinitionId = info.getDefinitionId();
+        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;
+            }
+        }
+
+        // It is not a root node ...
+        JcrNode node = new JcrNode(this, uuid);
+        jcrNodes.put(uuid, node);
+        return node;
+    }
+
+    /**
+     * Utility method that creates and caches the appropriate kind of AbstractJcrProperty implementation for property given by the
+     * supplied information.
+     * 
+     * @param info the information for the property; may not be null
+     * @return the <i>new</i> instance of the {@link Property}; never null
+     */
+    private AbstractJcrProperty createAndCacheJcrPropertyFor( PropertyInfo info ) {
+        boolean multiValued = info.getProperty().isMultiple();
+        JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId(), multiValued);
+        assert definition != null;
+        if (multiValued) {
+            return new JcrMultiValueProperty(this, info.getPropertyId());
+        }
+        return new JcrSingleValueProperty(this, info.getPropertyId());
+    }
+
+    // public AbstractJcrProperty findJcrProperty( Path propertyPath ) throws PathNotFoundException, RepositoryException {
+    // if (propertyPath.isRoot()) return null;
+    // Path nodePath = propertyPath.getParent();
+    // NodeInfo nodeInfo = findNodeInfo(nodePath);
+    // Name propertyName = propertyPath.getLastSegment().getName();
+    // PropertyInfo info = nodeInfo.getProperty(propertyName);
+    //
+    // // Look for an existing JCR Property object ...
+    // PropertyId propertyId = info.getPropertyId();
+    // AbstractJcrProperty property = jcrProperties.get(propertyId);
+    // if (property != null) return property;
+    //
+    // // Now create the appropriate JCR Property object ...
+    // return createAndCacheJcrPropertyFor(info);
+    // }
+
+    /**
+     * Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
+     * especially after the information has been cached. Note, however, that this method first checks the cache, and if the
+     * information is not in the cache, the information is read from the repository.
+     * 
+     * @param uuid the UUID for the node; may not be null
+     * @return the information for the node with the supplied UUID, or null if the information is not in the cache
+     * @throws ItemNotFoundException if there is no node with the supplied UUID
+     * @throws RepositoryException if any other error occurs while reading information from the repository
+     * @see #findNodeInfoInCache(UUID)
+     * @see #findNodeInfo(UUID, Path)
+     * @see #findNodeInfoForRoot()
+     */
+    NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+        // See if we already have something in the changed nodes ...
+        NodeInfo info = changedNodes.get(uuid);
+        if (info == null) {
+            // Or in the cache ...
+            info = cachedNodes.get(uuid);
+            if (info == null) {
+                info = loadFromGraph(uuid, null);
+            }
+        }
+        return info;
+    }
+
+    /**
+     * Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
+     * especially after the information has been cached. Note, however, that this method only checks the cache.
+     * 
+     * @param uuid the UUID for the node; may not be null
+     * @return the information for the node with the supplied UUID, or null if the information is not in the cache
+     * @see #findNodeInfo(UUID)
+     * @see #findNodeInfo(UUID, Path)
+     * @see #findNodeInfoForRoot()
+     */
+    NodeInfo findNodeInfoInCache( UUID uuid ) {
+        // See if we already have something in the changed nodes ...
+        NodeInfo info = changedNodes.get(uuid);
+        if (info == null) {
+            // Or in the cache ...
+            info = cachedNodes.get(uuid);
+        }
+        return info;
+    }
+
+    /**
+     * Find the information for the root node. Generally, this returns information that's in the cache, except for the first time
+     * the root node is needed.
+     * 
+     * @return the node information
+     * @throws RepositoryException if there is an error while obtaining the information from the repository
+     */
+    NodeInfo findNodeInfoForRoot() throws RepositoryException {
+        if (root == null) {
+            // We haven't found the root yet ...
+            NodeInfo info = loadFromGraph(this.rootPath, null);
+            root = info.getUuid();
+            this.jcrNodes.put(root, new JcrRootNode(this, root));
+            return info;
+        }
+        return findNodeInfo(root);
+    }
+
+    /**
+     * Find the information for the node given by the UUID of a reference node and a relative path from the reference node.
+     * 
+     * @param node the reference node; may be null if the root node is to be used as the reference
+     * @param relativePath the relative path from the reference node, but which may be an absolute path only when the reference
+     *        node is the root node; may not be null
+     * @return the information for the referenced node; never null
+     * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
+     * @throws PathNotFoundException if the node given by the relative path does not exist
+     * @throws RepositoryException if any other error occurs while reading information from the repository
+     * @see #findNodeInfoForRoot()
+     */
+    NodeInfo findNodeInfo( UUID node,
+                           Path relativePath ) throws ItemNotFoundException, PathNotFoundException, RepositoryException {
+        // The relative path must be normalized ...
+        assert relativePath.isNormalized();
+
+        // Find the node from which we're starting ...
+        NodeInfo fromInfo = null;
+        if (node == null) {
+            // This is only valid when the path is relative to the root (or it's an absolute path)
+            fromInfo = findNodeInfoForRoot();
+            node = fromInfo.getUuid();
+        } else {
+            fromInfo = findNodeInfo(node);
+            assert relativePath.isAbsolute() ? node == root : true;
+        }
+        if (relativePath.isAbsolute()) {
+            relativePath = relativePath.relativeTo(this.rootPath);
+        }
+
+        // If the relative path is of zero-length ...
+        if (relativePath.size() == 0) {
+            return fromInfo;
+        }
+        // Or it is of length 1 but it is a self reference ...
+        if (relativePath.size() == 1 && relativePath.getLastSegment().isSelfReference()) {
+            return fromInfo;
+        }
+
+        // TODO: This could be more efficient than always walking the path. For example, we could
+        // maintain a cache of paths. Right now, we are walking as much of the path as we can,
+        // but as soon as we reach the bottom-most cached/changed node, we need to read the rest
+        // from the graph. We are figuring out all of the remaining nodes and read them from
+        // the graph in one batch operation, so that part is pretty good.
+
+        // Now, walk the path to find the nodes, being sure to look for changed information ...
+        NodeInfo info = fromInfo;
+        Iterator<Path.Segment> pathsIter = relativePath.iterator();
+        while (pathsIter.hasNext()) {
+            Path.Segment child = pathsIter.next();
+            if (child.isParentReference()) {
+                // Walk up ...
+                UUID parentUuid = info.getParent();
+                if (parentUuid == null) {
+                    assert info.getUuid() == findNodeInfoForRoot().getUuid();
+                    String msg = JcrI18n.errorWhileFindingNodeWithPath.text(relativePath, workspaceName);
+                    throw new PathNotFoundException(msg);
+                }
+                info = findNodeInfo(parentUuid);
+            } else {
+                // Walk down ...
+                // Note that once we start walking down, a normalized path should never have any more parent
+                // or self references
+                ChildNode childNodeInfo = info.getChildren().getChild(child);
+                if (childNodeInfo == null) {
+                    // The node (no longer?) exists, so compute the
+                    Path fromPath = getPathFor(fromInfo);
+                    String msg = JcrI18n.pathNotFoundRelativeTo.text(relativePath, fromPath, workspaceName);
+                    throw new PathNotFoundException(msg);
+                }
+                // See if we already have something in the changed nodes ...
+                UUID uuid = childNodeInfo.getUuid();
+                NodeInfo childInfo = changedNodes.get(uuid);
+                if (childInfo == null) {
+                    // Or in the cache ...
+                    childInfo = cachedNodes.get(uuid);
+                    if (childInfo == null) {
+                        // At this point, we've reached the bottom of the nodes that we have locally.
+                        // Get the actual location of the last 'info', since all paths will be relative to it...
+                        Location actualLocation = info.getOriginalLocation();
+                        Path actualPath = actualLocation.getPath();
+                        Path nextPath = pathFactory.create(actualPath, child);
+                        if (pathsIter.hasNext()) {
+                            // There are multiple remaining paths, so load them all in one batch operation,
+                            // starting at the top-most path (the one we're currently at)...
+                            List<Path> pathsInBatch = new LinkedList<Path>();
+                            Results batchResults = null;
+                            try {
+                                Graph.Batch batch = store.batch();
+                                batch.read(nextPath);
+                                pathsInBatch.add(nextPath);
+                                while (pathsIter.hasNext()) {
+                                    child = pathsIter.next();
+                                    nextPath = pathFactory.create(nextPath, child);
+                                    batch.read(nextPath);
+                                    pathsInBatch.add(nextPath);
+                                }
+                                batchResults = batch.execute();
+                            } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+                                Path fromPath = getPathFor(fromInfo);
+                                throw new PathNotFoundException(JcrI18n.pathNotFoundRelativeTo.text(relativePath,
+                                                                                                    fromPath,
+                                                                                                    workspaceName));
+                            } catch (RepositorySourceException e) {
+                                throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithUuid.text(uuid,
+                                                                                                         workspaceName,
+                                                                                                         e.getLocalizedMessage()));
+                            }
+                            // Now process all of the nodes that we loaded, again starting at the top and going down ...
+                            for (Path batchPath : pathsInBatch) {
+                                org.jboss.dna.graph.Node dnaNode = batchResults.getNode(batchPath);
+                                childInfo = createNodeInfoFrom(dnaNode, info);
+                                this.cachedNodes.put(childInfo.getUuid(), childInfo);
+                                info = childInfo;
+                            }
+                        } else {
+                            // This is the last path, so do it a little more efficiently than above ...
+                            childInfo = loadFromGraph(nextPath, info);
+                            info = childInfo;
+                        }
+                    } else {
+                        info = childInfo;
+                    }
+                } else {
+                    info = childInfo;
+                }
+            }
+        }
+        return info;
+    }
+
+    /**
+     * Find the property information with the given identifier. If the property is not yet loaded into the cache, the node (and
+     * its properties) will be read from the repository.
+     * 
+     * @param propertyId the identifier for the property; may not be null
+     * @return the property information, or null if the node does not contain the specified property
+     * @throws PathNotFoundException if the node containing this property does not exist
+     * @throws RepositoryException if there is an error while obtaining the information
+     */
+    PropertyInfo findPropertyInfo( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+        NodeInfo info = findNodeInfo(propertyId.getNodeId());
+        return info.getProperty(propertyId.getPropertyName());
+    }
+
+    Path getPathFor( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+        if (uuid == root) return rootPath;
+        return getPathFor(findNodeInfo(uuid));
+    }
+
+    Path getPathFor( NodeInfo info ) throws ItemNotFoundException, RepositoryException {
+        if (info != null && info.getUuid() == root) return rootPath;
+        LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+        while (info != null) {
+            UUID parent = info.getParent();
+            if (parent == null) break;
+            NodeInfo parentInfo = findNodeInfo(parent);
+            ChildNode child = parentInfo.getChildren().getChild(info.getUuid());
+            if (child == null) break;
+            Path.Segment segment = pathFactory.createSegment(child.getName(), child.getSnsIndex());
+
+            segments.addFirst(segment);
+            info = parentInfo;
+        }
+        return pathFactory.createAbsolutePath(segments);
+    }
+
+    Path getPathFor( PropertyInfo propertyInfo ) throws ItemNotFoundException, RepositoryException {
+        Path nodePath = getPathFor(propertyInfo.getNodeUuid());
+        return pathFactory.create(nodePath, propertyInfo.getPropertyName());
+    }
+
+    Path getPathFor( PropertyId propertyId ) throws ItemNotFoundException, RepositoryException {
+        return getPathFor(findPropertyInfo(propertyId));
+    }
+
+    protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+        findNodeInfoForRoot();
+        if (nodeUuid == root) return nameFactory.create("");
+        // Get the parent ...
+        NodeInfo info = findNodeInfo(nodeUuid);
+        NodeInfo parent = findNodeInfo(info.getParent());
+        ChildNode child = parent.getChildren().getChild(info.getUuid());
+        return child.getName();
+    }
+
+    protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+        findNodeInfoForRoot();
+        if (nodeUuid == root) return 1;
+        // Get the parent ...
+        NodeInfo info = findNodeInfo(nodeUuid);
+        NodeInfo parent = findNodeInfo(info.getParent());
+        ChildNode child = parent.getChildren().getChild(info.getUuid());
+        return child.getSnsIndex();
+    }
+
+    /**
+     * Load from the underlying repository graph the information for the node with the supplied UUID. This method returns the
+     * information for the requested node (after placing it in the cache), but this method may (at its discretion) also load and
+     * cache information for other nodes.
+     * <p>
+     * Note that this method does not check the cache before loading from the repository graph.
+     * </p>
+     * 
+     * @param uuid the UUID of the node; may not be null
+     * @param parentInfo the parent information; may be null if not known
+     * @return the information for the node
+     * @throws ItemNotFoundException if the node does not exist in the repository
+     * @throws RepositoryException if there was an error obtaining this information from the repository
+     */
+    protected NodeInfo loadFromGraph( UUID uuid,
+                                      NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
+        // Load the node information from the store ...
+        try {
+            org.jboss.dna.graph.Node node = store.getNodeAt(uuid);
+            NodeInfo info = createNodeInfoFrom(node, parentInfo);
+            this.cachedNodes.put(info.getUuid(), info);
+            return info;
+        } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+            throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspaceName, e.getLocalizedMessage()));
+        } catch (RepositorySourceException e) {
+            throw new RepositoryException(
+                                          JcrI18n.errorWhileFindingNodeWithUuid.text(uuid, workspaceName, e.getLocalizedMessage()));
+        }
+    }
+
+    /**
+     * Load from the underlying repository graph the information for the node with the supplied UUID. This method returns the
+     * information for the requested node (after placing it in the cache), but this method may (at its discretion) also load and
+     * cache information for other nodes.
+     * <p>
+     * Note that this method does not check the cache before loading from the repository graph.
+     * </p>
+     * 
+     * @param path the path to the node; may not be null
+     * @param parentInfo the parent information; may be null if not known
+     * @return the information for the node
+     * @throws PathNotFoundException if the node does not exist in the repository
+     * @throws RepositoryException if there was an error obtaining this information from the repository
+     */
+    protected NodeInfo loadFromGraph( Path path,
+                                      NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
+        // Load the node information from the store ...
+        try {
+            org.jboss.dna.graph.Node node = store.getNodeAt(path);
+            NodeInfo info = createNodeInfoFrom(node, parentInfo);
+            this.cachedNodes.put(info.getUuid(), info);
+            return info;
+        } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+            throw new PathNotFoundException(JcrI18n.pathNotFound.text(path, workspaceName));
+        } catch (RepositorySourceException e) {
+            throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithPath.text(path, workspaceName));
+        }
+    }
+
+    /**
+     * Create the {@link NodeInfo} object given the DNA graph node and the parent node information (if it is available).
+     * 
+     * @param graphNode the DNA graph node; may not be null
+     * @param parentInfo the information for the parent node, or null if the supplied graph node represents the root node, or if
+     *        the parent information is not known
+     * @return the node information; never null
+     * @throws RepositoryException if there is an error determining the child {@link NodeDefinition} for the supplied node,
+     *         preventing the node information from being constructed
+     */
+    private NodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
+                                         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) {
+            // Check for an identification property ...
+            uuidProperty = location.getIdProperty(JcrLexicon.UUID);
+            if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+        }
+        if (uuidProperty == null) {
+            uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
+            if (uuidProperty != null) {
+                // Grab the first 'good' UUID value ...
+                for (Object uuidValue : uuidProperty) {
+                    try {
+                        uuid = factories.getUuidFactory().create(uuidValue);
+                        break;
+                    } catch (ValueFormatException e) {
+                        // Ignore; just continue with the next property value
+                    }
+                }
+            }
+            if (uuid == null) {
+                // Look for the DNA UUID property ...
+                org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
+                if (dnaUuidProperty != null) {
+                    // Grab the first 'good' UUID value ...
+                    for (Object uuidValue : dnaUuidProperty) {
+                        try {
+                            uuid = factories.getUuidFactory().create(uuidValue);
+                            break;
+                        } catch (ValueFormatException e) {
+                            // Ignore; just continue with the next property value
+                        }
+                    }
+                }
+            }
+        }
+        if (uuid == null) uuid = UUID.randomUUID();
+        if (uuidProperty == null) uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
+
+        // Either the UUID is not known, or there was no node. Either way, we have to create the node ...
+        if (uuid == null) uuid = UUID.randomUUID();
+
+        // Look for the primary type of the node ...
+        Map<Name, Property> graphProperties = graphNode.getPropertiesByName();
+        final boolean isRoot = location.getPath().isRoot();
+        Name primaryTypeName = null;
+        org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+        if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
+            try {
+                primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
+            } catch (ValueFormatException e) {
+                // use the default ...
+            }
+        }
+        if (primaryTypeName == null) {
+            // We have to have a primary type, so use the default ...
+            if (isRoot) {
+                primaryTypeName = DnaLexicon.ROOT;
+                primaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
+            } else {
+                primaryTypeName = defaultPrimaryTypeName;
+                primaryTypeProperty = defaultPrimaryTypeProperty;
+            }
+            // We have to add this property to the graph node...
+            graphProperties = new HashMap<Name, Property>(graphProperties);
+            graphProperties.put(primaryTypeProperty.getName(), primaryTypeProperty);
+        }
+        assert primaryTypeProperty != null;
+        assert primaryTypeProperty.isEmpty() == false;
+
+        // Look for a node definition stored on the node ...
+        JcrNodeDefinition definition = null;
+        org.jboss.dna.graph.property.Property nodeDefnProperty = graphProperties.get(DnaLexicon.NODE_DEFINITON);
+        if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
+            String nodeDefinitionString = stringFactory.create(nodeDefnProperty.getFirstValue());
+            NodeDefinitionId id = NodeDefinitionId.fromString(nodeDefinitionString, nameFactory);
+            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());
+                }
+                Name childName = path.getLastSegment().getName();
+                definition = findNodeDefinitionForChild(parentInfo, childName, primaryTypeName);
+                if (definition == null) {
+                    String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
+                    throw new RepositorySourceException(msg);
+                }
+            }
+        }
+
+        // Create the node information ...
+        NodeInfo info = new NodeInfo(location, primaryTypeName, definition.getId());
+        if (parentInfo != null) {
+            info.setParent(parentInfo.getUuid());
+        }
+
+        // --------------------------------------------------
+        // Set the node's children ...
+        // --------------------------------------------------
+        info.setChildren(graphNode.getChildren());
+
+        // ------------------------------------------------------
+        // Set the node's properties ...
+        // ------------------------------------------------------
+        // First get the property type for each property, based upon the primary type and mixins ...
+        // The map with single-valued properties...
+        Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+        // ... and the map with multi-valued properties
+        Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+
+        boolean referenceable = false;
+
+        List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
+        // Start with the primary type ...
+        NodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+            String nameString = propertyDefn.getName();
+            if ("*".equals(nameString)) {
+                anyPropertyDefinitions.add(propertyDefn);
+                continue;
+            }
+            Name name = nameFactory.create(nameString);
+
+            if (propertyDefn.isMultiple()) {
+                PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+            } else {
+                PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+            }
+        }
+        // The process the mixin types ...
+        org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
+        if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
+            for (Object mixinTypeValue : mixinTypesProperty) {
+                Name mixinTypeName = nameFactory.create(mixinTypeValue);
+                if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
+                String mixinTypeNameString = mixinTypeName.getString(namespaces);
+                NodeType mixinType = nodeTypes().getNodeType(mixinTypeNameString);
+                for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
+                    String nameString = propertyDefn.getName();
+                    if ("*".equals(nameString)) {
+                        anyPropertyDefinitions.add(propertyDefn);
+                        continue;
+                    }
+                    Name name = nameFactory.create(nameString);
+                    if (propertyDefn.isMultiple()) {
+                        PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                        if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+                    } else {
+                        PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+                        if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+                    }
+                }
+            }
+        }
+
+        // Now create the JCR property object wrappers around the other properties ...
+        for (org.jboss.dna.graph.property.Property dnaProp : graphProperties.values()) {
+            Name name = dnaProp.getName();
+
+            // Figure out the JCR property type for this property ...
+            PropertyDefinition propertyDefinition;
+            if (dnaProp.isMultiple()) {
+                propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+            } else {
+                propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);
+
+                // If the property has only one value, dnaProp.isMultiple() will return false, but the
+                // property may actually be a multi-valued property that happens to have one property set.
+                if (propertyDefinition == null) {
+                    propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+                }
+            }
+
+            // If no property type was found for this property, see if there is a wildcard property ...
+            if (propertyDefinition == null) {
+                for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext();) {
+                    PropertyDefinition nextDef = iter.next();
+
+                    // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
+                    if ((nextDef.isMultiple() && dnaProp.isMultiple()) || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
+                        propertyDefinition = nextDef;
+                        break;
+                    }
+                }
+            }
+
+            // If there still is no property type defined ...
+            if (propertyDefinition == null) {
+                assert anyPropertyDefinitions.isEmpty();
+                if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+                    // We can use the "nt:unstructured" property definitions for any property ...
+                    NodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
+                    for (PropertyDefinition anyDefinition : unstructured.getDeclaredPropertyDefinitions()) {
+                        if (anyDefinition.isMultiple()) {
+                            propertyDefinition = anyDefinition;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (propertyDefinition == null) {
+                // We're supposed to skip this property (since we don't have a definition for it) ...
+                continue;
+            }
+
+            // Figure out if this is a multi-valued property ...
+            boolean isMultiple = propertyDefinition.isMultiple();
+            if (!isMultiple && dnaProp.isEmpty()) {
+                // Only multi-valued properties can have no values; so if not multi-valued, then skip ...
+                continue;
+            }
+
+            // Figure out the property type ...
+            int propertyType = propertyDefinition.getRequiredType();
+            if (propertyType == PropertyType.UNDEFINED) {
+                propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+            }
+
+            // Record the property in the node information ...
+            PropertyId propId = new PropertyId(uuid, name);
+            info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, propertyType, dnaProp);
+        }
+
+        // Now add the "jcr:uuid" property if and only if referenceable ...
+        if (referenceable) {
+            // We know that this property is single-valued
+            PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
+            PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
+            info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, PropertyType.STRING, uuidProperty);
+        } else {
+            // Make sure there is NOT a "jcr:uuid" property ...
+            info.removeProperty(JcrLexicon.UUID);
+        }
+        // Make sure the "dna:uuid" property did not get in there ...
+        info.removeProperty(DnaLexicon.UUID);
+
+        return info;
+    }
+
+    /**
+     * Utility method to find the {@link NodeDefinition} for a child node with the supplied {@link Name name}, parent information,
+     * and the primary node type of the child.
+     * 
+     * @param parentInfo the parent information; may not be null
+     * @param childName the name of the child node (without any same-name-sibling index); may not be null
+     * @param primaryTypeOfChild the name of the child's primary type
+     * @return the node definition for this child, as best as can be determined, or null if the node definition could not be
+     *         determined
+     * @throws RepositoryException if the parent's pimary node type cannot be found in the {@link NodeTypeManager}
+     */
+    protected JcrNodeDefinition findNodeDefinitionForChild( NodeInfo parentInfo,
+                                                            Name childName,
+                                                            Name primaryTypeOfChild ) throws RepositoryException {
+        // Get the primary type of the parent, and look at it's child definitions ...
+        Name primaryTypeName = parentInfo.getPrimaryTypeName();
+        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        if (primaryType == null) {
+            String msg = JcrI18n.missingNodeTypeForExistingNode.text(primaryTypeName, parentInfo.getUuid(), workspaceName);
+            throw new RepositoryException(msg);
+        }
+        // TODO: should this also check the mixins?
+        return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
+    }
+
+    /**
+     * The information that describes a node. This is the information that is kept in the {@link SessionCache#cachedNodes cache}
+     * and in the record of {@link SessionCache#changedNodes changes} made by the session but not yet commited/saved.
+     */
+    @NotThreadSafe
+    protected class NodeInfo {
+        private final Location originalLocation;
+        private final UUID uuid;
+        private UUID parent;
+        private final Name primaryTypeName;
+        private final NodeDefinitionId definition;
+        private final Children children;
+        private final Map<Name, PropertyInfo> properties;
+
+        protected NodeInfo( Location originalLocation,
+                            Name primaryTypeName,
+                            NodeDefinitionId definition ) {
+            this.originalLocation = originalLocation;
+            this.primaryTypeName = primaryTypeName;
+            this.definition = definition;
+            this.uuid = this.originalLocation.getUuid();
+            this.children = new Children(this.uuid);
+            this.properties = new HashMap<Name, PropertyInfo>();
+            assert this.uuid != null;
+            assert this.definition != null;
+            assert this.primaryTypeName != null;
+        }
+
+        /**
+         * @return location
+         */
+        public Location getOriginalLocation() {
+            return originalLocation;
+        }
+
+        /**
+         * @return uuid
+         */
+        public UUID getUuid() {
+            return uuid;
+        }
+
+        /**
+         * @return parent
+         */
+        public UUID getParent() {
+            return parent;
+        }
+
+        /**
+         * @param parent Sets parent to the specified value.
+         */
+        protected void setParent( UUID parent ) {
+            this.parent = parent;
+        }
+
+        /**
+         * @return primaryTypeName
+         */
+        public Name getPrimaryTypeName() {
+            return primaryTypeName;
+        }
+
+        /**
+         * @return definition
+         */
+        public NodeDefinitionId getDefinitionId() {
+            return definition;
+        }
+
+        /**
+         * Get the children for this node.
+         * 
+         * @return the children; never null but possibly empty
+         */
+        public Children getChildren() {
+            return children;
+        }
+
+        /**
+         * @param children Sets children to the specified value.
+         * @return the children information; never null
+         */
+        public Children setChildren( List<Location> children ) {
+            this.children.append(SessionCache.this, children, true);
+            return this.children;
+        }
+
+        /**
+         * Get the map of information for each property.
+         * 
+         * @return the information for each property; never null but possibly (though unlikely) empty
+         */
+        public Map<Name, PropertyInfo> getProperties() {
+            return this.properties; // never null
+        }
+
+        public PropertyInfo getProperty( Name name ) {
+            return this.properties.get(name);
+        }
+
+        public PropertyInfo setProperty( PropertyId id,
+                                         JcrPropertyDefinition definition,
+                                         int propertyType,
+                                         Property dnaProperty ) {
+            // Initialize the map if required (this never replaces it, though) ...
+            PropertyInfo info = new PropertyInfo(id, definition.getId(), propertyType, dnaProperty, definition.isMultiple());
+            return this.properties.put(id.getPropertyName(), info);
+        }
+
+        public PropertyInfo removeProperty( Name name ) {
+            return this.properties.remove(name);
+        }
+    }
+
+    /**
+     * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
+     * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
+     * type}.
+     * <p>
+     * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an
+     * instance by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their
+     * reference. This is because these instances are replaced and discarded whenever anything about the property changes.
+     * </p>
+     */
+    @Immutable
+    public static class PropertyInfo {
+        private final PropertyId propertyId;
+        private final PropertyDefinitionId definitionId;
+        private final Property dnaProperty;
+        private final int propertyType;
+        private final boolean multiValued;
+
+        protected PropertyInfo( PropertyId propertyId,
+                                PropertyDefinitionId definitionId,
+                                int propertyType,
+                                Property dnaProperty,
+                                boolean multiValued ) {
+            this.propertyId = propertyId;
+            this.definitionId = definitionId;
+            this.propertyType = propertyType;
+            this.dnaProperty = dnaProperty;
+            this.multiValued = multiValued;
+        }
+
+        /**
+         * Get the durable identifier for this property.
+         * 
+         * @return propertyId
+         */
+        public PropertyId getPropertyId() {
+            return propertyId;
+        }
+
+        /**
+         * Get the UUID of the node to which this property belongs.
+         * 
+         * @return the owner node's UUID; never null
+         */
+        public UUID getNodeUuid() {
+            return propertyId.getNodeId();
+        }
+
+        /**
+         * The identifier for the property definition.
+         * 
+         * @return the property definition ID; never null
+         */
+        public PropertyDefinitionId getDefinitionId() {
+            return definitionId;
+        }
+
+        /**
+         * Get the DNA Property, which contains the name and value(s)
+         * 
+         * @return the property; never null
+         */
+        public Property getProperty() {
+            return dnaProperty;
+        }
+
+        /**
+         * Get the property name.
+         * 
+         * @return the property name; never null
+         */
+        public Name getPropertyName() {
+            return dnaProperty.getName();
+        }
+
+        /**
+         * Get the JCR {@link PropertyType} for this property.
+         * 
+         * @return the property type
+         */
+        public int getPropertyType() {
+            return propertyType;
+        }
+
+        /**
+         * @return multiValued
+         */
+        public boolean isMultiValued() {
+            return multiValued;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return propertyId.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof PropertyInfo) {
+                return propertyId.equals(((PropertyInfo)obj).getPropertyId());
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(propertyId);
+            sb.append(" defined by ").append(definitionId);
+            sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
+            if (dnaProperty.isSingle()) {
+                sb.append(" with value ");
+            } else {
+                sb.append(" with values ");
+            }
+            sb.append(dnaProperty.getValuesAsArray());
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Class that maintains the ordered list of {@link ChildNode} instances. This class uses a {@link ListMultimap} to maintain
+     * insertion order of the child nodes, and to allow fast access to the children with a specified name.
+     */
+    @ThreadSafe
+    public final static class Children implements Iterable<ChildNode> {
+        private final UUID parentUuid;
+        private final Map<UUID, ChildNode> childrenByUuid;
+        private final ListMultimap<Name, ChildNode> childrenByName;
+        private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+        Children( UUID parentUuid ) {
+            this.parentUuid = parentUuid;
+            this.childrenByUuid = new HashMap<UUID, ChildNode>();
+            this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+        }
+
+        /**
+         * Get the number of children.
+         * 
+         * @return the number of children
+         */
+        public int size() {
+            try {
+                lock.readLock().lock();
+                return childrenByName.size();
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Get the children.
+         * 
+         * @return a copy of the list of children
+         */
+        public List<ChildNode> asList() {
+            try {
+                lock.readLock().lock();
+                return new ArrayList<ChildNode>(childrenByName.values());
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Get the children.
+         * 
+         * @return a copy of the list of children
+         */
+        public Iterator<ChildNode> iterator() {
+            return asList().iterator();
+        }
+
+        /**
+         * The UUID of the parent node.
+         * 
+         * @return the parent node's UUID
+         */
+        public UUID getParentUuid() {
+            return parentUuid;
+        }
+
+        /**
+         * Get the child with the given UUID.
+         * 
+         * @param uuid the UUID of the child node
+         * @return the child node, or null if there is no child with the supplied UUID
+         */
+        public ChildNode getChild( UUID uuid ) {
+            try {
+                lock.readLock().lock();
+                return this.childrenByUuid.get(uuid);
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Get the child given the path segment.
+         * 
+         * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+         *        {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+         * @return the information for the child node, or null if no such child existed
+         */
+        public ChildNode getChild( Path.Segment segment ) {
+            try {
+                lock.readLock().lock();
+                List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+                int snsIndex = segment.getIndex();
+                if (childrenWithName.size() < snsIndex) return null;
+                return childrenWithName.get(snsIndex - 1);
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Get the same-name-sibling children that all share the supplied name.
+         * 
+         * @param name the name for the children; may not be null
+         * @return the children with the supplied name; never null
+         */
+        public List<ChildNode> getChildren( Name name ) {
+            try {
+                lock.readLock().lock();
+                return new ArrayList<ChildNode>(this.childrenByName.get(name));
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Remove the child with the given path segment.
+         * 
+         * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+         *        {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+         * @return the information for the child node that was removed, or null if no such child existed
+         */
+        public ChildNode remove( Path.Segment segment ) {
+            try {
+                lock.writeLock().lock();
+                List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+                int snsIndex = segment.getIndex();
+                int numChildrenWithName = childrenWithName.size();
+                if (numChildrenWithName < snsIndex) return null;
+                ChildNode result = childrenWithName.remove(snsIndex);
+                this.childrenByUuid.remove(result.getUuid());
+                --numChildrenWithName;
+                if (numChildrenWithName > snsIndex) {
+                    // We need to reduce the SNS index of every child after the one that was just removed ...
+                    ListIterator<ChildNode> siblingIter = childrenWithName.listIterator(snsIndex);
+                    while (siblingIter.hasNext()) {
+                        // Remove the next child and replace with one having the correct SNS index ...
+                        ChildNode next = siblingIter.next();
+                        siblingIter.remove();
+                        siblingIter.set(next.withChangedSnsIndex(-1));
+                    }
+                }
+                return result;
+            } finally {
+                lock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * Add a new child to the end of the list of existing children a new child with the supplied name and UUID. The
+         * same-name-sibling will be determined to be one more than the number of existing children with the same name.
+         * 
+         * @param cache the cache; may not be null
+         * @param name the name for the child to be appended; may not be null
+         * @param childUuid the UUID of the child; may not be null
+         * @return the information for the newly-added child; never null
+         */
+        public ChildNode append( SessionCache cache,
+                                 Name name,
+                                 UUID childUuid ) {
+            try {
+                lock.writeLock().lock();
+                List<ChildNode> childrenWithName = this.childrenByName.get(name);
+                ChildNode child = new ChildNode(childUuid, name, childrenWithName.size() + 1);
+                childrenWithName.add(child);
+                this.childrenByUuid.put(childUuid, child);
+                // Look for the child in the cache/changed nodes ...
+                NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
+                if (childInfo != null) childInfo.setParent(parentUuid);
+                return child;
+            } finally {
+                lock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * Append the children described by the supplied Location objects, optionally removing all existing children first.
+         * 
+         * @param cache the cache; may not be null
+         * @param children a list containing a Location object for each child
+         * @param removeExistingFirst true if the existing children should be removed before these children are added, or false if
+         *        these children should be appended to the existing children
+         */
+        public void append( SessionCache cache,
+                            List<Location> children,
+                            boolean removeExistingFirst ) {
+            try {
+                lock.writeLock().lock();
+                if (removeExistingFirst && !this.childrenByName.isEmpty()) {
+                    for (ChildNode child : this.childrenByName.values()) {
+                        // Look for the child in the cache/changed nodes ...
+                        NodeInfo childInfo = cache.findNodeInfoInCache(child.getUuid());
+                        if (childInfo != null) childInfo.setParent(null);
+                        // TODO: These nodes are deleted and should be handled as deletes ...
+                    }
+                    this.childrenByUuid.clear();
+                    this.childrenByName.clear();
+                }
+                for (Location childLocation : children) {
+                    UUID childUuid = childLocation.getUuid();
+                    Path.Segment segment = childLocation.getPath().getLastSegment();
+                    Name name = segment.getName();
+                    ChildNode child = new ChildNode(childUuid, name, segment.getIndex());
+                    this.childrenByName.put(name, child);
+                    this.childrenByUuid.put(childUuid, child);
+                    // Look for the child in the cache/changed nodes ...
+                    NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
+                    if (childInfo != null) childInfo.setParent(parentUuid);
+                }
+            } finally {
+                lock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            try {
+                lock.readLock().lock();
+                boolean first = true;
+                for (ChildNode child : childrenByName.values()) {
+                    if (!first) sb.append(", ");
+                    else first = false;
+                    sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
+                }
+            } finally {
+                lock.readLock().unlock();
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded.
+     * Clients should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects
+     * and replacing them with new instances.
+     */
+    @Immutable
+    public final static class ChildNode {
+        private final UUID uuid;
+        private final Name name;
+        private final int snsIndex;
+
+        protected ChildNode( UUID uuid,
+                             Name name,
+                             int snsIndex ) {
+            this.uuid = uuid;
+            this.name = name;
+            this.snsIndex = snsIndex;
+            assert this.snsIndex > 0;
+        }
+
+        /**
+         * Get the UUID of the node.
+         * 
+         * @return the node's UUID; never null
+         */
+        public UUID getUuid() {
+            return uuid;
+        }
+
+        /**
+         * Get the name of the node.
+         * 
+         * @return the node's current name; never null
+         */
+        public Name getName() {
+            return name;
+        }
+
+        /**
+         * Get the same-name-sibling index of the node.
+         * 
+         * @return the node's SNS index; always positive
+         */
+        public int getSnsIndex() {
+            return snsIndex;
+        }
+
+        /**
+         * Return a new child node that has a changed SNS index.
+         * 
+         * @param delta the amount the change the SNS index, either positive to increase the value or negative to decrease the
+         *        value
+         * @return the copy of this, with a changed SNS index; never null
+         */
+        public ChildNode withChangedSnsIndex( int delta ) {
+            return new ChildNode(uuid, name, snsIndex + delta);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return uuid.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof ChildNode) {
+                return this.uuid.equals(((ChildNode)obj).uuid);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return uuid.toString() + " ( " + name + "[" + snsIndex + "] )";
+        }
+    }
+
+}


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

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-16 22:50:09 UTC (rev 777)
@@ -28,7 +28,8 @@
 defaultWorkspaceName=
 inputStreamConsumed = This value was already consumed as an input stream.
 nonInputStreamConsumed = This value was already consumed as a non-input stream.
-pathNotFound = No item exists at path {0}
+pathNotFound = No item exists at path {0} in workspace "{1}"
+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.
@@ -51,9 +52,19 @@
 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
-itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}"
-errorWhileFindingNodeWithUuid = Error while finding the item with UUID "{0}" in workspace "{1}"
+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}"
+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}"
+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}"
+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}"
+missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
 
 REP_NAME_DESC = DNA Repository
 REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -27,24 +27,36 @@
 import static org.junit.Assert.assertThat;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
-import javax.jcr.Session;
+import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
 
-/**
- * @author jverhaeg
- */
 public class AbstractJcrItemTest {
 
     private AbstractJcrItem item;
+    @Mock
+    private SessionCache cache;
 
     @Before
     public void before() {
-        item = new AbstractJcrItem() {
+        MockitoAnnotations.initMocks(this);
+        item = new AbstractJcrItem(cache) {
 
             public void accept( ItemVisitor visitor ) {
             }
 
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.jcr.AbstractJcrItem#path()
+             */
+            @Override
+            Path path() {
+                throw new UnsupportedOperationException();
+            }
+
             public String getName() {
                 return null;
             }
@@ -57,10 +69,6 @@
                 return null;
             }
 
-            public Session getSession() {
-                return null;
-            }
-
             public boolean isNode() {
                 return false;
             }

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -25,13 +25,13 @@
 
 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.io.InputStream;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -39,20 +39,23 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
+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;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -64,36 +67,16 @@
  */
 public class AbstractJcrNodeTest {
 
-    static MockAbstractJcrNode createChild( JcrSession session,
-                                            String name,
-                                            int index,
-                                            List<Location> children,
-                                            AbstractJcrNode parent ) throws Exception {
-        MockAbstractJcrNode child = new MockAbstractJcrNode(session, name, parent);
-        String parentPath = parent.getPath();
-        String childPath = parentPath + "/" + name + "[" + index + "]";
-        Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(childPath);
-        Location location = Location.create(path, UUID.randomUUID());
-        children.add(location);
-        // Stub the session to return this node ...
-        String absolutePath = path.getString(session.getExecutionContext().getNamespaceRegistry());
-        stub(session.getChild(parent, location)).toReturn(child);
-        stub(session.getNode(location.getUuid())).toReturn(child);
-        stub(session.getItem(absolutePath)).toReturn(child);
-        return child;
-    }
-
     static class MockAbstractJcrNode extends AbstractJcrNode {
 
-        String name;
-        Node parent;
+        MockAbstractJcrNode( SessionCache cache,
+                             UUID uuid ) {
+            super(cache, uuid);
+        }
 
-        MockAbstractJcrNode( JcrSession session,
-                             String name,
-                             Node parent ) {
-            super(session, Location.create(UUID.randomUUID()), mock(NodeDefinition.class));
-            this.name = name;
-            this.parent = parent;
+        @Override
+        boolean isRoot() {
+            return false;
         }
 
         @Override
@@ -105,45 +88,67 @@
             return 0;
         }
 
-        public String getName() {
-            return name;
+        public String getName() throws RepositoryException {
+            return cache.getPathFor(nodeInfo()).getLastSegment().getString(namespaces());
         }
 
-        public Node getParent() {
-            return parent;
+        public Node getParent() throws RepositoryException {
+            return cache.findJcrNode(nodeInfo().getParent());
         }
 
         public String getPath() throws RepositoryException {
-            return (parent == null ? '/' + getName() : parent.getPath() + '/' + getName());
+            return cache.getPathFor(nodeInfo()).getString(namespaces());
         }
     }
 
+    private ExecutionContext context;
+    private UUID uuid;
     private AbstractJcrNode node;
+    private Children children;
     @Mock
+    private SessionCache cache;
+    @Mock
     private JcrSession session;
     @Mock
-    private Workspace workspace;
+    private JcrNodeTypeManager nodeTypes;
     @Mock
-    private Repository repository;
-    private List<Location> children;
-    private Map<Name, Property> properties;
-    private ExecutionContext context;
+    private NodeInfo info;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         context = new ExecutionContext();
-        stub(session.getExecutionContext()).toReturn(context);
-        children = new ArrayList<Location>();
-        properties = new HashMap<Name, Property>();
-        node = new MockAbstractJcrNode(session, "node", null);
-        node.setProperties(properties);
+        context.getNamespaceRegistry().register("acme", "http://www.example.com");
+        uuid = UUID.randomUUID();
+        stub(cache.session()).toReturn(session);
+        stub(cache.context()).toReturn(context);
+        stub(cache.workspaceName()).toReturn("my workspace");
+        stub(cache.findNodeInfo(uuid)).toReturn(info);
+        stub(cache.getPathFor(info)).toReturn(path("/a/b/c/d"));
+        stub(session.nodeTypeManager()).toReturn(nodeTypes);
+        node = new MockAbstractJcrNode(cache, uuid);
+
+        // Create the children container for the node ...
+        children = new Children(uuid);
+        stub(info.getChildren()).toReturn(children);
     }
 
     protected Name name( String name ) {
         return context.getValueFactories().getNameFactory().create(name);
     }
 
+    protected Path relativePath( String relativePath ) {
+        return context.getValueFactories().getPathFactory().create(relativePath);
+    }
+
+    protected Path path( String absolutePath ) {
+        return context.getValueFactories().getPathFactory().create(absolutePath);
+    }
+
+    protected Value stringValueFor( Object value ) {
+        return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
+    }
+
     @Test
     public void shouldAllowVisitation() throws Exception {
         ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -156,11 +161,6 @@
         node.accept(null);
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNoSession() throws Exception {
-        new MockAbstractJcrNode(null, null, null);
-    }
-
     @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowNegativeAncestorDepth() throws Exception {
         node.getAncestor(-1);
@@ -172,20 +172,50 @@
     }
 
     @Test
-    public void shouldProvideInternalUuid() throws Exception {
-        UUID uuid = UUID.randomUUID();
-        node.setInternalUuid(uuid);
-        assertThat(node.internalUuid(), is(uuid));
+    public void shouldReturnPropertyFromGetPropertyWithValidName() throws Exception {
+        PropertyId propertyId = new PropertyId(uuid, name("test"));
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrProperty(propertyId)).toReturn(property);
+        assertThat(node.getProperty("test"), is((Property)property));
     }
 
     @Test
-    public void shouldProvideNamedProperty() throws Exception {
-        Property property = Mockito.mock(Property.class);
-        stub(property.getName()).toReturn("test");
-        properties.put(name(property.getName()), property);
-        assertThat(node.getProperty("test"), is(property));
+    public void shouldReturnPropertyFromGetPropertyWithValidRelativePath() throws Exception {
+        PropertyId propertyId = new PropertyId(uuid, name("test"));
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrProperty(propertyId)).toReturn(property);
+        assertThat(node.getProperty("test"), is((Property)property));
     }
 
+    @Test( expected = PathNotFoundException.class )
+    public void shouldFailToReturnPropertyFromGetPropertyWithNameOfPropertyThatDoesNotExist() throws Exception {
+        node.getProperty("nonExistantProperty");
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailToReturnPropertyFromGetPropertyWithAbsolutePath() throws Exception {
+        node.getProperty("/test");
+    }
+
+    @Test( expected = PathNotFoundException.class )
+    public void shouldFailToReturnPropertyFromGetPropertyWithRelativePathToNonExistantItem() throws Exception {
+        node.getProperty("../bogus/path");
+    }
+
+    @Test
+    public void shouldReturnPropertyFromGetPropertyWithRelativePathToPropertyOnOtherNode() throws Exception {
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(property);
+        assertThat(node.getProperty("../good/path"), is((Property)property));
+    }
+
+    @Test( expected = PathNotFoundException.class )
+    public void shouldReturnPropertyFromGetPropertyWithRelativePathToOtherNode() throws Exception {
+        AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+        stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(otherNode);
+        node.getProperty("../good/path");
+    }
+
     @Test( expected = UnsupportedOperationException.class )
     public void shoudNotAllowAddMixin() throws Exception {
         node.addMixin(null);
@@ -241,152 +271,210 @@
         node.getLock();
     }
 
-    @Test
-    public void shouldProvideNode() throws Exception {
-        Node child = createChild(session, "child", 1, children, node);
-        node.setChildren(children);
-        stub(session.getItem("/node/child")).toReturn(child);
-        assertThat(node.getNode("child"), is(child));
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowGetPropertyWithNullPath() throws Exception {
+        node.getProperty((String)null);
     }
 
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowGetNodeWithNoPath() throws Exception {
-        node.getNode(null);
+    public void shouldNotAllowGetPropertyWithEmptyPath() throws Exception {
+        node.getProperty("");
     }
 
+    @Test
+    public void shouldReturnChildNodeFromGetNodeWithValidName() throws Exception {
+        AbstractJcrNode child = mock(AbstractJcrNode.class);
+        UUID childUuid = UUID.randomUUID();
+        children.append(cache, name("child"), childUuid);
+        stub(cache.findJcrNode(childUuid)).toReturn(child);
+        assertThat(node.getNode("child"), is((Node)child));
+    }
+
+    @Test
+    public void shouldReturnNonChildNodeFromGetNodeWithValidRelativePath() throws Exception {
+        AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+        stub(cache.findJcrItem(uuid, path("../other/node"))).toReturn(otherNode);
+        assertThat(node.getNode("../other/node"), is((Node)otherNode));
+    }
+
     @Test( expected = PathNotFoundException.class )
-    public void shouldNotProvideNodeIfPathNotFound() throws Exception {
-        node.getNode("bogus");
+    public void shouldFailToReturnNodeFromGetNodeWithValidRelativePathToProperty() throws Exception {
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrItem(uuid, path("../other/node"))).toReturn(property);
+        node.getNode("../other/node");
     }
 
     @Test( expected = PathNotFoundException.class )
-    public void shouldNotProvideNodeIfPathIsProperty() throws Exception {
-        Property property = Mockito.mock(Property.class);
-        stub(session.getItem("/property")).toReturn(property);
-        node.getNode("property");
+    public void shouldFailToReturnNodeFromGetNodeWithValidRelativePathToNoNodeOrProperty() throws Exception {
+        node.getNode("../other/node");
     }
 
     @Test
-    public void shouldProvideNodeIterator() throws Exception {
-        assertThat(node.getNodes(), notNullValue());
+    public void shouldReturnSelfFromGetNodeWithRelativePathContainingOnlySelfReference() throws Exception {
+        assertThat(node.getNode("."), is((Node)node));
     }
 
     @Test
-    public void shoudProvidePrimaryItem() throws Exception {
-        Property property = Mockito.mock(Property.class);
-        stub(property.getName()).toReturn("jcr:primaryItemName");
-        stub(property.getString()).toReturn("primaryItem");
-        properties.put(name(property.getName()), property);
-        Item primaryItem = Mockito.mock(Item.class);
-        stub(session.getItem("/node/primaryItem")).toReturn(primaryItem);
-        assertThat(node.getPrimaryItem(), is(primaryItem));
+    public void shouldReturnParentFromGetNodeWithRelativePathContainingOnlyParentReference() throws Exception {
+        AbstractJcrNode parent = mock(AbstractJcrNode.class);
+        UUID parentUuid = UUID.randomUUID();
+        stub(info.getParent()).toReturn(parentUuid);
+        stub(cache.findJcrNode(parentUuid)).toReturn(parent);
+        assertThat(node.getNode(".."), is((Node)parent));
     }
 
-    @Test( expected = ItemNotFoundException.class )
-    public void shoudNotProvidePrimaryItemIfUnavailable() throws Exception {
-        node.getPrimaryItem();
+    @Test( expected = PathNotFoundException.class )
+    public void shouldFailToReturnChildNodeFromGetNodeWithNameOfChildThatDoesNotExist() throws Exception {
+        node.getNode("something");
     }
 
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowGetNodeWithNoPath() throws Exception {
+        node.getNode(null);
+    }
+
     @Test
-    public void shouldProvideProperty() throws Exception {
-        Property prop1 = Mockito.mock(Property.class);
-        stub(prop1.getName()).toReturn("prop1");
-        properties.put(name(prop1.getName()), prop1);
-        assertThat(node.getProperty("prop1"), is(prop1));
-        MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
-        Map<Name, Property> properties = new HashMap<Name, Property>();
-        child.setProperties(properties);
-        Property prop2 = Mockito.mock(Property.class);
-        stub(prop2.getName()).toReturn("prop2");
-        stub(session.getItem("/node/child/prop2")).toReturn(prop2);
-        properties.put(name(prop2.getName()), prop2);
-        MockAbstractJcrNode prop3Node = createChild(session, "prop3", 1, children, child);
-        node.setChildren(children);
-        assertThat(node.getProperty("child/prop2"), is(prop2));
-        // Ensure we return a property even when a child exists with the same name
-        Property prop3 = Mockito.mock(Property.class);
-        stub(prop3.getName()).toReturn("prop3");
-        properties.put(name(prop3.getName()), prop3);
-        stub(session.getItem("/node/child/prop3")).toReturn(prop3Node);
-        assertThat(node.getProperty("child/prop3"), is(prop3));
+    public void shouldProvideNodeIterator() throws Exception {
+        AbstractJcrNode child = mock(AbstractJcrNode.class);
+        UUID childUuid = UUID.randomUUID();
+        children.append(cache, name("child"), childUuid);
+        stub(cache.findJcrNode(childUuid)).toReturn(child);
+        NodeIterator iter = node.getNodes();
+        assertThat(iter, notNullValue());
+        assertThat(iter.getSize(), is(1L));
+        assertThat(iter.next(), is((Object)child));
     }
 
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowGetPropertyWithNullPath() throws Exception {
-        node.getProperty((String)null);
+    @Test
+    public void shoudReturnItemFromGetPrimaryItemIfItExists() throws Exception {
+        // Define the node type ...
+        Name primaryTypeName = name("thePrimaryType");
+        JcrNodeType primaryType = mock(JcrNodeType.class);
+        stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+        stub(primaryType.getPrimaryItemName()).toReturn("thePrimaryItemName");
+        // Define the node info to use this primary type ...
+        stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+        // Now make an item with the appropriate name ...
+        AbstractJcrNode child = mock(AbstractJcrNode.class);
+        stub(cache.findJcrItem(uuid, path("thePrimaryItemName"))).toReturn(child);
+        // Now call the method ...
+        assertThat(node.getPrimaryItem(), is((Item)child));
     }
 
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowGetPropertyWithEmptyPath() throws Exception {
-        node.getProperty("");
+    @Test( expected = ItemNotFoundException.class )
+    public void shoudFailToReturnItemFromGetPrimaryItemIfPrimaryTypeDoesNotHavePrimaryItemName() throws Exception {
+        // Define the node type ...
+        Name primaryTypeName = name("thePrimaryType");
+        JcrNodeType primaryType = mock(JcrNodeType.class);
+        stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+        stub(primaryType.getPrimaryItemName()).toReturn(null);
+        // Define the node info to use this primary type ...
+        stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+        // Now call the method ...
+        node.getPrimaryItem();
     }
 
-    @Test( expected = PathNotFoundException.class )
-    public void shouldNotProvideChildPropertyIfNotAvailable() throws Exception {
-        node.getProperty("prop1");
+    @Test( expected = ItemNotFoundException.class )
+    public void shoudFailToReturnItemFromGetPrimaryItemIfThePrimaryTypeAsInvalidPrimaryItemName() throws Exception {
+        // Define the node type ...
+        Name primaryTypeName = name("thePrimaryType");
+        JcrNodeType primaryType = mock(JcrNodeType.class);
+        stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+        stub(primaryType.getPrimaryItemName()).toReturn("/this/is/not/valid");
+        // Define the node info to use this primary type ...
+        stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+        // Now call the method ...
+        node.getPrimaryItem();
     }
 
-    @Test( expected = PathNotFoundException.class )
-    public void shouldNotProvideDescendentPropertyIfNotAvailable() throws Exception {
-        MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
-        Map<Name, Property> properties = new HashMap<Name, Property>();
-        child.setProperties(properties);
-        MockAbstractJcrNode propNode = createChild(session, "prop", 1, children, child);
-        node.setChildren(children);
-        stub(session.getItem("/node/child/prop")).toReturn(propNode);
-        node.getProperty("child/prop");
+    @Test( expected = ItemNotFoundException.class )
+    public void shoudFailToReturnItemFromGetPrimaryItemIfTheNodeHasNoItemMatchingThatSpecifiedByThePrimaryType() throws Exception {
+        // Define the node type ...
+        Name primaryTypeName = name("thePrimaryType");
+        JcrNodeType primaryType = mock(JcrNodeType.class);
+        stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+        stub(primaryType.getPrimaryItemName()).toReturn("thePrimaryItemName");
+        // Define the node info to use this primary type ...
+        stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+        // Now call the method ...
+        stub(cache.findJcrItem(uuid, path("thePrimaryItemName"))).toThrow(new ItemNotFoundException());
+        node.getPrimaryItem();
     }
 
     @Test
-    public void shouldNotAllowGetReferences() throws Exception {
+    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, is(notNullValue()));
         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 shouldProvideSession() throws Exception {
         assertThat((JcrSession)node.getSession(), is(session));
     }
 
     @Test
     public void shouldProvideUuidIfReferenceable() throws Exception {
-        String uuid = "uuid";
-        Property mixinProp = Mockito.mock(Property.class);
-        stub(mixinProp.getName()).toReturn("jcr:mixinTypes");
-        Value value = Mockito.mock(Value.class);
-        stub(value.getString()).toReturn("mix:referenceable");
-        stub(mixinProp.getValues()).toReturn(new Value[] {value});
-        properties.put(name(mixinProp.getName()), mixinProp);
-        Property uuidProp = Mockito.mock(Property.class);
-        stub(uuidProp.getName()).toReturn("jcr:uuid");
-        stub(uuidProp.getString()).toReturn(uuid);
-        properties.put(name(uuidProp.getName()), uuidProp);
-        assertThat(node.getUUID(), is(uuid));
+        // Create the property ...
+        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrProperty(propertyId)).toReturn(property);
+        stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:referenceable")});
+        // Call the method ...
+        assertThat(node.getUUID(), is(uuid.toString()));
     }
 
     @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
-        String uuid = "uuid";
-        Property mixinProp = Mockito.mock(Property.class);
-        stub(mixinProp.getName()).toReturn("jcr:mixinTypes");
-        Value value = Mockito.mock(Value.class);
-        stub(mixinProp.getValues()).toReturn(new Value[] {value});
-        properties.put(name(mixinProp.getName()), mixinProp);
-        Property uuidProp = Mockito.mock(Property.class);
-        stub(uuidProp.getName()).toReturn("jcr:uuid");
-        stub(uuidProp.getString()).toReturn(uuid);
-        properties.put(name(uuidProp.getName()), uuidProp);
+        // Create the property ...
+        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrProperty(propertyId)).toReturn(property);
+        stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:notReferenceable")});
+        // Call the method ...
         node.getUUID();
     }
 
     @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
-        String uuid = "uuid";
-        Property uuidProp = Mockito.mock(Property.class);
-        stub(uuidProp.getName()).toReturn("jcr:uuid");
-        stub(uuidProp.getString()).toReturn(uuid);
-        properties.put(name(uuidProp.getName()), uuidProp);
+        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+        stub(cache.findJcrProperty(propertyId)).toReturn(null);
+        // Call the method ...
         node.getUUID();
     }
 
@@ -395,76 +483,134 @@
         node.getVersionHistory();
     }
 
-    @Test
-    public void shouldProvideHasNode() throws Exception {
-        assertThat(node.hasNode("{}child"), is(false));
-        Property prop = Mockito.mock(Property.class);
-        stub(prop.getName()).toReturn("prop");
-        properties.put(name(prop.getName()), prop);
-        assertThat(node.hasNode("prop"), is(false));
-        AbstractJcrNode child = createChild(session, "child", 1, children, node);
-        AbstractJcrNode child2 = createChild(session, "child2", 1, children, child);
-        node.setChildren(children);
-        assertThat(node.hasNode("child"), is(true));
-        stub(session.getItem("/node/child/{}child2")).toReturn(child2);
-        assertThat(node.hasNode("child/{}child2"), is(true));
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowNullPathInHasNode() throws Exception {
+        node.hasNode((String)null);
     }
 
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowHasNodeWithNoPath() throws Exception {
-        node.hasNode(null);
+    public void shouldNotAllowEmptyPathInHasNode() throws Exception {
+        node.hasNode("");
     }
 
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowHasNodeWithEmptyPath() throws Exception {
-        node.hasNode("");
+    public void shouldNotAbsolutePathInHasNode() throws Exception {
+        node.hasNode("/a/b/c");
     }
 
     @Test
-    public void shouldProvideHasNodes() throws Exception {
-        assertThat(node.hasNodes(), is(false));
-        createChild(session, "child", 1, children, node);
-        node.setChildren(children);
-        assertThat(node.hasNodes(), is(true));
+    public void shouldReturnTrueFromHasNodeWithSelfReference() throws Exception {
+        assertThat(node.hasNode("."), is(true));
     }
 
     @Test
-    public void shouldProvideHasProperties() throws Exception {
-        assertThat(node.hasProperties(), is(false));
-        properties.put(name("something"), Mockito.mock(Property.class));
-        assertThat(node.hasProperties(), is(true));
+    public void shouldReturnTrueFromHasNodeWithParentReferenceIfNotRootNode() throws Exception {
+        assertThat(node.hasNode(".."), is(!node.isRoot()));
     }
 
     @Test
-    public void shouldIndicateHasProperty() throws Exception {
-        assertThat(node.hasProperty("prop"), is(false));
-        MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
-        node.setChildren(children);
-        assertThat(node.hasProperty("child"), is(false));
-        Property prop = Mockito.mock(Property.class);
-        stub(prop.getName()).toReturn("prop");
-        properties.put(name(prop.getName()), prop);
-        assertThat(node.hasProperty("prop"), is(true));
-        Map<Name, Property> properties = new HashMap<Name, Property>();
-        child.setProperties(properties);
-        Property prop2 = Mockito.mock(Property.class);
-        stub(prop2.getName()).toReturn("prop2");
-        properties.put(name(prop2.getName()), prop2);
-        stub(session.getItem("/node/child/prop2")).toReturn(prop2);
-        assertThat(node.hasProperty("child/prop2"), is(true));
+    public void shouldReturnTrueFromHasNodeIfRelativePathIdentifiesExistingNode() throws Exception {
+        AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+        stub(cache.findJcrNode(uuid, path("../other/node"))).toReturn(otherNode);
+        assertThat(node.hasNode("../other/node"), is(true));
     }
 
+    @Test
+    public void shouldReturnTrueFromHasNodeIfChildWithNameExists() throws Exception {
+        children.append(cache, name("child"), UUID.randomUUID());
+        assertThat(node.hasNode("child[1]"), is(true));
+        assertThat(node.hasNode("child[2]"), is(false));
+        children.append(cache, name("child"), UUID.randomUUID());
+        assertThat(node.hasNode("child[2]"), is(true));
+        assertThat(node.hasNode("child[3]"), is(false));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasNodeWithNameOfChildThatDoesNotExist() throws Exception {
+        assertThat(node.hasNode("something"), is(false));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasNodesIfThereAreNoChildren() throws Exception {
+        assertThat(node.hasNodes(), is(false));
+    }
+
+    @Test
+    public void shouldReturnTrueFromHasNodesIfThereIsAtLeastOneChild() throws Exception {
+        children.append(cache, name("child"), UUID.randomUUID());
+        assertThat(node.hasNodes(), is(true));
+    }
+
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowHasPropertyWithNoPath() throws Exception {
-        node.hasProperty(null);
+    public void shouldNotAllowNullPathInHasProperty() throws Exception {
+        node.hasProperty((String)null);
     }
 
     @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowHasPropertyWithEmptyPath() throws Exception {
+    public void shouldNotAllowEmptyPathInHasProperty() throws Exception {
         node.hasProperty("");
     }
 
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAbsolutePathInHasProperty() throws Exception {
+        node.hasProperty("/a/b/c");
+    }
+
     @Test
+    public void shouldReturnTrueFromHasPropertyIfPathIsRelativePathToOtherNodeWithNamedProperty() throws Exception {
+        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+        stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(property);
+        assertThat(node.hasProperty("../good/path"), is(true));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasPropertyIfPathIsRelativePathToOtherNodeWithoutNamedProperty() throws Exception {
+        stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toThrow(new PathNotFoundException());
+        assertThat(node.hasProperty("../good/path"), is(false));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasPropertyIfPathIsParentPath() throws Exception {
+        assertThat(node.hasProperty(".."), is(false));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasPropertyIfPathIsSelfPath() throws Exception {
+        assertThat(node.hasProperty("."), is(false));
+    }
+
+    @Test
+    public void shouldReturnTrueFromHasPropertyIfPathIsNameAndNodeHasProperty() throws Exception {
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        stub(cache.findPropertyInfo(new PropertyId(uuid, name("prop")))).toReturn(propertyInfo);
+        assertThat(node.hasProperty("prop"), is(true));
+    }
+
+    @Test
+    public void shouldReturnFalseFromHasPropertyIfPathIsNameAndNodeDoesNotHaveProperty() throws Exception {
+        stub(cache.findPropertyInfo(new PropertyId(uuid, name("prop")))).toReturn(null);
+        assertThat(node.hasProperty("prop"), is(false));
+    }
+
+    @Test
+    @SuppressWarnings( "unchecked" )
+    public void shouldReturnTrueFromHasPropertiesIfNodeHasAtLeastOneProperty() throws Exception {
+        Map<Name, PropertyInfo> properties = mock(Map.class);
+        stub(properties.size()).toReturn(5);
+        stub(info.getProperties()).toReturn(properties);
+        assertThat(node.hasProperties(), is(true));
+    }
+
+    @Test
+    @SuppressWarnings( "unchecked" )
+    public void shouldReturnFalseFromHasPropertiesIfNodeHasNoProperties() throws Exception {
+        Map<Name, PropertyInfo> properties = mock(Map.class);
+        stub(properties.size()).toReturn(0);
+        stub(info.getProperties()).toReturn(properties);
+        assertThat(node.hasProperties(), is(false));
+    }
+
+    @Test
     public void shouldNotAllowHoldsLock() throws Exception {
         assertThat(node.holdsLock(), is(false));
     }
@@ -485,29 +631,87 @@
     }
 
     @Test
-    public void shouldProvideIsSame() throws Exception {
-        stub(session.getWorkspace()).toReturn(Mockito.mock(Workspace.class));
-        stub(session.getRepository()).toReturn(repository);
+    public void shouldReturnFalseFromIsSameIfTheRepositoryInstanceIsDifferent() throws Exception {
+        Workspace workspace1 = mock(Workspace.class);
+        Repository repository1 = mock(Repository.class);
+        stub(session.getWorkspace()).toReturn(workspace1);
+        stub(session.getRepository()).toReturn(repository1);
+        stub(workspace1.getName()).toReturn("workspace1");
+
+        Workspace workspace2 = mock(Workspace.class);
         JcrSession session2 = Mockito.mock(JcrSession.class);
-        JcrRepository repository2 = Mockito.mock(JcrRepository.class);
+        Repository repository2 = mock(Repository.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session2.getRepository()).toReturn(repository2);
+        stub(workspace2.getName()).toReturn("workspace1");
+        stub(cache2.session()).toReturn(session2);
 
-        stub(session2.getRepository()).toReturn(repository2);
-        stub(session2.getWorkspace()).toReturn(workspace);
-        Node node2 = new MockAbstractJcrNode(session2, node.getName(), node.getParent());
+        UUID uuid2 = uuid;
+        Node node2 = new MockAbstractJcrNode(cache2, uuid2);
         assertThat(node.isSame(node2), is(false));
+    }
 
-        Property prop = Mockito.mock(Property.class);
-        stub(prop.getSession()).toReturn(session);
-        assertThat(node.isSame(prop), is(false));
-        node2 = Mockito.mock(Node.class);
-        stub(node2.getSession()).toReturn(session);
+    @Test
+    public void shouldReturnFalseFromIsSameIfTheWorkspaceNameIsDifferent() throws Exception {
+        Workspace workspace1 = mock(Workspace.class);
+        Repository repository1 = mock(Repository.class);
+        stub(session.getWorkspace()).toReturn(workspace1);
+        stub(session.getRepository()).toReturn(repository1);
+        stub(workspace1.getName()).toReturn("workspace1");
+
+        Workspace workspace2 = mock(Workspace.class);
+        JcrSession session2 = Mockito.mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session2.getRepository()).toReturn(repository1);
+        stub(workspace2.getName()).toReturn("workspace2");
+        stub(cache2.session()).toReturn(session2);
+
+        UUID uuid2 = uuid;
+        Node node2 = new MockAbstractJcrNode(cache2, uuid2);
         assertThat(node.isSame(node2), is(false));
-        node2 = new MockAbstractJcrNode(session, node.getName(), node.getParent());
-        UUID uuid = UUID.randomUUID();
-        node.setInternalUuid(uuid);
-        ((MockAbstractJcrNode)node2).setInternalUuid(UUID.randomUUID());
+    }
+
+    @Test
+    public void shouldReturnFalseFromIsSameIfTheNodeUuidIsDifferent() throws Exception {
+        Workspace workspace1 = mock(Workspace.class);
+        Repository repository1 = mock(Repository.class);
+        stub(session.getWorkspace()).toReturn(workspace1);
+        stub(session.getRepository()).toReturn(repository1);
+        stub(workspace1.getName()).toReturn("workspace1");
+
+        Workspace workspace2 = mock(Workspace.class);
+        JcrSession session2 = Mockito.mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session2.getRepository()).toReturn(repository1);
+        stub(workspace2.getName()).toReturn("workspace1");
+        stub(cache2.session()).toReturn(session2);
+
+        UUID uuid2 = UUID.randomUUID();
+        Node node2 = new MockAbstractJcrNode(cache2, uuid2);
         assertThat(node.isSame(node2), is(false));
-        ((MockAbstractJcrNode)node2).setInternalUuid(uuid);
+    }
+
+    @Test
+    public void shouldReturnTrueFromIsSameIfTheNodeUuidAndWorkspaceNameAndRepositoryInstanceAreSame() throws Exception {
+        Workspace workspace1 = mock(Workspace.class);
+        Repository repository1 = mock(Repository.class);
+        stub(session.getWorkspace()).toReturn(workspace1);
+        stub(session.getRepository()).toReturn(repository1);
+        stub(workspace1.getName()).toReturn("workspace1");
+
+        Workspace workspace2 = mock(Workspace.class);
+        JcrSession session2 = Mockito.mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session2.getRepository()).toReturn(repository1);
+        stub(workspace2.getName()).toReturn("workspace1");
+        stub(cache2.session()).toReturn(session2);
+
+        UUID uuid2 = uuid;
+        Node node2 = new MockAbstractJcrNode(cache2, uuid2);
         assertThat(node.isSame(node2), is(true));
     }
 

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -25,6 +25,7 @@
 
 import static org.hamcrest.core.Is.is;
 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.Calendar;
@@ -34,17 +35,16 @@
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.Property;
-import javax.jcr.PropertyType;
 import javax.jcr.Repository;
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
-import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -56,48 +56,54 @@
  */
 public class AbstractJcrPropertyTest {
 
+    private PropertyId propertyId;
+    private PropertyInfo info;
+    private ExecutionContext executionContext;
+    private JcrNode node;
     private AbstractJcrProperty prop;
     @Mock
-    private Workspace workspace;
-    @Mock
-    private Repository repository;
-    @Mock
     private JcrSession session;
-    private AbstractJcrNode node;
     @Mock
-    private NodeDefinition nodeDefinition;
-    @Mock
-    private PropertyDefinition propertyDefinition;
-    private ExecutionContext executionContext;
-    private org.jboss.dna.graph.property.Property dnaProperty;
-    private Location rootLocation;
-    private Location nodeLocation;
+    private SessionCache cache;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
-        dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
-        stub(propertyDefinition.getRequiredType()).toReturn(PropertyType.STRING);
-        stub(session.getWorkspace()).toReturn(workspace);
-        stub(session.getRepository()).toReturn(repository);
         stub(session.getExecutionContext()).toReturn(executionContext);
 
-        UUID rootUuid = UUID.randomUUID();
-        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-        rootLocation = Location.create(rootPath, rootUuid);
-        JcrRootNode rootNode = new JcrRootNode(session, rootLocation, nodeDefinition);
-        stub(session.getNode(rootUuid)).toReturn(rootNode);
-
         UUID uuid = UUID.randomUUID();
-        Path path = executionContext.getValueFactories().getPathFactory().create("/nodeName");
-        nodeLocation = Location.create(path, uuid);
-        node = new JcrNode(session, rootUuid, nodeLocation, nodeDefinition);
-        stub(session.getNode(uuid)).toReturn(node);
+        node = new JcrNode(cache, uuid);
+        propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+        prop = new MockAbstractJcrProperty(cache, propertyId);
 
-        prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
+        info = mock(PropertyInfo.class);
+        stub(info.getPropertyId()).toReturn(propertyId);
+        stub(info.getPropertyName()).toReturn(propertyId.getPropertyName());
+
+        stub(cache.session()).toReturn(session);
+        stub(cache.context()).toReturn(executionContext);
+        stub(cache.findJcrProperty(propertyId)).toReturn(prop);
+        stub(cache.findPropertyInfo(propertyId)).toReturn(info);
+        stub(cache.getPathFor(info)).toReturn(path("/a/b/c/jcr:mimeType"));
+        stub(cache.getPathFor(propertyId)).toReturn(path("/a/b/c/jcr:mimeType"));
+        stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c"));
+
+        NodeInfo nodeInfo = mock(NodeInfo.class);
+        stub(cache.findJcrNode(uuid)).toReturn(node);
+        stub(cache.findNodeInfo(uuid)).toReturn(nodeInfo);
+        stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c"));
+        stub(cache.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
     }
 
+    protected Name name( String name ) {
+        return executionContext.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Path path( String path ) {
+        return executionContext.getValueFactories().getPathFactory().create(path);
+    }
+
     @Test
     public void shouldAllowVisitation() throws Exception {
         ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -123,18 +129,18 @@
     }
 
     @Test( expected = ItemNotFoundException.class )
-    public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
-        prop.getAncestor(3);
+    public void shouldNotAllowAncestorDepthGreaterThanPropertyDepth() throws Exception {
+        prop.getAncestor(prop.getDepth() + 1);
     }
 
     @Test
     public void shouldProvideDepth() throws Exception {
-        assertThat(prop.getDepth(), is(2));
+        assertThat(prop.getDepth(), is(4));
     }
 
     @Test
     public void shouldProvideExecutionContext() throws Exception {
-        assertThat(prop.getExecutionContext(), is(executionContext));
+        assertThat(prop.context(), is(executionContext));
     }
 
     @Test
@@ -149,7 +155,7 @@
 
     @Test
     public void shouldProvidePath() throws Exception {
-        assertThat(prop.getPath(), is("/nodeName/jcr:mimeType"));
+        assertThat(prop.getPath(), is("/a/b/c/jcr:mimeType"));
     }
 
     @Test
@@ -163,43 +169,111 @@
     }
 
     @Test
-    public void shouldIndicateSameAsNodeWithSameParentAndSamePropertyName() throws Exception {
-        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
-                                                                                 .create(dnaProperty.getName());
+    public void shouldIndicateSameAsPropertyWithSameNodeAndSamePropertyName() throws Exception {
+        Repository repository = mock(Repository.class);
+        Workspace workspace = mock(Workspace.class);
+        Workspace workspace2 = mock(Workspace.class);
+        stub(workspace.getName()).toReturn("workspace");
+        stub(workspace2.getName()).toReturn("workspace");
+        JcrSession session2 = mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getRepository()).toReturn(repository);
+        stub(session.getRepository()).toReturn(repository);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session.getWorkspace()).toReturn(workspace);
+        stub(cache2.session()).toReturn(session2);
+        stub(cache2.context()).toReturn(executionContext);
+
         // Make the other node have the same UUID ...
-        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+        UUID uuid = node.internalUuid();
+        NodeInfo nodeInfo = mock(NodeInfo.class);
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+        stub(propertyInfo.getPropertyId()).toReturn(propertyId);
+        stub(propertyInfo.getPropertyName()).toReturn(propertyId.getPropertyName());
+        stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+        stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+        stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+        stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+        stub(cache2.findPropertyInfo(propertyId)).toReturn(info);
 
         assertThat(node.isSame(otherNode), is(true));
-        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+        Property prop = new MockAbstractJcrProperty(cache, propertyId);
+        Property otherProp = new MockAbstractJcrProperty(cache2, propertyId);
         assertThat(prop.isSame(otherProp), is(true));
     }
 
     @Test
     public void shouldIndicateDifferentThanNodeWithDifferentParent() throws Exception {
-        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
-                                                                                 .create(dnaProperty.getName());
-        UUID otherUuid = UUID.randomUUID();
-        Path otherPath = executionContext.getValueFactories().getPathFactory().create("/nodeName");
-        Location location = Location.create(otherPath, otherUuid);
-        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), location, nodeDefinition);
-        stub(session.getNode(otherUuid)).toReturn(otherNode);
+        Repository repository = mock(Repository.class);
+        Workspace workspace = mock(Workspace.class);
+        Workspace workspace2 = mock(Workspace.class);
+        stub(workspace.getName()).toReturn("workspace");
+        stub(workspace2.getName()).toReturn("workspace");
+        JcrSession session2 = mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getRepository()).toReturn(repository);
+        stub(session.getRepository()).toReturn(repository);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session.getWorkspace()).toReturn(workspace);
+        stub(cache2.session()).toReturn(session2);
+        stub(cache2.context()).toReturn(executionContext);
 
+        // Make the other node have a different UUID ...
+        UUID uuid = UUID.randomUUID();
+        PropertyId propertyId2 = new PropertyId(uuid, JcrLexicon.MIXIN_TYPES);
+        NodeInfo nodeInfo = mock(NodeInfo.class);
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+        stub(propertyInfo.getPropertyId()).toReturn(propertyId2);
+        stub(propertyInfo.getPropertyName()).toReturn(propertyId2.getPropertyName());
+        stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+        stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+        stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+        stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+
         assertThat(node.isSame(otherNode), is(false));
-        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+        Property prop = new MockAbstractJcrProperty(cache, propertyId);
+        Property otherProp = new MockAbstractJcrProperty(cache2, propertyId2);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
     @Test
     public void shouldIndicateDifferentThanPropertyWithSameNodeWithDifferentPropertyName() throws Exception {
-        org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
+        Repository repository = mock(Repository.class);
+        Workspace workspace = mock(Workspace.class);
+        Workspace workspace2 = mock(Workspace.class);
+        stub(workspace.getName()).toReturn("workspace");
+        stub(workspace2.getName()).toReturn("workspace");
+        JcrSession session2 = mock(JcrSession.class);
+        SessionCache cache2 = mock(SessionCache.class);
+        stub(session2.getRepository()).toReturn(repository);
+        stub(session.getRepository()).toReturn(repository);
+        stub(session2.getWorkspace()).toReturn(workspace2);
+        stub(session.getWorkspace()).toReturn(workspace);
+        stub(cache2.session()).toReturn(session2);
+        stub(cache2.context()).toReturn(executionContext);
+
         // Make the other node have the same UUID ...
-        JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+        UUID uuid = node.internalUuid();
+        NodeInfo nodeInfo = mock(NodeInfo.class);
+        PropertyInfo propertyInfo = mock(PropertyInfo.class);
+        PropertyId propertyId2 = new PropertyId(uuid, JcrLexicon.NAME);
+        AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+        stub(propertyInfo.getPropertyId()).toReturn(propertyId2);
+        stub(propertyInfo.getPropertyName()).toReturn(propertyId2.getPropertyName());
+        stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+        stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+        stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+        stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+        stub(cache2.findPropertyInfo(propertyId2)).toReturn(propertyInfo);
 
         assertThat(node.isSame(otherNode), is(true));
-        Property prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
-        Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+        Property prop = new MockAbstractJcrProperty(cache, propertyId);
+        Property otherProp = new MockAbstractJcrProperty(cache2, propertyId2);
         assertThat(prop.isSame(otherProp), is(false));
     }
 
@@ -255,10 +329,9 @@
 
     private class MockAbstractJcrProperty extends AbstractJcrProperty {
 
-        MockAbstractJcrProperty( AbstractJcrNode node,
-                                 PropertyDefinition propertyDefinition,
-                                 org.jboss.dna.graph.property.Property dnaProperty ) {
-            super(node, propertyDefinition, propertyDefinition.getRequiredType(), dnaProperty);
+        MockAbstractJcrProperty( SessionCache cache,
+                                 PropertyId propertyId ) {
+            super(cache, propertyId);
         }
 
         /**
@@ -266,8 +339,9 @@
          * 
          * @see javax.jcr.Property#getNode()
          */
+        @SuppressWarnings( "synthetic-access" )
         public Node getNode() {
-            throw new UnsupportedOperationException(); // shouldn't be called
+            return node;
         }
 
         /**

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,112 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import org.jboss.dna.graph.property.basic.BasicName;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * 
+ */
+public class JcrChildNodeIteratorTest {
+
+    private List<ChildNode> children;
+    private List<Node> childNodes;
+    private NodeIterator iter;
+    @Mock
+    private SessionCache cache;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        children = new ArrayList<ChildNode>();
+        childNodes = new ArrayList<Node>();
+        for (int i = 0; i != 10; ++i) {
+            UUID uuid = UUID.randomUUID();
+            ChildNode child = new ChildNode(uuid, new BasicName("", "name"), i + 1);
+            AbstractJcrNode node = mock(AbstractJcrNode.class);
+            stub(cache.findJcrNode(uuid)).toReturn(node);
+            children.add(child);
+            childNodes.add(node);
+        }
+        iter = new JcrChildNodeIterator(cache, children, children.size());
+    }
+
+    @Test
+    public void shouldProperlyDetermineHasNext() {
+        Iterator<Node> nodeIter = childNodes.iterator();
+        long position = 0L;
+        assertThat(iter.getPosition(), is(position));
+        while (iter.hasNext()) {
+            assertThat(nodeIter.hasNext(), is(true));
+            Node actual = (Node)iter.next();
+            Node expected = nodeIter.next();
+            assertThat(iter.getPosition(), is(++position));
+            assertThat(iter.getPosition(), is(position)); // call twice
+            assertThat(actual, is(sameInstance(expected)));
+        }
+        assertThat(iter.hasNext(), is(false));
+        assertThat(nodeIter.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldStartWithPositionOfZero() {
+        assertThat(iter.getPosition(), is(0L));
+    }
+
+    @Test
+    public void shouldHaveCorrectSize() {
+        assertThat(iter.getSize(), is((long)childNodes.size()));
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldNotAllowRemove() {
+        iter.remove();
+    }
+
+    @Test( expected = NoSuchElementException.class )
+    public void shouldFailWhenNextIsCalled() {
+        while (iter.hasNext()) {
+            iter.next();
+        }
+        iter.next();
+    }
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,70 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.NoSuchElementException;
+import javax.jcr.NodeIterator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class JcrEmptyNodeIteratorTest {
+
+    private NodeIterator iter;
+
+    @Before
+    public void beforeEach() {
+        iter = new JcrEmptyNodeIterator();
+    }
+
+    @Test
+    public void shouldNotHaveNext() {
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldHavePositionOfZero() {
+        assertThat(iter.getPosition(), is(0L));
+    }
+
+    @Test
+    public void shouldHaveSizeOfZero() {
+        assertThat(iter.getSize(), is(0L));
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldNotAllowRemove() {
+        iter.remove();
+    }
+
+    @Test( expected = NoSuchElementException.class )
+    public void shouldFailWhenNextIsCalled() {
+        iter.next();
+    }
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,70 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.NoSuchElementException;
+import javax.jcr.PropertyIterator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class JcrEmptyPropertyIteratorTest {
+
+    private PropertyIterator iter;
+
+    @Before
+    public void beforeEach() {
+        iter = new JcrEmptyPropertyIterator();
+    }
+
+    @Test
+    public void shouldNotHaveNext() {
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldHavePositionOfZero() {
+        assertThat(iter.getPosition(), is(0L));
+    }
+
+    @Test
+    public void shouldHaveSizeOfZero() {
+        assertThat(iter.getSize(), is(0L));
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldNotAllowRemove() {
+        iter.remove();
+    }
+
+    @Test( expected = NoSuchElementException.class )
+    public void shouldFailWhenNextIsCalled() {
+        iter.next();
+    }
+
+}


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

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -28,15 +28,13 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.stub;
 import java.util.UUID;
-import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -47,34 +45,50 @@
  */
 public class JcrMultiValuePropertyTest {
 
-    private Property prop;
-    private AbstractJcrNode node;
+    private PropertyId propertyId;
+    private JcrMultiValueProperty prop;
     private ExecutionContext executionContext;
+    private org.jboss.dna.graph.property.Property dnaProperty;
     @Mock
+    private SessionCache cache;
+    @Mock
     private JcrSession session;
     @Mock
-    private NodeDefinition nodeDefinition;
+    private PropertyInfo propertyInfo;
     @Mock
-    private PropertyDefinition definition;
-    private org.jboss.dna.graph.property.Property dnaProperty;
+    private JcrPropertyDefinition definition;
+    @Mock
+    private JcrNodeTypeManager nodeTypes;
 
     @Before
-    public void before() {
+    public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
+        stub(cache.session()).toReturn(session);
+        stub(cache.context()).toReturn(executionContext);
+        stub(session.nodeTypeManager()).toReturn(nodeTypes);
 
-        UUID rootUuid = UUID.randomUUID();
-        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-        Location rootLocation = Location.create(rootPath, rootUuid);
-        node = new JcrRootNode(session, rootLocation, nodeDefinition);
-        stub(session.getExecutionContext()).toReturn(executionContext);
-
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
         stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
+        stub(nodeTypes.getPropertyDefinition(definitionId, true)).toReturn(definition);
+
+        UUID uuid = UUID.randomUUID();
+        propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+        prop = new JcrMultiValueProperty(cache, propertyId);
+
+        stub(cache.findPropertyInfo(propertyId)).toReturn(propertyInfo);
+        stub(propertyInfo.getDefinitionId()).toReturn(definitionId);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BOOLEAN);
+        stub(propertyInfo.isMultiValued()).toReturn(true);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
     }
 
+    protected Name name( String name ) {
+        return executionContext.getValueFactories().getNameFactory().create(name);
+    }
+
     @Test
     public void shouldProvideAppropriateType() throws Exception {
         assertThat(prop.getType(), is(definition.getRequiredType()));
@@ -82,7 +96,7 @@
 
     @Test
     public void shouldProvidePropertyDefinition() throws Exception {
-        assertThat(prop.getDefinition(), notNullValue());
+        assertThat(prop.getDefinition(), is((PropertyDefinition)definition));
     }
 
     @Test
@@ -149,9 +163,10 @@
 
         Object value = "value";
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(cache, propertyId);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -160,9 +175,10 @@
         value = new Object();
         long expectedLength = executionContext.getValueFactories().getBinaryFactory().create(value).getSize();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(cache, propertyId);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(1));
@@ -170,9 +186,10 @@
 
         String[] values = new String[] {"value1", "value2", "value 3 is longer"};
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object[])values);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(true);
-        prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+        prop = new JcrMultiValueProperty(cache, propertyId);
         lengths = prop.getLengths();
         assertThat(lengths, notNullValue());
         assertThat(lengths.length, is(values.length));

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -1,101 +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 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.List;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
-
-/**
- * @author jverhaeg
- */
-public class JcrNodeIteratorTest {
-
-    private AbstractJcrNode node;
-    @Mock
-    private JcrSession session;
-    private List<Location> children;
-
-    @Before
-    public void before() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        ExecutionContext context = new ExecutionContext();
-        stub(session.getExecutionContext()).toReturn(context);
-        children = new ArrayList<Location>();
-        node = new MockAbstractJcrNode(session, "node", null);
-    }
-
-    @Test
-    public void shouldProvideNodeIterator() throws Exception {
-        Node child1 = AbstractJcrNodeTest.createChild(session, "child1", 1, children, node);
-        Node child2_1 = AbstractJcrNodeTest.createChild(session, "child2", 1, children, node);
-        Node child2_2 = AbstractJcrNodeTest.createChild(session, "child2", 2, children, node);
-        AbstractJcrNodeTest.createChild(session, "child3", 1, children, node);
-        AbstractJcrNodeTest.createChild(session, "child4", 1, children, node);
-        Node child5 = AbstractJcrNodeTest.createChild(session, "child5", 1, children, node);
-        node.setChildren(children);
-        NodeIterator iter = node.getNodes();
-        assertThat(iter, notNullValue());
-        assertThat(iter.getSize(), is(6L));
-        assertThat(iter.getPosition(), is(0L));
-        assertThat(iter.hasNext(), is(true));
-        assertThat((Node)iter.next(), is(child1));
-        assertThat(iter.getPosition(), is(1L));
-        assertThat(iter.hasNext(), is(true));
-        assertThat(iter.nextNode(), is(child2_1));
-        assertThat(iter.getPosition(), is(2L));
-        assertThat(iter.hasNext(), is(true));
-        assertThat(iter.nextNode(), is(child2_2));
-        assertThat(iter.getPosition(), is(3L));
-        assertThat(iter.hasNext(), is(true));
-        iter.skip(2);
-        assertThat(iter.getPosition(), is(5L));
-        assertThat(iter.hasNext(), is(true));
-        assertThat(iter.nextNode(), is(child5));
-        assertThat(iter.getPosition(), is(6L));
-        assertThat(iter.hasNext(), is(false));
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowNodeIteratorRemove() throws Exception {
-        node.getNodes().remove();
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void shouldNotAllowNodeIteratorNegativeSkip() throws Exception {
-        node.getNodes().skip(-1);
-    }
-}

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -25,17 +25,14 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.UUID;
 import javax.jcr.ItemNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -46,56 +43,62 @@
  */
 public class JcrNodeTest {
 
+    private UUID uuid;
     private JcrNode node;
-    private AbstractJcrNode root;
+    private ExecutionContext context;
     @Mock
-    private JcrSession session;
-    @Mock
-    private NodeDefinition rootNodeDefinition;
-    @Mock
-    private NodeDefinition nodeDefinition;
+    private SessionCache cache;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        ExecutionContext context = new ExecutionContext();
-        UUID rootUuid = UUID.randomUUID();
-        Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
-        Location rootLocation = Location.create(rootPath, rootUuid);
-        root = new JcrRootNode(session, rootLocation, rootNodeDefinition);
-        UUID uuid = UUID.randomUUID();
-        Path path = context.getValueFactories().getPathFactory().create("/name[2]");
-        Location location = Location.create(path, uuid);
-        node = new JcrNode(session, rootUuid, location, nodeDefinition);
-        stub(session.getExecutionContext()).toReturn(context);
-        stub(session.getNode(rootUuid)).toReturn(root);
-        stub(session.getNode(uuid)).toReturn(node);
-        node.setProperties(new HashMap<Name, Property>());
-        node.setChildren(new ArrayList<Location>());
+
+        uuid = UUID.randomUUID();
+        node = new JcrNode(cache, uuid);
+
+        context = new ExecutionContext();
+        stub(cache.context()).toReturn(context);
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
     @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
-        node.getAncestor(2);
+        NodeInfo info = mock(NodeInfo.class);
+        stub(cache.findNodeInfo(uuid)).toReturn(info);
+        stub(cache.getPathFor(info)).toReturn(path("/a/b/c/name[2]"));
+        node.getAncestor(6);
     }
 
     @Test
     public void shouldProvideDepth() throws Exception {
-        assertThat(node.getDepth(), is(1));
+        NodeInfo info = mock(NodeInfo.class);
+        stub(cache.findNodeInfo(uuid)).toReturn(info);
+        stub(cache.getPathFor(info)).toReturn(path("/a/b/c/name[2]"));
+        assertThat(node.getDepth(), is(4));
     }
 
     @Test
     public void shouldProvideIndex() throws Exception {
-        assertThat(node.getIndex(), is(2));
+        stub(cache.getSnsIndexOf(uuid)).toReturn(1);
+        assertThat(node.getIndex(), is(1));
     }
 
     @Test
     public void shouldProvideName() throws Exception {
+        stub(cache.getNameOf(uuid)).toReturn(name("name"));
         assertThat(node.getName(), is("name"));
     }
 
     @Test
     public void shouldProvidePath() throws Exception {
-        assertThat(node.getPath(), is("/name[2]"));
+        stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c/name[2]"));
+        assertThat(node.getPath(), is("/a/b/c/name[2]"));
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -32,31 +32,30 @@
 import javax.jcr.PropertyIterator;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
 
 /**
  * @author jverhaeg
  */
 public class JcrPropertyIteratorTest {
 
-    private AbstractJcrNode node;
-    @Mock
-    private JcrSession session;
     private Map<Name, Property> properties;
     private ExecutionContext context;
+    private PropertyIterator iter;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         context = new ExecutionContext();
         properties = new HashMap<Name, Property>();
-        node = new MockAbstractJcrNode(session, "node", null);
-        node.setProperties(properties);
+        properties.put(name("prop1"), Mockito.mock(Property.class));
+        properties.put(name("prop2"), Mockito.mock(Property.class));
+        properties.put(name("prop3"), Mockito.mock(Property.class));
+        properties.put(name("prop4"), Mockito.mock(Property.class));
+        iter = new JcrPropertyIterator(properties.values());
     }
 
     protected Name name( String name ) {
@@ -65,11 +64,6 @@
 
     @Test
     public void shouldProvidePropertyIterator() throws Exception {
-        properties.put(name("prop1"), Mockito.mock(Property.class));
-        properties.put(name("prop2"), Mockito.mock(Property.class));
-        properties.put(name("prop3"), Mockito.mock(Property.class));
-        properties.put(name("prop4"), Mockito.mock(Property.class));
-        PropertyIterator iter = node.getProperties();
         assertThat(iter, notNullValue());
         assertThat(iter.getSize(), is(4L));
         assertThat(iter.getPosition(), is(0L));
@@ -87,11 +81,11 @@
 
     @Test( expected = UnsupportedOperationException.class )
     public void shouldNotAllowPropertyIteratorRemove() throws Exception {
-        node.getProperties().remove();
+        new JcrPropertyIterator(properties.values()).remove();
     }
 
     @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowPropertyIteratorNegativeSkip() throws Exception {
-        node.getProperties().skip(-1);
+        new JcrPropertyIterator(properties.values()).skip(-1);
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -26,14 +26,10 @@
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertThat;
-import java.util.HashMap;
-import java.util.Map;
+import static org.mockito.Mockito.stub;
 import java.util.UUID;
 import javax.jcr.ItemNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.nodetype.NodeDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.junit.Before;
@@ -46,25 +42,31 @@
  */
 public class JcrRootNodeTest {
 
+    private UUID uuid;
     private JcrRootNode root;
+    private ExecutionContext context;
     @Mock
-    private JcrSession session;
-    @Mock
-    private NodeDefinition nodeDefinition;
-    private Map<Name, Property> properties;
+    private SessionCache cache;
 
     @Before
     public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
-        properties = new HashMap<Name, Property>();
-        ExecutionContext context = new ExecutionContext();
-        UUID rootUuid = UUID.randomUUID();
-        Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
-        Location rootLocation = Location.create(rootPath, rootUuid);
-        root = new JcrRootNode(session, rootLocation, nodeDefinition);
-        root.setProperties(properties);
+
+        uuid = UUID.randomUUID();
+        root = new JcrRootNode(cache, uuid);
+
+        context = new ExecutionContext();
+        stub(cache.context()).toReturn(context);
     }
 
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
     @Test( expected = ItemNotFoundException.class )
     public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
         root.getAncestor(1);

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-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -23,13 +23,13 @@
  */
 package org.jboss.dna.jcr;
 
-import static org.junit.Assert.assertTrue;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsNull.nullValue;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.io.ByteArrayOutputStream;
@@ -71,7 +71,7 @@
 public class JcrSessionTest {
 
     private static final String MULTI_LINE_VALUE = "Line\t1\nLine 2\rLine 3\r\nLine 4";
-    
+
     private String workspaceName;
     private ExecutionContext context;
     private InMemoryRepositorySource source;
@@ -102,7 +102,7 @@
         graph.set("booleanProperty").on("/a/b").to(true);
         graph.set("stringProperty").on("/a/b/c").to("value");
         graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
-        
+
         // Make sure the path to the namespaces exists ...
         graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
 
@@ -287,13 +287,10 @@
 
     @Test
     public void shouldProvideRootNode() throws Exception {
-        Map<UUID, AbstractJcrNode> nodesByUuid = session.getNodesByUuid();
-        assertThat(nodesByUuid.isEmpty(), is(true));
         Node root = session.getRootNode();
         assertThat(root, notNullValue());
         UUID uuid = ((JcrRootNode)root).internalUuid();
         assertThat(uuid, notNullValue());
-        assertThat(nodesByUuid.get(uuid), is(root));
     }
 
     @Test
@@ -394,14 +391,14 @@
         assertThat(session.getNamespaceURI("sv"), is("http://www.jcp.org/jcr/sv/1.0"));
         // assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
     }
-    
+
     @Test
     public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
         OutputStream os = new ByteArrayOutputStream();
-        
+
         session.exportSystemView("/a/b/c", os, false, true);
-        
+
         String fileContents = os.toString();
-        assertTrue(fileContents.contains(MULTI_LINE_VALUE)); 
+        assertTrue(fileContents.contains(MULTI_LINE_VALUE));
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -31,17 +31,15 @@
 import java.io.InputStream;
 import java.util.UUID;
 import javax.jcr.Node;
-import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
 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.jcr.SessionCache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -52,39 +50,57 @@
  */
 public class JcrSingleValuePropertyTest {
 
-    private Property prop;
-    private AbstractJcrNode node;
+    private PropertyId propertyId;
+    private JcrSingleValueProperty prop;
     private ExecutionContext executionContext;
+    private org.jboss.dna.graph.property.Property dnaProperty;
     @Mock
+    private SessionCache cache;
+    @Mock
     private JcrSession session;
     @Mock
-    private NodeDefinition nodeDefinition;
+    private PropertyInfo propertyInfo;
     @Mock
-    Name name;
+    private JcrPropertyDefinition definition;
     @Mock
-    private PropertyDefinition definition;
-    private org.jboss.dna.graph.property.Property dnaProperty;
+    private JcrNodeTypeManager nodeTypes;
 
     @Before
-    public void before() {
+    public void before() throws Exception {
         MockitoAnnotations.initMocks(this);
         executionContext = new ExecutionContext();
-
-        UUID rootUuid = UUID.randomUUID();
-        Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-        Location rootLocation = Location.create(rootPath, rootUuid);
-        node = new JcrRootNode(session, rootLocation, nodeDefinition);
+        stub(cache.session()).toReturn(session);
+        stub(cache.context()).toReturn(executionContext);
+        stub(session.nodeTypeManager()).toReturn(nodeTypes);
         stub(session.getExecutionContext()).toReturn(executionContext);
 
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
+        stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(false);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
+        stub(nodeTypes.getPropertyDefinition(definitionId, false)).toReturn(definition);
+
+        UUID uuid = UUID.randomUUID();
+        propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+        prop = new JcrSingleValueProperty(cache, propertyId);
+
+        stub(cache.findPropertyInfo(propertyId)).toReturn(propertyInfo);
+        stub(propertyInfo.getDefinitionId()).toReturn(definitionId);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
+        stub(propertyInfo.isMultiValued()).toReturn(false);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyName()).toReturn(dnaProperty.getName());
     }
 
+    protected Name name( String name ) {
+        return executionContext.getValueFactories().getNameFactory().create(name);
+    }
+
     @Test
     public void shouldProvideBoolean() throws Exception {
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BOOLEAN);
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.BOOLEAN, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         assertThat(prop.getBoolean(), is(true));
         assertThat(prop.getType(), is(PropertyType.BOOLEAN));
     }
@@ -92,7 +108,6 @@
     @Test
     public void shouldIndicateHasSingleValue() throws Exception {
         PropertyDefinition def = prop.getDefinition();
-        assertThat(def, notNullValue());
         assertThat(def.isMultiple(), is(false));
     }
 
@@ -100,7 +115,8 @@
     public void shouldProvideDate() throws Exception {
         DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.DATE, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.DATE);
         assertThat(prop.getDate(), is(dnaDate.toCalendar()));
         assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
         assertThat(prop.getType(), is(PropertyType.DATE));
@@ -111,9 +127,10 @@
     public void shouldProvideNode() throws Exception {
         UUID referencedUuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.REFERENCE);
         AbstractJcrNode referencedNode = mock(AbstractJcrNode.class);
-        stub(session.getNode(referencedUuid)).toReturn(referencedNode);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.REFERENCE, dnaProperty);
+        stub(cache.findJcrNode(referencedUuid)).toReturn(referencedNode);
         assertThat(prop.getNode(), is((Node)referencedNode));
         assertThat(prop.getType(), is(PropertyType.REFERENCE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -122,12 +139,13 @@
     @Test
     public void shouldProvideDouble() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.DOUBLE);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         assertThat(prop.getDouble(), is(1.0));
         assertThat(prop.getType(), is(PropertyType.DOUBLE));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -136,23 +154,26 @@
     @Test
     public void shouldProvideLong() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.LONG);
         assertThat(prop.getLong(), is(1L));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getType(), is(PropertyType.LONG));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
 
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.LONG);
         assertThat(prop.getLong(), is(1L));
         assertThat(prop.getString(), is("1"));
-        assertThat(prop.getType(), is(PropertyType.DOUBLE));
+        assertThat(prop.getType(), is(PropertyType.LONG));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
     }
 
     @Test
     public void shouldProvideStream() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BINARY);
         assertThat(prop.getType(), is(PropertyType.BINARY));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
         InputStream stream = prop.getStream();
@@ -168,7 +189,8 @@
     @Test
     public void shouldProvideString() throws Exception {
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
         assertThat(prop.getString(), is("value"));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -178,7 +200,8 @@
     public void shouldAllowReferenceValue() throws Exception {
         UUID uuid = UUID.randomUUID();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
         assertThat(prop.getString(), is(uuid.toString()));
         assertThat(prop.getType(), is(PropertyType.STRING));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -189,7 +212,8 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.NAME, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.NAME);
         assertThat(prop.getString(), is("acme:something"));
         assertThat(prop.getType(), is(PropertyType.NAME));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -203,7 +227,8 @@
         executionContext.getNamespaceRegistry().register("acme", "http://example.com");
         Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.PATH, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+        stub(propertyInfo.getPropertyType()).toReturn(PropertyType.PATH);
         assertThat(prop.getString(), is("/a/b/acme:c"));
         assertThat(prop.getType(), is(PropertyType.PATH));
         assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -230,13 +255,13 @@
 
         dnaValue = "some other value";
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         assertThat(prop.getLength(), is((long)dnaValue.length()));
 
         Object obj = new Object();
         long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
-        prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
+        stub(propertyInfo.getProperty()).toReturn(dnaProperty);
         assertThat(prop.getLength(), is(binaryLength));
     }
 

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,480 @@
+/*
+ * 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 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.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.stub;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+import org.xml.sax.SAXException;
+
+/**
+ * 
+ */
+public class SessionCacheTest {
+
+    private String workspaceName;
+    private ExecutionContext context;
+    private JcrNodeTypeManager nodeTypes;
+    private Stopwatch sw;
+    private Graph store;
+    private SessionCache cache;
+    private Map<String, Graph> storesByName;
+    private Map<String, SessionCache> cachesByName;
+
+    @Mock
+    private JcrSession session;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        sw = new Stopwatch();
+        storesByName = new HashMap<String, Graph>();
+        cachesByName = new HashMap<String, SessionCache>();
+
+        context = new ExecutionContext();
+        context.getNamespaceRegistry().register("vehix", "http://example.com/vehicles");
+
+        workspaceName = "theWorkspace";
+
+        stub(session.getExecutionContext()).toReturn(context);
+        stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
+
+        // Load up all the node types ...
+        JcrNodeTypeSource nodeTypeSource = new JcrBuiltinNodeTypeSource(session);
+        nodeTypeSource = new DnaBuiltinNodeTypeSource(session, nodeTypeSource);
+        nodeTypeSource = new VehixNodeTypeSource(session, nodeTypeSource);
+        nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
+        stub(session.nodeTypeManager()).toReturn(nodeTypes);
+
+        InMemoryRepositorySource source = new InMemoryRepositorySource();
+        source.setName("store");
+        source.setDefaultWorkspaceName(workspaceName);
+        store = Graph.create(source, context);
+
+        // Import the "cars.xml" file into the repository
+        store.importXmlFrom(new File("src/test/resources/vehicles.xml")).into("/");
+
+        cache = new SessionCache(session, workspaceName, context, nodeTypes, store);
+    }
+
+    /**
+     * Define the node types for the "vehix" namespace.
+     */
+    public static class VehixNodeTypeSource extends AbstractJcrNodeTypeSource {
+        private final List<JcrNodeType> primaryNodeTypes;
+        private final List<JcrNodeType> mixinNodeTypes;
+
+        public VehixNodeTypeSource( JcrSession session,
+                                    JcrNodeTypeSource predecessor ) {
+            super(predecessor);
+            this.primaryNodeTypes = new ArrayList<JcrNodeType>();
+            this.mixinNodeTypes = new ArrayList<JcrNodeType>();
+            Name carName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:car");
+            Name aircraftName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:aircraft");
+            JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
+
+            // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
+            JcrNodeType car = new JcrNodeType(session, carName, Arrays.asList(new NodeType[] {unstructured}),
+                                              NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
+                                              ORDERABLE_CHILD_NODES);
+
+            // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
+            JcrNodeType aircraft = new JcrNodeType(session, aircraftName, Arrays.asList(new NodeType[] {unstructured}),
+                                                   NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
+                                                   ORDERABLE_CHILD_NODES);
+
+            primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+            return mixinNodeTypes;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+            return primaryNodeTypes;
+        }
+
+    }
+
+    protected Graph createFrom( String repositoryName,
+                                String workspaceName,
+                                File file ) throws IOException, SAXException {
+        InMemoryRepositorySource source = new InMemoryRepositorySource();
+        source.setName(repositoryName);
+        source.setDefaultWorkspaceName(workspaceName);
+        Graph graph = Graph.create(source, context);
+
+        if (file != null) {
+            graph.importXmlFrom(file).into("/");
+        }
+        return graph;
+    }
+
+    protected Graph getGraph( String name ) throws IOException, SAXException {
+        Graph graph = storesByName.get(name);
+        if (graph == null) {
+            graph = createFrom(name, name + " workspace", new File("src/test/resources/" + name + ".xml"));
+            storesByName.put(name, graph);
+        }
+        return graph;
+    }
+
+    protected SessionCache getCache( String name ) throws IOException, SAXException {
+        SessionCache cache = cachesByName.get(name);
+        if (cache == null) {
+            cache = new SessionCache(session, name + " workspace", context, nodeTypes, getGraph(name));
+            cachesByName.put(name, cache);
+        }
+        return cache;
+    }
+
+    protected Name name( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Path path( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected Path.Segment segment( String segment ) {
+        return context.getValueFactories().getPathFactory().createSegment(segment);
+    }
+
+    protected String pad( String name ) {
+        return StringUtil.justifyLeft(name, 22, ' ');
+    }
+
+    protected void assertSameProperties( NodeInfo nodeInfo,
+                                         Node dnaNode ) {
+        UUID uuid = dnaNode.getLocation().getUuid();
+        assertThat(nodeInfo.getUuid(), is(uuid));
+        assertThat(nodeInfo.getOriginalLocation().getUuid(), is(uuid));
+        Set<Name> propertyNames = nodeInfo.getProperties().keySet();
+        for (Name propertyName : propertyNames) {
+            PropertyInfo info = nodeInfo.getProperty(propertyName);
+            assertThat(info.getNodeUuid(), is(uuid));
+            assertThat(info.getPropertyId().getNodeId(), is(uuid));
+            assertThat(info.getPropertyId().getPropertyName(), is(propertyName));
+            assertThat(info.getProperty().getName(), is(propertyName));
+            Property actual = dnaNode.getProperty(propertyName);
+            if (actual != null) {
+                assertThat(info.getProperty().size(), is(actual.size()));
+                assertThat(info.getProperty().getValuesAsArray(), is(actual.getValuesAsArray()));
+            } else {
+                assertThat(propertyName, is(JcrLexicon.PRIMARY_TYPE));
+            }
+        }
+    }
+
+    @Test
+    public void shouldCreateWithValidParameters() {
+        assertThat(cache, is(notNullValue()));
+    }
+
+    @Test
+    public void shouldFindNodeInfoForRootByUuid() throws Exception {
+        Node root = store.getNodeAt("/");
+        UUID rootUuid = root.getLocation().getUuid();
+        NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+        assertThat(rootInfo, is(notNullValue()));
+        assertThat(rootInfo.getUuid(), is(rootUuid));
+        assertThat(rootInfo.getDefinitionId().getNodeTypeName(), is(name("dna:root")));
+        assertThat(rootInfo.getDefinitionId().getChildDefinitionName(), is(name("*")));
+        assertThat(rootInfo.getPrimaryTypeName(), is(name("dna:root")));
+        assertThat(rootInfo.getOriginalLocation().getPath().isRoot(), is(true));
+        assertThat(rootInfo.getOriginalLocation().getUuid(), is(rootUuid));
+        Set<Name> rootProperties = rootInfo.getProperties().keySet();
+        assertThat(rootProperties, hasItems(name("jcr:primaryType")));
+        assertSameProperties(rootInfo, root);
+
+        PropertyInfo info = rootInfo.getProperty(JcrLexicon.PRIMARY_TYPE);
+        assertThat(info.getProperty().getFirstValue(), is((Object)name("dna:root")));
+    }
+
+    @Test
+    public void shouldRepeatedlyFindRootNodeInfoByUuid() throws Exception {
+        Node root = store.getNodeAt("/");
+        UUID rootUuid = root.getLocation().getUuid();
+        for (int i = 0; i != 20; ++i) {
+            NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+            assertThat(rootInfo, is(notNullValue()));
+        }
+    }
+
+    @Test
+    public void shouldRepeatedlyFindRootNodeInfoByPath() throws Exception {
+        String sourceName = "cars";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+        Node root = store.getNodeAt("/");
+        UUID rootUuid = root.getLocation().getUuid();
+        NodeInfo nodeInfo = cache.findNodeInfoForRoot();
+        assertThat(nodeInfo.getUuid(), is(rootUuid));
+        for (int i = 0; i != 20; ++i) {
+            sw.start();
+            assertThat(nodeInfo, is(sameInstance(cache.findNodeInfoForRoot())));
+            sw.stop();
+            assertThat(nodeInfo, is(sameInstance(cache.findNodeInfo(rootUuid))));
+        }
+        System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+    }
+
+    @Test
+    public void shouldRepeatedlyFindRootNodeInfoByUuidFromVehiclesSource() throws Exception {
+        String sourceName = "vehicles";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+        sw.start();
+        Node root = store.getNodeAt("/");
+        sw.stop();
+        UUID rootUuid = root.getLocation().getUuid();
+        for (int i = 0; i != 20; ++i) {
+            sw.start();
+            NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+            sw.stop();
+            assertThat(rootInfo, is(notNullValue()));
+        }
+        System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+    }
+
+    @Test
+    public void shouldRepeatedlyFindRootNodeInfoByUuidFromSourceWithPrimaryTypes() throws Exception {
+        String sourceName = "repositoryForTckTests";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+        sw.start();
+        Node root = store.getNodeAt("/");
+        sw.stop();
+        UUID rootUuid = root.getLocation().getUuid();
+        for (int i = 0; i != 20; ++i) {
+            sw.start();
+            NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+            sw.stop();
+            assertThat(rootInfo, is(notNullValue()));
+        }
+        System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+    }
+
+    @Test
+    public void shouldFindChildrenInNodeInfoForRoot() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        Children children = root.getChildren();
+        assertThat(children, is(notNullValue()));
+        assertThat(children.size(), is(1));
+        assertThat(children.getChild(segment("vehix:Vehicles")), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldFindNodeInfoForNonRootNodeByPath() throws Exception {
+        String sourceName = "vehicles";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+        // Get the root ...
+        NodeInfo root = cache.findNodeInfoForRoot();
+
+        // Now try to load a node that is well-below the root ...
+        Path lr3Path = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        Node lr3Node = store.getNodeAt(lr3Path);
+        sw.start();
+        NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), lr3Path);
+        sw.stop();
+        assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+        assertSameProperties(lr3, lr3Node);
+        System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+
+        // Verify that this loaded all the intermediate nodes, by walking up ...
+        NodeInfo info = lr3;
+        while (true) {
+            UUID parent = info.getParent();
+            if (parent == null) {
+                // then we should be at the root ...
+                assertThat(info.getUuid(), is(root.getUuid()));
+                break;
+            }
+            // Otherwise, we're not at the root, so we should find the parent ...
+            info = cache.findNodeInfoInCache(parent);
+            assertThat(info, is(notNullValue()));
+        }
+    }
+
+    @Test
+    public void shouldFindInfoForNodeUsingRelativePathFromRoot() throws Exception {
+        String sourceName = "vehicles";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+
+        // Verify that the node does exist in the source ...
+        Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+
+        // Get the root ...
+        NodeInfo root = cache.findNodeInfoForRoot();
+
+        // Now try to load a node that is well-below the root ...
+        Path lr3Path = path("vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        assertThat(lr3Path.isAbsolute(), is(false));
+        NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), lr3Path);
+        assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+        assertSameProperties(lr3, lr3Node);
+
+        Path recoveredPath = cache.getPathFor(lr3);
+        assertThat(recoveredPath, is(lr3AbsolutePath));
+    }
+
+    @Test
+    public void shouldFindInfoForNodeUsingRelativePathFromNonRoot() throws Exception {
+        String sourceName = "vehicles";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+
+        // Verify that the node does exist in the source ...
+        Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
+        Node carsNode = store.getNodeAt(carsAbsolutePath);
+        Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+        Path b787AbsolutePath = path("/vehix:Vehicles/vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+        Node b787Node = store.getNodeAt(b787AbsolutePath);
+
+        // Get the root ...
+        NodeInfo root = cache.findNodeInfoForRoot();
+
+        // Now try to load the cars node ...
+        Path carsPath = path("vehix:Vehicles/vehix:Cars");
+        assertThat(carsPath.isAbsolute(), is(false));
+        NodeInfo cars = cache.findNodeInfo(root.getUuid(), carsPath);
+        assertThat(cars.getUuid(), is(carsNode.getLocation().getUuid()));
+        assertSameProperties(cars, carsNode);
+
+        // Now try to find the LR3 node relative to the car ...
+        Path lr3Path = path("vehix:Utility/vehix:Land Rover LR3");
+        assertThat(lr3Path.isAbsolute(), is(false));
+        NodeInfo lr3 = cache.findNodeInfo(cars.getUuid(), lr3Path);
+        assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+        assertSameProperties(lr3, lr3Node);
+
+        // Now try to find the "Boeing 787" node relative to the LR3 node ...
+        Path b787Path = path("../../../vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+        assertThat(b787Path.isAbsolute(), is(false));
+        assertThat(b787Path.isNormalized(), is(true));
+        NodeInfo b787 = cache.findNodeInfo(lr3.getUuid(), b787Path);
+        assertThat(b787.getUuid(), is(b787Node.getLocation().getUuid()));
+        assertSameProperties(b787, b787Node);
+
+        assertThat(cache.getPathFor(cars), is(carsAbsolutePath));
+        assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
+        assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
+    }
+
+    @Test
+    public void shouldFindJcrNodeUsingAbsolutePaths() throws Exception {
+        String sourceName = "vehicles";
+        Graph store = getGraph(sourceName);
+        SessionCache cache = getCache(sourceName);
+
+        // Verify that the node does exist in the source ...
+        Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
+        Node carsNode = store.getNodeAt(carsAbsolutePath);
+        Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+        Path b787AbsolutePath = path("/vehix:Vehicles/vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+        Node b787Node = store.getNodeAt(b787AbsolutePath);
+
+        // Get the root ...
+        NodeInfo root = cache.findNodeInfoForRoot();
+
+        // Now try to load the cars node ...
+        Path carsPath = path("vehix:Vehicles/vehix:Cars");
+        assertThat(carsPath.isAbsolute(), is(false));
+        AbstractJcrNode carJcrNode = cache.findJcrNode(root.getUuid(), carsAbsolutePath);
+        assertThat(carJcrNode.internalUuid(), is(carsNode.getLocation().getUuid()));
+        NodeInfo cars = cache.findNodeInfo(root.getUuid(), carsPath);
+        assertThat(cars.getUuid(), is(carsNode.getLocation().getUuid()));
+        assertSameProperties(cars, carsNode);
+
+        // Now try to find the LR3 node relative to the car ...
+        Path lr3Path = path("vehix:Utility/vehix:Land Rover LR3");
+        assertThat(lr3Path.isAbsolute(), is(false));
+        AbstractJcrNode lr3JcrNode = cache.findJcrNode(root.getUuid(), lr3AbsolutePath);
+        assertThat(lr3JcrNode.internalUuid(), is(lr3Node.getLocation().getUuid()));
+        NodeInfo lr3 = cache.findNodeInfo(cars.getUuid(), lr3Path);
+        assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+        assertSameProperties(lr3, lr3Node);
+
+        // Now try to find the "Boeing 787" node relative to the LR3 node ...
+        Path b787Path = path("../../../vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+        assertThat(b787Path.isAbsolute(), is(false));
+        assertThat(b787Path.isNormalized(), is(true));
+        AbstractJcrNode b787JcrNode = cache.findJcrNode(root.getUuid(), b787AbsolutePath);
+        assertThat(b787JcrNode.internalUuid(), is(b787Node.getLocation().getUuid()));
+        NodeInfo b787 = cache.findNodeInfo(lr3.getUuid(), b787Path);
+        assertThat(b787.getUuid(), is(b787Node.getLocation().getUuid()));
+        assertSameProperties(b787, b787Node);
+
+        assertThat(cache.getPathFor(cars), is(carsAbsolutePath));
+        assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
+        assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
+    }
+}


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

Modified: trunk/dna-jcr/src/test/resources/cars.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/cars.xml	2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/resources/cars.xml	2009-03-16 22:50:09 UTC (rev 777)
@@ -45,4 +45,4 @@
         <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" />
     </Utility>
-</Cars>\ No newline at end of file
+</Cars>
\ No newline at end of file

Added: trunk/dna-jcr/src/test/resources/vehicles.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/vehicles.xml	                        (rev 0)
+++ trunk/dna-jcr/src/test/resources/vehicles.xml	2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss DNA (http://www.jboss.org/dna)
+  ~
+  ~ See the COPYRIGHT.txt file distributed with this work for information
+  ~ regarding copyright ownership.  Some portions may be licensed
+  ~ to Red Hat, Inc. under one or more contributor license agreements.
+  ~ See the AUTHORS.txt file in the distribution for a full listing of 
+  ~ individual contributors.
+  ~
+  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+  ~ is licensed to you under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ JBoss DNA is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+  ~ for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public License
+  ~ along with this distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<Vehicles xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns="http://example.com/vehicles">
+    <Cars>
+        <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"/>
+        </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" />
+        </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" />
+        </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" />
+        </Utility>
+    </Cars>
+    <Aircraft>
+        <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"/>
+        </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"/>
+        </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"/>
+        </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"/>
+        </Homebuilt>
+    </Aircraft>
+</Vehicles>
\ No newline at end of file


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



From dna-commits at lists.jboss.org  Tue Mar 17 11:43:09 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Mar 2009 11:43:09 -0400
Subject: [dna-commits] DNA SVN: r778 - in trunk:
	dna-common/src/main/java/org/jboss/dna/common/util and 3
	other directories.
Message-ID: <E1LjbRd-0002Xh-8T@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-17 11:43:09 -0400 (Tue, 17 Mar 2009)
New Revision: 778

Added:
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.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
Removed:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java
Modified:
   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/JcrChildNodeIterator.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/JcrChildNodeIteratorTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-194 Implement update JCR capability

Changed SessionCache to use immutable versions of the node, child and property representations, in preparation for mutable ones in the "changed cache".

Copied: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java (from rev 776, trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java)
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java	                        (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,61 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.collection;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * @author jverhaeg
+ * @param <T> some type
+ */
+public final class EmptyIterator<T> implements Iterator<T> {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public T next() {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+}


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

Added: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java	                        (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,70 @@
+/*
+ * 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.common.collection;
+
+import java.util.Iterator;
+
+/**
+ * An {@link Iterator} implementation that only allows reading elements, used as a wrapper around another iterator to make the
+ * contents appear to be immutable.
+ * 
+ * @param <T> the type that is being iterated over
+ */
+public final class ReadOnlyIterator<T> implements Iterator<T> {
+
+    private final Iterator<T> delegate;
+
+    public ReadOnlyIterator( Iterator<T> delegate ) {
+        this.delegate = delegate;
+        assert this.delegate != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        return delegate.hasNext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#next()
+     */
+    public T next() {
+        return delegate.next();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}


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

Deleted: trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -1,61 +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.common.util;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * @author jverhaeg
- * @param <T> some type
- */
-public final class EmptyIterator<T> implements Iterator<T> {
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.util.Iterator#hasNext()
-     */
-    public boolean hasNext() {
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.util.Iterator#next()
-     */
-    public T next() {
-        throw new NoSuchElementException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.util.Iterator#remove()
-     */
-    public void remove() {
-        throw new UnsupportedOperationException();
-    }
-}

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -56,14 +56,14 @@
 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.ChildNode;
-import org.jboss.dna.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.cache.ChildNode;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.NodeInfo;
 
 /**
  * An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
- * {@link SessionCache}. Each instance references the {@link SessionCache.NodeInfo node information} also managed by the
- * SessionCache, and finds and operates against this information with each method call.
+ * {@link SessionCache}. Each instance references the {@link NodeInfo node information} also managed by the SessionCache, and
+ * finds and operates against this information with each method call.
  */
 @Immutable
 abstract class AbstractJcrNode extends AbstractJcrItem implements Node {
@@ -266,7 +266,7 @@
      * @see javax.jcr.Node#hasProperties()
      */
     public final boolean hasProperties() throws RepositoryException {
-        return nodeInfo().getProperties().size() > 0;
+        return nodeInfo().hasProperties();
     }
 
     /**

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -37,7 +37,7 @@
 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.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 
 /**
  * @author jverhaeg

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -29,7 +29,7 @@
 import javax.jcr.RepositoryException;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.jcr.cache.ChildNode;
 
 /**
  */

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -25,15 +25,14 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 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;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
@@ -45,8 +44,6 @@
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.nodetype.PropertyDefinition;
-import net.jcip.annotations.Immutable;
-import net.jcip.annotations.NotThreadSafe;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
@@ -63,9 +60,13 @@
 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.jcr.cache.ChildNode;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.ImmutableChildren;
+import org.jboss.dna.jcr.cache.ImmutableNodeInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import com.google.common.base.ReferenceType;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
 import com.google.common.collect.ReferenceMap;
 
 /**
@@ -247,10 +248,10 @@
     public Collection<AbstractJcrProperty> findJcrPropertiesFor( UUID nodeUuid )
         throws ItemNotFoundException, RepositoryException {
         NodeInfo info = findNodeInfo(nodeUuid);
-        Map<Name, PropertyInfo> properties = info.getProperties();
-        Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(properties.size());
-        for (PropertyInfo propertyInfo : properties.values()) {
-            result.add(findJcrProperty(propertyInfo.getPropertyId()));
+        Set<Name> propertyNames = info.getPropertyNames();
+        Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(propertyNames.size());
+        for (Name propertyName : propertyNames) {
+            result.add(findJcrProperty(new PropertyId(nodeUuid, propertyName)));
         }
         return result;
     }
@@ -377,22 +378,6 @@
         return new JcrSingleValueProperty(this, info.getPropertyId());
     }
 
-    // public AbstractJcrProperty findJcrProperty( Path propertyPath ) throws PathNotFoundException, RepositoryException {
-    // if (propertyPath.isRoot()) return null;
-    // Path nodePath = propertyPath.getParent();
-    // NodeInfo nodeInfo = findNodeInfo(nodePath);
-    // Name propertyName = propertyPath.getLastSegment().getName();
-    // PropertyInfo info = nodeInfo.getProperty(propertyName);
-    //
-    // // Look for an existing JCR Property object ...
-    // PropertyId propertyId = info.getPropertyId();
-    // AbstractJcrProperty property = jcrProperties.get(propertyId);
-    // if (property != null) return property;
-    //
-    // // Now create the appropriate JCR Property object ...
-    // return createAndCacheJcrPropertyFor(info);
-    // }
-
     /**
      * Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
      * especially after the information has been cached. Note, however, that this method first checks the cache, and if the
@@ -617,9 +602,7 @@
             NodeInfo parentInfo = findNodeInfo(parent);
             ChildNode child = parentInfo.getChildren().getChild(info.getUuid());
             if (child == null) break;
-            Path.Segment segment = pathFactory.createSegment(child.getName(), child.getSnsIndex());
-
-            segments.addFirst(segment);
+            segments.addFirst(child.getSegment());
             info = parentInfo;
         }
         return pathFactory.createAbsolutePath(segments);
@@ -826,17 +809,6 @@
             }
         }
 
-        // Create the node information ...
-        NodeInfo info = new NodeInfo(location, primaryTypeName, definition.getId());
-        if (parentInfo != null) {
-            info.setParent(parentInfo.getUuid());
-        }
-
-        // --------------------------------------------------
-        // Set the node's children ...
-        // --------------------------------------------------
-        info.setChildren(graphNode.getChildren());
-
         // ------------------------------------------------------
         // Set the node's properties ...
         // ------------------------------------------------------
@@ -894,6 +866,7 @@
         }
 
         // 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()) {
             Name name = dnaProp.getName();
 
@@ -958,7 +931,9 @@
 
             // Record the property in the node information ...
             PropertyId propId = new PropertyId(uuid, name);
-            info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, propertyType, dnaProp);
+            JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), propertyType, dnaProp, defn.isMultiple());
+            props.put(name, propInfo);
         }
 
         // Now add the "jcr:uuid" property if and only if referenceable ...
@@ -966,15 +941,21 @@
             // We know that this property is single-valued
             PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
             PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
-            info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, PropertyType.STRING, uuidProperty);
+            JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+            PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
+            props.put(JcrLexicon.UUID, propInfo);
         } else {
             // Make sure there is NOT a "jcr:uuid" property ...
-            info.removeProperty(JcrLexicon.UUID);
+            props.remove(JcrLexicon.UUID);
         }
         // Make sure the "dna:uuid" property did not get in there ...
-        info.removeProperty(DnaLexicon.UUID);
+        props.remove(DnaLexicon.UUID);
 
-        return info;
+        // Create the node information ...
+        UUID parentUuid = parentInfo != null ? parentInfo.getUuid() : null;
+        Children children = new ImmutableChildren(parentUuid, graphNode.getChildren());
+        props = Collections.unmodifiableMap(props);
+        return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
     }
 
     /**
@@ -1001,583 +982,4 @@
         // TODO: should this also check the mixins?
         return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
     }
-
-    /**
-     * The information that describes a node. This is the information that is kept in the {@link SessionCache#cachedNodes cache}
-     * and in the record of {@link SessionCache#changedNodes changes} made by the session but not yet commited/saved.
-     */
-    @NotThreadSafe
-    protected class NodeInfo {
-        private final Location originalLocation;
-        private final UUID uuid;
-        private UUID parent;
-        private final Name primaryTypeName;
-        private final NodeDefinitionId definition;
-        private final Children children;
-        private final Map<Name, PropertyInfo> properties;
-
-        protected NodeInfo( Location originalLocation,
-                            Name primaryTypeName,
-                            NodeDefinitionId definition ) {
-            this.originalLocation = originalLocation;
-            this.primaryTypeName = primaryTypeName;
-            this.definition = definition;
-            this.uuid = this.originalLocation.getUuid();
-            this.children = new Children(this.uuid);
-            this.properties = new HashMap<Name, PropertyInfo>();
-            assert this.uuid != null;
-            assert this.definition != null;
-            assert this.primaryTypeName != null;
-        }
-
-        /**
-         * @return location
-         */
-        public Location getOriginalLocation() {
-            return originalLocation;
-        }
-
-        /**
-         * @return uuid
-         */
-        public UUID getUuid() {
-            return uuid;
-        }
-
-        /**
-         * @return parent
-         */
-        public UUID getParent() {
-            return parent;
-        }
-
-        /**
-         * @param parent Sets parent to the specified value.
-         */
-        protected void setParent( UUID parent ) {
-            this.parent = parent;
-        }
-
-        /**
-         * @return primaryTypeName
-         */
-        public Name getPrimaryTypeName() {
-            return primaryTypeName;
-        }
-
-        /**
-         * @return definition
-         */
-        public NodeDefinitionId getDefinitionId() {
-            return definition;
-        }
-
-        /**
-         * Get the children for this node.
-         * 
-         * @return the children; never null but possibly empty
-         */
-        public Children getChildren() {
-            return children;
-        }
-
-        /**
-         * @param children Sets children to the specified value.
-         * @return the children information; never null
-         */
-        public Children setChildren( List<Location> children ) {
-            this.children.append(SessionCache.this, children, true);
-            return this.children;
-        }
-
-        /**
-         * Get the map of information for each property.
-         * 
-         * @return the information for each property; never null but possibly (though unlikely) empty
-         */
-        public Map<Name, PropertyInfo> getProperties() {
-            return this.properties; // never null
-        }
-
-        public PropertyInfo getProperty( Name name ) {
-            return this.properties.get(name);
-        }
-
-        public PropertyInfo setProperty( PropertyId id,
-                                         JcrPropertyDefinition definition,
-                                         int propertyType,
-                                         Property dnaProperty ) {
-            // Initialize the map if required (this never replaces it, though) ...
-            PropertyInfo info = new PropertyInfo(id, definition.getId(), propertyType, dnaProperty, definition.isMultiple());
-            return this.properties.put(id.getPropertyName(), info);
-        }
-
-        public PropertyInfo removeProperty( Name name ) {
-            return this.properties.remove(name);
-        }
-    }
-
-    /**
-     * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
-     * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
-     * type}.
-     * <p>
-     * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an
-     * instance by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their
-     * reference. This is because these instances are replaced and discarded whenever anything about the property changes.
-     * </p>
-     */
-    @Immutable
-    public static class PropertyInfo {
-        private final PropertyId propertyId;
-        private final PropertyDefinitionId definitionId;
-        private final Property dnaProperty;
-        private final int propertyType;
-        private final boolean multiValued;
-
-        protected PropertyInfo( PropertyId propertyId,
-                                PropertyDefinitionId definitionId,
-                                int propertyType,
-                                Property dnaProperty,
-                                boolean multiValued ) {
-            this.propertyId = propertyId;
-            this.definitionId = definitionId;
-            this.propertyType = propertyType;
-            this.dnaProperty = dnaProperty;
-            this.multiValued = multiValued;
-        }
-
-        /**
-         * Get the durable identifier for this property.
-         * 
-         * @return propertyId
-         */
-        public PropertyId getPropertyId() {
-            return propertyId;
-        }
-
-        /**
-         * Get the UUID of the node to which this property belongs.
-         * 
-         * @return the owner node's UUID; never null
-         */
-        public UUID getNodeUuid() {
-            return propertyId.getNodeId();
-        }
-
-        /**
-         * The identifier for the property definition.
-         * 
-         * @return the property definition ID; never null
-         */
-        public PropertyDefinitionId getDefinitionId() {
-            return definitionId;
-        }
-
-        /**
-         * Get the DNA Property, which contains the name and value(s)
-         * 
-         * @return the property; never null
-         */
-        public Property getProperty() {
-            return dnaProperty;
-        }
-
-        /**
-         * Get the property name.
-         * 
-         * @return the property name; never null
-         */
-        public Name getPropertyName() {
-            return dnaProperty.getName();
-        }
-
-        /**
-         * Get the JCR {@link PropertyType} for this property.
-         * 
-         * @return the property type
-         */
-        public int getPropertyType() {
-            return propertyType;
-        }
-
-        /**
-         * @return multiValued
-         */
-        public boolean isMultiValued() {
-            return multiValued;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#hashCode()
-         */
-        @Override
-        public int hashCode() {
-            return propertyId.hashCode();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#equals(java.lang.Object)
-         */
-        @Override
-        public boolean equals( Object obj ) {
-            if (obj == this) return true;
-            if (obj instanceof PropertyInfo) {
-                return propertyId.equals(((PropertyInfo)obj).getPropertyId());
-            }
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#toString()
-         */
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append(propertyId);
-            sb.append(" defined by ").append(definitionId);
-            sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
-            if (dnaProperty.isSingle()) {
-                sb.append(" with value ");
-            } else {
-                sb.append(" with values ");
-            }
-            sb.append(dnaProperty.getValuesAsArray());
-            return sb.toString();
-        }
-    }
-
-    /**
-     * Class that maintains the ordered list of {@link ChildNode} instances. This class uses a {@link ListMultimap} to maintain
-     * insertion order of the child nodes, and to allow fast access to the children with a specified name.
-     */
-    @ThreadSafe
-    public final static class Children implements Iterable<ChildNode> {
-        private final UUID parentUuid;
-        private final Map<UUID, ChildNode> childrenByUuid;
-        private final ListMultimap<Name, ChildNode> childrenByName;
-        private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
-        Children( UUID parentUuid ) {
-            this.parentUuid = parentUuid;
-            this.childrenByUuid = new HashMap<UUID, ChildNode>();
-            this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
-        }
-
-        /**
-         * Get the number of children.
-         * 
-         * @return the number of children
-         */
-        public int size() {
-            try {
-                lock.readLock().lock();
-                return childrenByName.size();
-            } finally {
-                lock.readLock().unlock();
-            }
-        }
-
-        /**
-         * Get the children.
-         * 
-         * @return a copy of the list of children
-         */
-        public List<ChildNode> asList() {
-            try {
-                lock.readLock().lock();
-                return new ArrayList<ChildNode>(childrenByName.values());
-            } finally {
-                lock.readLock().unlock();
-            }
-        }
-
-        /**
-         * Get the children.
-         * 
-         * @return a copy of the list of children
-         */
-        public Iterator<ChildNode> iterator() {
-            return asList().iterator();
-        }
-
-        /**
-         * The UUID of the parent node.
-         * 
-         * @return the parent node's UUID
-         */
-        public UUID getParentUuid() {
-            return parentUuid;
-        }
-
-        /**
-         * Get the child with the given UUID.
-         * 
-         * @param uuid the UUID of the child node
-         * @return the child node, or null if there is no child with the supplied UUID
-         */
-        public ChildNode getChild( UUID uuid ) {
-            try {
-                lock.readLock().lock();
-                return this.childrenByUuid.get(uuid);
-            } finally {
-                lock.readLock().unlock();
-            }
-        }
-
-        /**
-         * Get the child given the path segment.
-         * 
-         * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
-         *        {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
-         * @return the information for the child node, or null if no such child existed
-         */
-        public ChildNode getChild( Path.Segment segment ) {
-            try {
-                lock.readLock().lock();
-                List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
-                int snsIndex = segment.getIndex();
-                if (childrenWithName.size() < snsIndex) return null;
-                return childrenWithName.get(snsIndex - 1);
-            } finally {
-                lock.readLock().unlock();
-            }
-        }
-
-        /**
-         * Get the same-name-sibling children that all share the supplied name.
-         * 
-         * @param name the name for the children; may not be null
-         * @return the children with the supplied name; never null
-         */
-        public List<ChildNode> getChildren( Name name ) {
-            try {
-                lock.readLock().lock();
-                return new ArrayList<ChildNode>(this.childrenByName.get(name));
-            } finally {
-                lock.readLock().unlock();
-            }
-        }
-
-        /**
-         * Remove the child with the given path segment.
-         * 
-         * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
-         *        {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
-         * @return the information for the child node that was removed, or null if no such child existed
-         */
-        public ChildNode remove( Path.Segment segment ) {
-            try {
-                lock.writeLock().lock();
-                List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
-                int snsIndex = segment.getIndex();
-                int numChildrenWithName = childrenWithName.size();
-                if (numChildrenWithName < snsIndex) return null;
-                ChildNode result = childrenWithName.remove(snsIndex);
-                this.childrenByUuid.remove(result.getUuid());
-                --numChildrenWithName;
-                if (numChildrenWithName > snsIndex) {
-                    // We need to reduce the SNS index of every child after the one that was just removed ...
-                    ListIterator<ChildNode> siblingIter = childrenWithName.listIterator(snsIndex);
-                    while (siblingIter.hasNext()) {
-                        // Remove the next child and replace with one having the correct SNS index ...
-                        ChildNode next = siblingIter.next();
-                        siblingIter.remove();
-                        siblingIter.set(next.withChangedSnsIndex(-1));
-                    }
-                }
-                return result;
-            } finally {
-                lock.writeLock().unlock();
-            }
-        }
-
-        /**
-         * Add a new child to the end of the list of existing children a new child with the supplied name and UUID. The
-         * same-name-sibling will be determined to be one more than the number of existing children with the same name.
-         * 
-         * @param cache the cache; may not be null
-         * @param name the name for the child to be appended; may not be null
-         * @param childUuid the UUID of the child; may not be null
-         * @return the information for the newly-added child; never null
-         */
-        public ChildNode append( SessionCache cache,
-                                 Name name,
-                                 UUID childUuid ) {
-            try {
-                lock.writeLock().lock();
-                List<ChildNode> childrenWithName = this.childrenByName.get(name);
-                ChildNode child = new ChildNode(childUuid, name, childrenWithName.size() + 1);
-                childrenWithName.add(child);
-                this.childrenByUuid.put(childUuid, child);
-                // Look for the child in the cache/changed nodes ...
-                NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
-                if (childInfo != null) childInfo.setParent(parentUuid);
-                return child;
-            } finally {
-                lock.writeLock().unlock();
-            }
-        }
-
-        /**
-         * Append the children described by the supplied Location objects, optionally removing all existing children first.
-         * 
-         * @param cache the cache; may not be null
-         * @param children a list containing a Location object for each child
-         * @param removeExistingFirst true if the existing children should be removed before these children are added, or false if
-         *        these children should be appended to the existing children
-         */
-        public void append( SessionCache cache,
-                            List<Location> children,
-                            boolean removeExistingFirst ) {
-            try {
-                lock.writeLock().lock();
-                if (removeExistingFirst && !this.childrenByName.isEmpty()) {
-                    for (ChildNode child : this.childrenByName.values()) {
-                        // Look for the child in the cache/changed nodes ...
-                        NodeInfo childInfo = cache.findNodeInfoInCache(child.getUuid());
-                        if (childInfo != null) childInfo.setParent(null);
-                        // TODO: These nodes are deleted and should be handled as deletes ...
-                    }
-                    this.childrenByUuid.clear();
-                    this.childrenByName.clear();
-                }
-                for (Location childLocation : children) {
-                    UUID childUuid = childLocation.getUuid();
-                    Path.Segment segment = childLocation.getPath().getLastSegment();
-                    Name name = segment.getName();
-                    ChildNode child = new ChildNode(childUuid, name, segment.getIndex());
-                    this.childrenByName.put(name, child);
-                    this.childrenByUuid.put(childUuid, child);
-                    // Look for the child in the cache/changed nodes ...
-                    NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
-                    if (childInfo != null) childInfo.setParent(parentUuid);
-                }
-            } finally {
-                lock.writeLock().unlock();
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#toString()
-         */
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            try {
-                lock.readLock().lock();
-                boolean first = true;
-                for (ChildNode child : childrenByName.values()) {
-                    if (!first) sb.append(", ");
-                    else first = false;
-                    sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
-                }
-            } finally {
-                lock.readLock().unlock();
-            }
-            return sb.toString();
-        }
-    }
-
-    /**
-     * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded.
-     * Clients should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects
-     * and replacing them with new instances.
-     */
-    @Immutable
-    public final static class ChildNode {
-        private final UUID uuid;
-        private final Name name;
-        private final int snsIndex;
-
-        protected ChildNode( UUID uuid,
-                             Name name,
-                             int snsIndex ) {
-            this.uuid = uuid;
-            this.name = name;
-            this.snsIndex = snsIndex;
-            assert this.snsIndex > 0;
-        }
-
-        /**
-         * Get the UUID of the node.
-         * 
-         * @return the node's UUID; never null
-         */
-        public UUID getUuid() {
-            return uuid;
-        }
-
-        /**
-         * Get the name of the node.
-         * 
-         * @return the node's current name; never null
-         */
-        public Name getName() {
-            return name;
-        }
-
-        /**
-         * Get the same-name-sibling index of the node.
-         * 
-         * @return the node's SNS index; always positive
-         */
-        public int getSnsIndex() {
-            return snsIndex;
-        }
-
-        /**
-         * Return a new child node that has a changed SNS index.
-         * 
-         * @param delta the amount the change the SNS index, either positive to increase the value or negative to decrease the
-         *        value
-         * @return the copy of this, with a changed SNS index; never null
-         */
-        public ChildNode withChangedSnsIndex( int delta ) {
-            return new ChildNode(uuid, name, snsIndex + delta);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#hashCode()
-         */
-        @Override
-        public int hashCode() {
-            return uuid.hashCode();
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#equals(java.lang.Object)
-         */
-        @Override
-        public boolean equals( Object obj ) {
-            if (obj == this) return true;
-            if (obj instanceof ChildNode) {
-                return this.uuid.equals(((ChildNode)obj).uuid);
-            }
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see java.lang.Object#toString()
-         */
-        @Override
-        public String toString() {
-            return uuid.toString() + " ( " + name + "[" + snsIndex + "] )";
-        }
-    }
-
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,133 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * 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.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * 
+ */
+/**
+ * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded. Clients
+ * should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects and
+ * replacing them with new instances.
+ */
+ at Immutable
+public final class ChildNode {
+
+    private final UUID uuid;
+    private final Path.Segment segment;
+
+    public ChildNode( UUID uuid,
+                      Path.Segment segment ) {
+        assert uuid != null;
+        assert segment != null;
+        this.uuid = uuid;
+        this.segment = segment;
+    }
+
+    /**
+     * Get the UUID of the node.
+     * 
+     * @return the node's UUID; never null
+     */
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    /**
+     * Get the path segment for this node.
+     * 
+     * @return the path segment; never null
+     */
+    public Path.Segment getSegment() {
+        return segment;
+    }
+
+    /**
+     * Get the name of the node.
+     * 
+     * @return the node's current name; never null
+     */
+    public Name getName() {
+        return segment.getName();
+    }
+
+    /**
+     * Get the same-name-sibling index of the node.
+     * 
+     * @return the node's SNS index; always positive
+     */
+    public int getSnsIndex() {
+        return segment.getIndex();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return uuid.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof ChildNode) {
+            return this.uuid.equals(((ChildNode)obj).uuid);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return uuid.toString() + " ( " + getSegment() + " )";
+    }
+
+    /**
+     * Obtain a new instance that uses the same {@link #getUuid() UUID} but the supplied path segment.
+     * 
+     * @param newSegment the new segment; may not be null
+     * @return the new instance; never null
+     */
+    public ChildNode with( Path.Segment newSegment ) {
+        return new ChildNode(uuid, newSegment);
+    }
+
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,88 @@
+/*
+ * 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.Iterator;
+import java.util.UUID;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * Class that maintains the ordered list of {@link ChildNode} instances yet allows fast access to the children with a specified
+ * name.
+ */
+public interface Children extends Iterable<ChildNode> {
+
+    /**
+     * Get the number of children.
+     * 
+     * @return the number of children
+     */
+    int size();
+
+    /**
+     * The UUID of the parent node.
+     * 
+     * @return the parent node's UUID
+     */
+    UUID getParentUuid();
+
+    /**
+     * Get the child with the given UUID.
+     * 
+     * @param uuid the UUID of the child node
+     * @return the child node, or null if there is no child with the supplied UUID
+     */
+    ChildNode getChild( UUID uuid );
+
+    /**
+     * Get the child given the path segment.
+     * 
+     * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+     *        {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+     * @return the information for the child node, or null if no such child existed
+     */
+    ChildNode getChild( Path.Segment segment );
+
+    /**
+     * Get the same-name-sibling children that all share the supplied name, in order of increasing SNS index.
+     * 
+     * @param name the name for the children; may not be null
+     * @return the children with the supplied name; never null
+     */
+    Iterator<ChildNode> getChildren( Name name );
+
+    /**
+     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * 
+     * @param newChildName the name of the new child; may not be null
+     * @param newChildUuid the UUID of the new child; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    Children with( Name newChildName,
+                   UUID newChildUuid,
+                   PathFactory pathFactory );
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,117 @@
+/*
+ * 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.Iterator;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.EmptyIterator;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * An immutable implementation of {@link Children}.
+ */
+ at Immutable
+public final class EmptyChildren implements Children {
+
+    private static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
+
+    private final UUID parentUuid;
+
+    public EmptyChildren( UUID parentUuid ) {
+        this.parentUuid = parentUuid;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#size()
+     */
+    public int size() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<ChildNode> iterator() {
+        return EMPTY_ITERATOR;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getParentUuid()
+     */
+    public UUID getParentUuid() {
+        return parentUuid;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID)
+     */
+    public ChildNode getChild( UUID uuid ) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment)
+     */
+    public ChildNode getChild( Segment segment ) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name)
+     */
+    public Iterator<ChildNode> getChildren( Name name ) {
+        return EMPTY_ITERATOR;
+    }
+
+    public ImmutableChildren with( Name newChildName,
+                                   UUID newChildUuid,
+                                   PathFactory pathFactory ) {
+        return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "";
+    }
+}


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

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,164 @@
+/*
+ * 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.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.ReadOnlyIterator;
+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.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+
+/**
+ * An immutable implementation of {@link Children}.
+ */
+ at Immutable
+public final class ImmutableChildren implements Children {
+    private final UUID parentUuid;
+    private final Map<UUID, ChildNode> childrenByUuid;
+    private final ListMultimap<Name, ChildNode> childrenByName;
+
+    public ImmutableChildren( UUID parentUuid,
+                              Iterable<Location> children ) {
+        this.parentUuid = parentUuid;
+        this.childrenByUuid = new HashMap<UUID, ChildNode>();
+        this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+        for (Location childLocation : children) {
+            UUID childUuid = childLocation.getUuid();
+            Path.Segment segment = childLocation.getPath().getLastSegment();
+            Name name = segment.getName();
+            ChildNode child = new ChildNode(childUuid, segment);
+            this.childrenByName.put(name, child);
+            this.childrenByUuid.put(childUuid, child);
+        }
+    }
+
+    protected ImmutableChildren( Children original,
+                                 Name additionalChildName,
+                                 UUID childUuid,
+                                 PathFactory pathFactory ) {
+        this.parentUuid = original.getParentUuid();
+        this.childrenByUuid = new HashMap<UUID, ChildNode>();
+        this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+        Iterator<ChildNode> iter = original.iterator();
+        while (iter.hasNext()) {
+            ChildNode child = iter.next();
+            this.childrenByName.put(child.getName(), child);
+            this.childrenByUuid.put(child.getUuid(), child);
+        }
+        List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName);
+        Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1);
+        ChildNode additionalChild = new ChildNode(childUuid, segment);
+        this.childrenByName.put(additionalChildName, additionalChild);
+        this.childrenByUuid.put(childUuid, additionalChild);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#size()
+     */
+    public int size() {
+        return childrenByName.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<ChildNode> iterator() {
+        return new ReadOnlyIterator<ChildNode>(this.childrenByName.values().iterator());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getParentUuid()
+     */
+    public UUID getParentUuid() {
+        return parentUuid;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID)
+     */
+    public ChildNode getChild( UUID uuid ) {
+        return this.childrenByUuid.get(uuid);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment)
+     */
+    public ChildNode getChild( Segment segment ) {
+        List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+        int snsIndex = segment.getIndex();
+        if (childrenWithName.size() < snsIndex) return null;
+        return childrenWithName.get(snsIndex - 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name)
+     */
+    public Iterator<ChildNode> getChildren( Name name ) {
+        return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator());
+    }
+
+    public ImmutableChildren with( Name newChildName,
+                                   UUID newChildUuid,
+                                   PathFactory pathFactory ) {
+        return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (ChildNode child : childrenByName.values()) {
+            if (!first) sb.append(", ");
+            else first = false;
+            sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
+        }
+        return sb.toString();
+    }
+}


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

Added: 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	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,155 @@
+/*
+ * 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.Set;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ */
+ at Immutable
+public class ImmutableNodeInfo implements NodeInfo {
+    private final Location originalLocation;
+    private final UUID uuid;
+    private final UUID parent;
+    private final Name primaryTypeName;
+    private final NodeDefinitionId definition;
+    private final Children children;
+    private final Map<Name, PropertyInfo> properties;
+
+    /**
+     * 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 children the immutable children
+     * @param properties the unmodifiable map of properties
+     */
+    public ImmutableNodeInfo( Location originalLocation,
+                              Name primaryTypeName,
+                              NodeDefinitionId definition,
+                              UUID parent,
+                              Children children,
+                              Map<Name, PropertyInfo> properties ) {
+        this.originalLocation = originalLocation;
+        this.primaryTypeName = primaryTypeName;
+        this.definition = definition;
+        this.parent = parent;
+        this.uuid = this.originalLocation.getUuid();
+        this.children = children;
+        this.properties = properties;
+        assert this.uuid != null;
+        assert this.definition != null;
+        assert this.primaryTypeName != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getOriginalLocation()
+     */
+    public Location getOriginalLocation() {
+        return originalLocation;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getUuid()
+     */
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getParent()
+     */
+    public UUID getParent() {
+        return parent;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPrimaryTypeName()
+     */
+    public Name getPrimaryTypeName() {
+        return primaryTypeName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
+     */
+    public NodeDefinitionId getDefinitionId() {
+        return definition;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
+     */
+    public Children getChildren() {
+        return children;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#hasProperties()
+     */
+    public boolean hasProperties() {
+        return this.properties.size() != 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
+     */
+    public Set<Name> getPropertyNames() {
+        return this.properties.keySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getProperty(org.jboss.dna.graph.property.Name)
+     */
+    public PropertyInfo getProperty( Name name ) {
+        return this.properties.get(name);
+    }
+}


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

Added: 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	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,90 @@
+/*
+ * 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.Set;
+import java.util.UUID;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ */
+public interface NodeInfo {
+
+    /**
+     * @return location
+     */
+    public Location getOriginalLocation();
+
+    /**
+     * @return uuid
+     */
+    public UUID getUuid();
+
+    /**
+     * @return parent
+     */
+    public UUID getParent();
+
+    /**
+     * @return primaryTypeName
+     */
+    public Name getPrimaryTypeName();
+
+    /**
+     * @return definition
+     */
+    public NodeDefinitionId getDefinitionId();
+
+    /**
+     * Get the children for this node.
+     * 
+     * @return the children; never null but possibly empty
+     */
+    public Children getChildren();
+
+    /**
+     * Return true of this node has at least one property.
+     * 
+     * @return true if there is at least one property, or false if there are none
+     */
+    public boolean hasProperties();
+
+    /**
+     * Get the names of the properties that are owned by this node.
+     * 
+     * @return the unmodifiable set of property names
+     */
+    public Set<Name> getPropertyNames();
+
+    /**
+     * Get this node's property that has the supplied name.
+     * 
+     * @param name the property name; may not be null
+     * @return the property information, or null if this node has no property with the supplied name
+     */
+    public PropertyInfo getProperty( Name name );
+}


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

Added: 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	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,168 @@
+/*
+ * 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.UUID;
+import javax.jcr.PropertyType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.PropertyDefinitionId;
+import org.jboss.dna.jcr.PropertyId;
+
+/**
+ * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
+ * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
+ * type}.
+ * <p>
+ * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an instance
+ * by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their reference. This
+ * is because these instances are replaced and discarded whenever anything about the property changes.
+ * </p>
+ */
+ at Immutable
+public class PropertyInfo {
+    private final PropertyId propertyId;
+    private final PropertyDefinitionId definitionId;
+    private final Property dnaProperty;
+    private final int propertyType;
+    private final boolean multiValued;
+
+    public PropertyInfo( PropertyId propertyId,
+                         PropertyDefinitionId definitionId,
+                         int propertyType,
+                         Property dnaProperty,
+                         boolean multiValued ) {
+        this.propertyId = propertyId;
+        this.definitionId = definitionId;
+        this.propertyType = propertyType;
+        this.dnaProperty = dnaProperty;
+        this.multiValued = multiValued;
+    }
+
+    /**
+     * Get the durable identifier for this property.
+     * 
+     * @return propertyId
+     */
+    public PropertyId getPropertyId() {
+        return propertyId;
+    }
+
+    /**
+     * Get the UUID of the node to which this property belongs.
+     * 
+     * @return the owner node's UUID; never null
+     */
+    public UUID getNodeUuid() {
+        return propertyId.getNodeId();
+    }
+
+    /**
+     * The identifier for the property definition.
+     * 
+     * @return the property definition ID; never null
+     */
+    public PropertyDefinitionId getDefinitionId() {
+        return definitionId;
+    }
+
+    /**
+     * Get the DNA Property, which contains the name and value(s)
+     * 
+     * @return the property; never null
+     */
+    public Property getProperty() {
+        return dnaProperty;
+    }
+
+    /**
+     * Get the property name.
+     * 
+     * @return the property name; never null
+     */
+    public Name getPropertyName() {
+        return dnaProperty.getName();
+    }
+
+    /**
+     * Get the JCR {@link PropertyType} for this property.
+     * 
+     * @return the property type
+     */
+    public int getPropertyType() {
+        return propertyType;
+    }
+
+    /**
+     * @return multiValued
+     */
+    public boolean isMultiValued() {
+        return multiValued;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return propertyId.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof PropertyInfo) {
+            return propertyId.equals(((PropertyInfo)obj).getPropertyId());
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(propertyId);
+        sb.append(" defined by ").append(definitionId);
+        sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
+        if (dnaProperty.isSingle()) {
+            sb.append(" with value ");
+        } else {
+            sb.append(" with values ");
+        }
+        sb.append(dnaProperty.getValuesAsArray());
+        return sb.toString();
+    }
+}


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

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -33,7 +33,6 @@
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
@@ -53,9 +52,10 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.EmptyChildren;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -129,7 +129,7 @@
         node = new MockAbstractJcrNode(cache, uuid);
 
         // Create the children container for the node ...
-        children = new Children(uuid);
+        children = new EmptyChildren(uuid);
         stub(info.getChildren()).toReturn(children);
     }
 
@@ -149,6 +149,12 @@
         return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
     }
 
+    protected void addChild( Name childName,
+                             UUID childUuid ) {
+        children = children.with(childName, childUuid, context.getValueFactories().getPathFactory());
+        stub(info.getChildren()).toReturn(children);
+    }
+
     @Test
     public void shouldAllowVisitation() throws Exception {
         ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -285,7 +291,7 @@
     public void shouldReturnChildNodeFromGetNodeWithValidName() throws Exception {
         AbstractJcrNode child = mock(AbstractJcrNode.class);
         UUID childUuid = UUID.randomUUID();
-        children.append(cache, name("child"), childUuid);
+        addChild(name("child"), childUuid);
         stub(cache.findJcrNode(childUuid)).toReturn(child);
         assertThat(node.getNode("child"), is((Node)child));
     }
@@ -337,7 +343,7 @@
     public void shouldProvideNodeIterator() throws Exception {
         AbstractJcrNode child = mock(AbstractJcrNode.class);
         UUID childUuid = UUID.randomUUID();
-        children.append(cache, name("child"), childUuid);
+        addChild(name("child"), childUuid);
         stub(cache.findJcrNode(childUuid)).toReturn(child);
         NodeIterator iter = node.getNodes();
         assertThat(iter, notNullValue());
@@ -517,10 +523,10 @@
 
     @Test
     public void shouldReturnTrueFromHasNodeIfChildWithNameExists() throws Exception {
-        children.append(cache, name("child"), UUID.randomUUID());
+        addChild(name("child"), UUID.randomUUID());
         assertThat(node.hasNode("child[1]"), is(true));
         assertThat(node.hasNode("child[2]"), is(false));
-        children.append(cache, name("child"), UUID.randomUUID());
+        addChild(name("child"), UUID.randomUUID());
         assertThat(node.hasNode("child[2]"), is(true));
         assertThat(node.hasNode("child[3]"), is(false));
     }
@@ -537,7 +543,7 @@
 
     @Test
     public void shouldReturnTrueFromHasNodesIfThereIsAtLeastOneChild() throws Exception {
-        children.append(cache, name("child"), UUID.randomUUID());
+        addChild(name("child"), UUID.randomUUID());
         assertThat(node.hasNodes(), is(true));
     }
 
@@ -593,20 +599,14 @@
     }
 
     @Test
-    @SuppressWarnings( "unchecked" )
     public void shouldReturnTrueFromHasPropertiesIfNodeHasAtLeastOneProperty() throws Exception {
-        Map<Name, PropertyInfo> properties = mock(Map.class);
-        stub(properties.size()).toReturn(5);
-        stub(info.getProperties()).toReturn(properties);
+        stub(info.hasProperties()).toReturn(true);
         assertThat(node.hasProperties(), is(true));
     }
 
     @Test
-    @SuppressWarnings( "unchecked" )
     public void shouldReturnFalseFromHasPropertiesIfNodeHasNoProperties() throws Exception {
-        Map<Name, PropertyInfo> properties = mock(Map.class);
-        stub(properties.size()).toReturn(0);
-        stub(info.getProperties()).toReturn(properties);
+        stub(info.hasProperties()).toReturn(false);
         assertThat(node.hasProperties(), is(false));
     }
 

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -43,8 +43,8 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -36,7 +36,8 @@
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import org.jboss.dna.graph.property.basic.BasicName;
-import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.graph.property.basic.BasicPathSegment;
+import org.jboss.dna.jcr.cache.ChildNode;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -60,7 +61,7 @@
         childNodes = new ArrayList<Node>();
         for (int i = 0; i != 10; ++i) {
             UUID uuid = UUID.randomUUID();
-            ChildNode child = new ChildNode(uuid, new BasicName("", "name"), i + 1);
+            ChildNode child = new ChildNode(uuid, new BasicPathSegment(new BasicName("", "name"), i + 1));
             AbstractJcrNode node = mock(AbstractJcrNode.class);
             stub(cache.findJcrNode(uuid)).toReturn(node);
             children.add(child);

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -34,7 +34,7 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -32,7 +32,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -39,7 +39,7 @@
 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.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;

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-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-17 15:43:09 UTC (rev 778)
@@ -49,9 +49,9 @@
 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.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
@@ -212,7 +212,7 @@
         UUID uuid = dnaNode.getLocation().getUuid();
         assertThat(nodeInfo.getUuid(), is(uuid));
         assertThat(nodeInfo.getOriginalLocation().getUuid(), is(uuid));
-        Set<Name> propertyNames = nodeInfo.getProperties().keySet();
+        Set<Name> propertyNames = nodeInfo.getPropertyNames();
         for (Name propertyName : propertyNames) {
             PropertyInfo info = nodeInfo.getProperty(propertyName);
             assertThat(info.getNodeUuid(), is(uuid));
@@ -246,7 +246,7 @@
         assertThat(rootInfo.getPrimaryTypeName(), is(name("dna:root")));
         assertThat(rootInfo.getOriginalLocation().getPath().isRoot(), is(true));
         assertThat(rootInfo.getOriginalLocation().getUuid(), is(rootUuid));
-        Set<Name> rootProperties = rootInfo.getProperties().keySet();
+        Set<Name> rootProperties = rootInfo.getPropertyNames();
         assertThat(rootProperties, hasItems(name("jcr:primaryType")));
         assertSameProperties(rootInfo, root);
 



From dna-commits at lists.jboss.org  Tue Mar 17 11:45:11 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Mar 2009 11:45:11 -0400
Subject: [dna-commits] DNA SVN: r779 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <E1LjbTb-0002hC-9X@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-17 11:45:11 -0400 (Tue, 17 Mar 2009)
New Revision: 779

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-304 SessionCache Uses Graph Layer Cardinality for Properties Instead of Cardinality from JCR Property Definition

Applied the patch the corrected SessionCache to determine the multiplicity using the property definition rather than the number of values in the DNA property.

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-03-17 15:43:09 UTC (rev 778)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-17 15:45:11 UTC (rev 779)
@@ -369,7 +369,7 @@
      * @return the <i>new</i> instance of the {@link Property}; never null
      */
     private AbstractJcrProperty createAndCacheJcrPropertyFor( PropertyInfo info ) {
-        boolean multiValued = info.getProperty().isMultiple();
+        boolean multiValued = info.isMultiValued();
         JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId(), multiValued);
         assert definition != null;
         if (multiValued) {



From dna-commits at lists.jboss.org  Tue Mar 17 15:16:04 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 17 Mar 2009 15:16:04 -0400
Subject: [dna-commits] DNA SVN: r780 - in trunk/dna-jcr/src:
	main/java/org/jboss/dna/jcr/cache and 2 other directories.
Message-ID: <E1Ljelg-0003yA-M1@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-17 15:16:04 -0400 (Tue, 17 Mar 2009)
New Revision: 780

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
Log:
DNA-194 Implement update JCR capability

Added mutable versions of the classes used to store node, property and children representations.

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-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -62,6 +62,7 @@
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
+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.NodeInfo;
@@ -131,7 +132,7 @@
     private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
     private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
 
-    private final HashMap<UUID, NodeInfo> cachedNodes;
+    private final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
     private final HashMap<UUID, NodeInfo> changedNodes;
 
     public SessionCache( JcrSession session,
@@ -159,7 +160,7 @@
         this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
         this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
 
-        this.cachedNodes = new HashMap<UUID, NodeInfo>();
+        this.cachedNodes = new HashMap<UUID, ImmutableNodeInfo>();
         this.changedNodes = new HashMap<UUID, NodeInfo>();
     }
 
@@ -554,9 +555,10 @@
                             // Now process all of the nodes that we loaded, again starting at the top and going down ...
                             for (Path batchPath : pathsInBatch) {
                                 org.jboss.dna.graph.Node dnaNode = batchResults.getNode(batchPath);
-                                childInfo = createNodeInfoFrom(dnaNode, info);
-                                this.cachedNodes.put(childInfo.getUuid(), childInfo);
-                                info = childInfo;
+                                ImmutableNodeInfo originalChildInfo = createNodeInfoFrom(dnaNode, info);
+                                this.cachedNodes.put(originalChildInfo.getUuid(), originalChildInfo);
+                                childInfo = originalChildInfo;
+                                info = originalChildInfo;
                             }
                         } else {
                             // This is the last path, so do it a little more efficiently than above ...
@@ -651,12 +653,12 @@
      * @throws ItemNotFoundException if the node does not exist in the repository
      * @throws RepositoryException if there was an error obtaining this information from the repository
      */
-    protected NodeInfo loadFromGraph( UUID uuid,
-                                      NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
+    protected ImmutableNodeInfo loadFromGraph( UUID uuid,
+                                               NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
         // Load the node information from the store ...
         try {
             org.jboss.dna.graph.Node node = store.getNodeAt(uuid);
-            NodeInfo info = createNodeInfoFrom(node, parentInfo);
+            ImmutableNodeInfo info = createNodeInfoFrom(node, parentInfo);
             this.cachedNodes.put(info.getUuid(), info);
             return info;
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
@@ -681,12 +683,12 @@
      * @throws PathNotFoundException if the node does not exist in the repository
      * @throws RepositoryException if there was an error obtaining this information from the repository
      */
-    protected NodeInfo loadFromGraph( Path path,
-                                      NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
+    protected ImmutableNodeInfo loadFromGraph( Path path,
+                                               NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
         // Load the node information from the store ...
         try {
             org.jboss.dna.graph.Node node = store.getNodeAt(path);
-            NodeInfo info = createNodeInfoFrom(node, parentInfo);
+            ImmutableNodeInfo info = createNodeInfoFrom(node, parentInfo);
             this.cachedNodes.put(info.getUuid(), info);
             return info;
         } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
@@ -706,8 +708,8 @@
      * @throws RepositoryException if there is an error determining the child {@link NodeDefinition} for the supplied node,
      *         preventing the node information from being constructed
      */
-    private NodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
-                                         NodeInfo parentInfo ) throws RepositoryException {
+    private ImmutableNodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
+                                                  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();
@@ -953,7 +955,8 @@
 
         // Create the node information ...
         UUID parentUuid = parentInfo != null ? parentInfo.getUuid() : null;
-        Children children = new ImmutableChildren(parentUuid, graphNode.getChildren());
+        List<Location> locations = graphNode.getChildren();
+        Children children = locations.isEmpty() ? new EmptyChildren(parentUuid) : new ImmutableChildren(parentUuid, locations);
         props = Collections.unmodifiableMap(props);
         return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
     }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,101 @@
+/*
+ * 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.List;
+import java.util.ListIterator;
+import java.util.UUID;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A {@link NotThreadSafe non-thread safe} implementation of {@link Children} that can be modified in place. This is typically
+ * used to capture changes made within a session.
+ */
+ at NotThreadSafe
+public class ChangedChildren extends ImmutableChildren {
+
+    public ChangedChildren( Children original ) {
+        super(original);
+    }
+
+    /**
+     * Creates an empty instance.
+     * 
+     * @param parentUuid the UUID of the parent node
+     */
+    protected ChangedChildren( UUID parentUuid ) {
+        super(parentUuid);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.ImmutableChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+     *      org.jboss.dna.graph.property.PathFactory)
+     */
+    @Override
+    public ChangedChildren with( Name newChildName,
+                                 UUID newChildUuid,
+                                 PathFactory pathFactory ) {
+        // Simply add the node to this object ...
+        super.add(newChildName, newChildUuid, pathFactory);
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(org.jboss.dna.jcr.cache.ChildNode,
+     *      org.jboss.dna.graph.property.PathFactory)
+     */
+    @Override
+    public ChangedChildren without( ChildNode child,
+                                    PathFactory pathFactory ) {
+        // Make sure this object contains the child ...
+        if (!childrenByUuid.containsKey(child.getUuid())) {
+            return this;
+        }
+        // Remove the child fro this object, then adjust the remaining child node instances that follow it ...
+        Name childName = child.getName();
+        List<ChildNode> childrenWithSameName = childrenByName.get(childName);
+        int snsIndex = child.getSnsIndex();
+        if (snsIndex > childrenWithSameName.size()) {
+            // The child node (with that SNS index) is no longer here) ...
+            return this;
+        }
+        ListIterator<ChildNode> iter = childrenWithSameName.listIterator(--snsIndex);
+        assert iter.hasNext();
+        iter.next(); // start ...
+        iter.remove(); // removes the item that was last returned from 'next()'
+        while (iter.hasNext()) {
+            ChildNode next = iter.next();
+            ChildNode newNext = next.with(pathFactory.createSegment(childName, ++snsIndex));
+            iter.set(newNext);
+        }
+        return this;
+    }
+
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -27,7 +27,6 @@
 import java.util.UUID;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
 
 /**
  * Class that maintains the ordered list of {@link ChildNode} instances yet allows fast access to the children with a specified
@@ -74,15 +73,4 @@
      */
     Iterator<ChildNode> getChildren( Name name );
 
-    /**
-     * Create another Children object that is equivalent to this node but with the supplied child added.
-     * 
-     * @param newChildName the name of the new child; may not be null
-     * @param newChildUuid the UUID of the new child; may not be null
-     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
-     * @return the new Children object; never null
-     */
-    Children with( Name newChildName,
-                   UUID newChildUuid,
-                   PathFactory pathFactory );
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -37,7 +37,7 @@
 @Immutable
 public final class EmptyChildren implements Children {
 
-    private static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
+    static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
 
     private final UUID parentUuid;
 
@@ -99,13 +99,36 @@
         return EMPTY_ITERATOR;
     }
 
-    public ImmutableChildren with( Name newChildName,
-                                   UUID newChildUuid,
-                                   PathFactory pathFactory ) {
-        return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+    /**
+     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * 
+     * @param newChildName the name of the new child; may not be null
+     * @param newChildUuid the UUID of the new child; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    public ChangedChildren with( Name newChildName,
+                                 UUID newChildUuid,
+                                 PathFactory pathFactory ) {
+        ChangedChildren result = new ChangedChildren(this);
+        result.add(newChildName, newChildUuid, pathFactory);
+        return result;
     }
 
     /**
+     * Create another Children object that is equivalent to this node but without the supplied child. If the supplied child is not
+     * a current child, this method silently returns this same instance (since it has not changed).
+     * 
+     * @param child the child to be removed; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    public ChangedChildren without( ChildNode child,
+                                    PathFactory pathFactory ) {
+        return new ChangedChildren(this.parentUuid);
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Object#toString()

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -42,16 +42,14 @@
  * An immutable implementation of {@link Children}.
  */
 @Immutable
-public final class ImmutableChildren implements Children {
-    private final UUID parentUuid;
-    private final Map<UUID, ChildNode> childrenByUuid;
-    private final ListMultimap<Name, ChildNode> childrenByName;
+public class ImmutableChildren implements Children {
+    protected final UUID parentUuid;
+    protected final Map<UUID, ChildNode> childrenByUuid;
+    protected final ListMultimap<Name, ChildNode> childrenByName;
 
     public ImmutableChildren( UUID parentUuid,
                               Iterable<Location> children ) {
-        this.parentUuid = parentUuid;
-        this.childrenByUuid = new HashMap<UUID, ChildNode>();
-        this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+        this(parentUuid);
         for (Location childLocation : children) {
             UUID childUuid = childLocation.getUuid();
             Path.Segment segment = childLocation.getPath().getLastSegment();
@@ -62,10 +60,13 @@
         }
     }
 
-    protected ImmutableChildren( Children original,
-                                 Name additionalChildName,
-                                 UUID childUuid,
-                                 PathFactory pathFactory ) {
+    public ImmutableChildren( UUID parentUuid ) {
+        this.parentUuid = parentUuid;
+        this.childrenByUuid = new HashMap<UUID, ChildNode>();
+        this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+    }
+
+    protected ImmutableChildren( Children original ) {
         this.parentUuid = original.getParentUuid();
         this.childrenByUuid = new HashMap<UUID, ChildNode>();
         this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
@@ -75,11 +76,38 @@
             this.childrenByName.put(child.getName(), child);
             this.childrenByUuid.put(child.getUuid(), child);
         }
+    }
+
+    protected ImmutableChildren( Children original,
+                                 Name additionalChildName,
+                                 UUID childUuid,
+                                 PathFactory pathFactory ) {
+        this(original);
+        add(additionalChildName, childUuid, pathFactory);
+    }
+
+    /**
+     * Utility method that adds a child with the supplied name. This method is not exposed publicly, ensuring that this class
+     * remains publicly immutable. Subclasses that use this method (in places other than constructors) will no longer be
+     * {@link Immutable immutable}.
+     * 
+     * @param additionalChildName
+     * @param childUuid
+     * @param pathFactory
+     * @return the child node that was just added; never null
+     */
+    protected ChildNode add( Name additionalChildName,
+                             UUID childUuid,
+                             PathFactory pathFactory ) {
+        ChildNode existing = this.childrenByUuid.get(childUuid);
+        if (existing != null) return existing;
+
         List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName);
         Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1);
         ChildNode additionalChild = new ChildNode(childUuid, segment);
         this.childrenByName.put(additionalChildName, additionalChild);
         this.childrenByUuid.put(childUuid, additionalChild);
+        return additionalChild;
     }
 
     /**
@@ -139,13 +167,39 @@
         return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator());
     }
 
-    public ImmutableChildren with( Name newChildName,
-                                   UUID newChildUuid,
-                                   PathFactory pathFactory ) {
-        return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+    /**
+     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * 
+     * @param newChildName the name of the new child; may not be null
+     * @param newChildUuid the UUID of the new child; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    public ChangedChildren with( Name newChildName,
+                                 UUID newChildUuid,
+                                 PathFactory pathFactory ) {
+        // Create a mutable version ...
+        ChangedChildren newChildren = new ChangedChildren(this);
+        return newChildren.with(newChildName, newChildUuid, pathFactory);
     }
 
     /**
+     * Create another Children object that is equivalent to this node but without the supplied child.
+     * 
+     * @param child the child to be removed; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    public ChangedChildren without( ChildNode child,
+                                    PathFactory pathFactory ) {
+        if (this.childrenByUuid.containsKey(child.getUuid()) && this.size() == 1) {
+            return new ChangedChildren(this.parentUuid);
+        }
+        ChangedChildren newChildren = new ChangedChildren(this);
+        return newChildren.without(child, pathFactory);
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Object#toString()

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-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -62,7 +62,7 @@
     /**
      * Get the children for this node.
      * 
-     * @return the children; never null but possibly empty
+     * @return the immutable children; never null but possibly empty
      */
     public Children getChildren();
 

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-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -52,6 +52,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.cache.ChangedChildren;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.EmptyChildren;
 import org.jboss.dna.jcr.cache.NodeInfo;
@@ -151,7 +152,11 @@
 
     protected void addChild( Name childName,
                              UUID childUuid ) {
-        children = children.with(childName, childUuid, context.getValueFactories().getPathFactory());
+        if (children instanceof EmptyChildren) {
+            children = ((EmptyChildren)children).with(childName, childUuid, context.getValueFactories().getPathFactory());
+        } else if (children instanceof ChangedChildren) {
+            children = ((ChangedChildren)children).with(childName, childUuid, context.getValueFactories().getPathFactory());
+        }
         stub(info.getChildren()).toReturn(children);
     }
 

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,168 @@
+/*
+ * 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.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @param <ChildrenType> the type for the {@link #children} variable
+ */
+public abstract class AbstractChildrenTest<ChildrenType extends Children> {
+
+    protected ChildrenType children;
+    protected ExecutionContext context;
+    protected PathFactory pathFactory;
+    protected NameFactory nameFactory;
+    protected UUID parentUuid;
+    protected UUID firstChildUuid;
+    protected Name firstChildName;
+    protected ChildNode firstChild;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        pathFactory = context.getValueFactories().getPathFactory();
+        nameFactory = context.getValueFactories().getNameFactory();
+
+        parentUuid = UUID.randomUUID();
+        firstChildUuid = UUID.randomUUID();
+        firstChildName = nameFactory.create("childA");
+    }
+
+    protected Name name( String name ) {
+        return nameFactory.create(name);
+    }
+
+    protected void assertChildNodesWithName( Children children,
+                                             String childName,
+                                             Object... childNodes ) {
+        Name name = name(childName);
+        Iterator<ChildNode> iter = children.getChildren(name);
+        int expectedSnsIndex = 1;
+        for (Object expectedChild : childNodes) {
+            assertThat(iter.hasNext(), is(true));
+            ChildNode next = iter.next();
+            assertThat(next.getName(), is(name));
+            assertThat(next.getSnsIndex(), is(expectedSnsIndex++));
+            if (expectedChild instanceof ChildNode) {
+                assertThat(next, is(sameInstance(expectedChild)));
+            } else if (expectedChild instanceof Name) {
+                Name expectedName = (Name)expectedChild;
+                assertThat(next.getName(), is(expectedName));
+            } else if (expectedChild instanceof String) {
+                Name expectedName = name((String)expectedChild);
+                assertThat(next.getName(), is(expectedName));
+            }
+        }
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    protected void assertChildNodes( Children children,
+                                     Object... childNodes ) {
+        Iterator<ChildNode> iter = children.iterator();
+        Map<Name, AtomicInteger> expectedSnsIndexes = new HashMap<Name, AtomicInteger>();
+        for (Object expectedChild : childNodes) {
+            assertThat(iter.hasNext(), is(true));
+            ChildNode next = iter.next();
+            Name actualName = next.getName();
+            // Check the name ...
+            if (expectedChild instanceof ChildNode) {
+                assertThat(next, is(sameInstance(expectedChild)));
+            } else if (expectedChild instanceof Name) {
+                Name expectedName = (Name)expectedChild;
+                assertThat(actualName, is(expectedName));
+            } else if (expectedChild instanceof String) {
+                Name expectedName = name((String)expectedChild);
+                assertThat(actualName, is(expectedName));
+            }
+            // Check the SNS ...
+            AtomicInteger expectedSns = expectedSnsIndexes.get(actualName);
+            if (expectedSns == null) {
+                expectedSns = new AtomicInteger(1);
+                expectedSnsIndexes.put(actualName, expectedSns);
+            }
+            assertThat(next.getSnsIndex(), is(expectedSns.getAndIncrement()));
+        }
+        assertThat(iter.hasNext(), is(false));
+    }
+
+    protected void assertSameContent( Children children,
+                                      Children other ) {
+        Iterator<ChildNode> iter = children.iterator();
+        Iterator<ChildNode> otherIter = other.iterator();
+        while (iter.hasNext()) {
+            assertThat(otherIter.hasNext(), is(true));
+            ChildNode next = iter.next();
+            ChildNode otherNext = otherIter.next();
+            assertThat(next, is(otherNext));
+        }
+        assertThat(iter.hasNext(), is(false));
+        assertThat(otherIter.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldFindFirstChildByUuid() {
+        ChildNode firstChild = children.getChild(firstChildUuid);
+        assertThat(firstChild, is(notNullValue()));
+        assertThat(firstChild, is(sameInstance(this.firstChild)));
+        assertThat(firstChild.getUuid(), is(firstChildUuid));
+        assertThat(firstChild.getName(), is(firstChildName));
+        assertThat(firstChild.getSnsIndex(), is(1));
+        assertThat(firstChild.getSegment().getIndex(), is(1));
+        assertThat(firstChild.getSegment().getName(), is(firstChildName));
+    }
+
+    @Test
+    public void shouldFindFirstChildByName() {
+        Segment segment = pathFactory.createSegment(firstChildName, 1);
+        ChildNode firstChild = children.getChild(segment);
+        assertThat(firstChild, is(notNullValue()));
+        assertThat(firstChild, is(sameInstance(this.firstChild)));
+        assertThat(firstChild.getUuid(), is(firstChildUuid));
+        assertThat(firstChild.getName(), is(firstChildName));
+        assertThat(firstChild.getSnsIndex(), is(1));
+        assertThat(firstChild.getSegment().getIndex(), is(1));
+        assertThat(firstChild.getSegment().getName(), is(firstChildName));
+    }
+
+    @Test
+    public void shouldImplementToString() {
+        children.toString();
+    }
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,150 @@
+/*
+ * 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.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class ChangedChildrenTest extends AbstractChildrenTest<ChangedChildren> {
+
+    private ImmutableChildren original;
+
+    @Override
+    @Before
+    public void beforeEach() {
+        super.beforeEach();
+
+        original = new ImmutableChildren(parentUuid);
+        firstChild = original.add(firstChildName, firstChildUuid, pathFactory);
+
+        children = new ChangedChildren(original);
+    }
+
+    @Test
+    public void shouldHaveCorrectSize() {
+        assertThat(children.size(), is(1));
+    }
+
+    @Test
+    public void shouldHaveSameContentsAsOriginal() {
+        assertSameContent(children, original);
+    }
+
+    @Test
+    public void shouldFindChildrenByName() {
+        Iterator<ChildNode> iter = children.getChildren(firstChildName);
+        assertThat(iter.hasNext(), is(true));
+        assertThat(iter.next(), is(sameInstance(firstChild)));
+        assertThat(iter.hasNext(), is(false));
+        try {
+            iter.next();
+            fail("Failed to throw exception");
+        } catch (NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void shouldReturnSameInstanceFromWithoutIfSuppliedChildIsNotFound() {
+        ChildNode nonExistant = new ChildNode(UUID.randomUUID(), pathFactory.createSegment("some segment"));
+        assertThat(children.without(nonExistant, pathFactory), is(sameInstance((Children)children)));
+    }
+
+    @Test
+    public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
+        Children newChildren = children.without(firstChild, pathFactory);
+        assertThat(newChildren.size(), is(0));
+    }
+
+    @Test
+    public void shouldReturnSameInstanceFromWithIfSuppliedChildThatIsFoundInContainer() {
+        assertThat(children.with(firstChildName, firstChildUuid, pathFactory), is(sameInstance((Children)children)));
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldReturnIteratorThatDoesNotSupportRemoving() {
+        Iterator<ChildNode> iter = children.getChildren(firstChildName);
+        assertThat(iter.hasNext(), is(true));
+        assertThat(iter.next(), is(sameInstance(firstChild)));
+        iter.remove();
+    }
+
+    @Test
+    public void shouldReturnChangedChildrenFromWithoutIfSuppliedChildIsFound() {
+        ChildNode child2 = children.add(name("childB"), UUID.randomUUID(), pathFactory);
+        ChildNode child3 = children.add(name("childC"), UUID.randomUUID(), pathFactory);
+        ChildNode child4 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+        ChildNode child5 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+        ChildNode child6 = children.add(name("childD"), UUID.randomUUID(), pathFactory);
+
+        // Check that the children contains what we expect ...
+        assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+        assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+
+        // Remove 'child4' ...
+        Children result = children.without(child4, pathFactory);
+
+        // but the result should not have child4 ...
+        assertChildNodesWithName(result, "childA", firstChild, "childA");
+
+        // Now check that all the child nodes are in the result, in the expected order ...
+        assertChildNodes(result, firstChild, child2, child3, "childA", child6);
+    }
+
+    @Test
+    public void shouldReturnChangedChildrenFromWithIfSuppliedChildIsNotFound() {
+        // Make sure that children contains what we expect ...
+        assertChildNodes(children, firstChild);
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add a node ...
+        Children result = children.with(name("childB"), UUID.randomUUID(), pathFactory);
+        assertThat(result, is(sameInstance((Children)children)));
+        assertChildNodes(children, firstChild, "childB");
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add another node ...
+        result = children.with(name("childC"), UUID.randomUUID(), pathFactory);
+        assertThat(result, is(sameInstance((Children)children)));
+        assertChildNodes(children, firstChild, "childB", "childC");
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add another node ...
+        result = children.with(name("childA"), UUID.randomUUID(), pathFactory);
+        assertThat(result, is(sameInstance((Children)children)));
+        assertChildNodes(children, firstChild, "childB", "childC", "childA");
+        assertChildNodesWithName(children, "childA", firstChild, "childA");
+    }
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java	2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,141 @@
+/*
+ * 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.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class ImmutableChildrenTest extends AbstractChildrenTest<ImmutableChildren> {
+
+    @Override
+    @Before
+    public void beforeEach() {
+        super.beforeEach();
+
+        children = new ImmutableChildren(parentUuid);
+        firstChild = children.add(firstChildName, firstChildUuid, pathFactory);
+    }
+
+    @Test
+    public void shouldHaveCorrectSize() {
+        assertThat(children.size(), is(1));
+    }
+
+    @Test
+    public void shouldFindChildrenByName() {
+        Iterator<ChildNode> iter = children.getChildren(firstChildName);
+        assertThat(iter.hasNext(), is(true));
+        assertThat(iter.next(), is(sameInstance(firstChild)));
+        assertThat(iter.hasNext(), is(false));
+        try {
+            iter.next();
+            fail("Failed to throw exception");
+        } catch (NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldReturnIteratorThatDoesNotSupportRemoving() {
+        Iterator<ChildNode> iter = children.getChildren(firstChildName);
+        assertThat(iter.hasNext(), is(true));
+        assertThat(iter.next(), is(sameInstance(firstChild)));
+        iter.remove();
+    }
+
+    @Test
+    public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
+        Children newChildren = children.without(firstChild, pathFactory);
+        assertThat(newChildren.size(), is(0));
+    }
+
+    @Test
+    public void shouldReturnChangedChildrenFromWithoutIfSuppliedChildIsFound() {
+        ChildNode child2 = children.add(name("childB"), UUID.randomUUID(), pathFactory);
+        ChildNode child3 = children.add(name("childC"), UUID.randomUUID(), pathFactory);
+        ChildNode child4 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+        ChildNode child5 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+        ChildNode child6 = children.add(name("childD"), UUID.randomUUID(), pathFactory);
+
+        // Check that the children contains what we expect ...
+        assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+        assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+
+        // Remove 'child4' ...
+        Children result = children.without(child4, pathFactory);
+
+        // the original should not have been changed ...
+        assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+        // but the result should not have child4 ...
+        assertChildNodesWithName(result, "childA", firstChild, "childA");
+
+        // Now check that all the child nodes are still in the original, in the same order ...
+        assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+
+        // Now check that all the child nodes are in the result, in the expected order ...
+        assertChildNodes(result, firstChild, child2, child3, "childA", child6);
+    }
+
+    @Test
+    public void shouldReturnChangedChildrenFromWithIfSuppliedChildIsNotFound() {
+        // Make sure that children contains what we expect ...
+        assertChildNodes(children, firstChild);
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add a node ...
+        ChangedChildren newChildren = children.with(name("childB"), UUID.randomUUID(), pathFactory);
+        assertChildNodes(newChildren, firstChild, "childB");
+        assertChildNodesWithName(newChildren, "childA", firstChild);
+        // And make sure 'children' hasn't changed ...
+        assertChildNodes(children, firstChild);
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add another node ...
+        newChildren = newChildren.with(name("childC"), UUID.randomUUID(), pathFactory);
+        assertChildNodes(newChildren, firstChild, "childB", "childC");
+        assertChildNodesWithName(newChildren, "childA", firstChild);
+        // And make sure 'children' hasn't changed ...
+        assertChildNodes(children, firstChild);
+        assertChildNodesWithName(children, "childA", firstChild);
+
+        // Add another node ...
+        newChildren = newChildren.with(name("childA"), UUID.randomUUID(), pathFactory);
+        assertChildNodes(newChildren, firstChild, "childB", "childC", "childA");
+        assertChildNodesWithName(newChildren, "childA", firstChild, "childA");
+        // And make sure 'children' hasn't changed ...
+        assertChildNodes(children, firstChild);
+        assertChildNodesWithName(children, "childA", firstChild);
+    }
+
+}


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



From dna-commits at lists.jboss.org  Wed Mar 18 00:38:45 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Mar 2009 00:38:45 -0400
Subject: [dna-commits] DNA SVN: r781 -
	trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
Message-ID: <E1LjnYD-00087C-3m@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-18 00:38:44 -0400 (Wed, 18 Mar 2009)
New Revision: 781

Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
Log:
DNA-304 SessionCache Uses Graph Layer Cardinality for Properties Instead of Cardinality from JCR Property Definition

Applied the changes to the test cases.

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-03-17 19:16:04 UTC (rev 780)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-18 04:38:44 UTC (rev 781)
@@ -48,6 +48,7 @@
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.ValueFactory;
 import javax.security.auth.Subject;
@@ -58,6 +59,7 @@
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -101,6 +103,7 @@
         graph.create("/a").and().create("/a/b").and().create("/a/b/c");
         graph.set("booleanProperty").on("/a/b").to(true);
         graph.set("stringProperty").on("/a/b/c").to("value");
+        graph.set("jcr:mixinTypes").on("/a/b").to("mix:referenceable");
         graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
 
         // Make sure the path to the namespaces exists ...
@@ -392,7 +395,32 @@
         // assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
     }
 
+    /**
+     * DNA JCR implementation is supposed to have root type named {@link DnaLexicon#ROOT}.
+     * 
+     * @throws Exception if an error occurs during the test
+     */
     @Test
+    public void rootNodeShouldHaveProperType() throws Exception {
+        Node rootNode = session.getRootNode();
+
+        assertTrue(rootNode.getPrimaryNodeType().equals(session.nodeTypeManager().getNodeType(DnaLexicon.ROOT)));
+
+    }
+
+    /**
+     * DNA JCR implementation is supposed to have a referenceable root.
+     * 
+     * @throws RepositoryException if an error occurs during the test
+     */
+    @Test
+    public void rootNodeShouldBeReferenceable() throws RepositoryException {
+        Node rootNode = session.getRootNode();
+
+        assertTrue(rootNode.getPrimaryNodeType().isNodeType(JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry())));
+    }
+
+    @Test
     public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
         OutputStream os = new ByteArrayOutputStream();
 
@@ -401,4 +429,22 @@
         String fileContents = os.toString();
         assertTrue(fileContents.contains(MULTI_LINE_VALUE));
     }
+
+    @Test
+    public void shouldUseJcrCardinalityPerPropertyDefinition() throws Exception {
+
+        // Verify that the node does exist in the source ...
+        Path pathToNode = context.getValueFactories().getPathFactory().create("/a/b");
+        Node carsNode = session.getNode(pathToNode);
+
+        String mixinTypesName = JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext().getNamespaceRegistry());
+        Property mixinTypes = carsNode.getProperty(mixinTypesName);
+
+        // Check that the underlying DNA property has one value - this is a test of the testing data
+        assertThat(((AbstractJcrProperty)mixinTypes).property().isMultiple(), is(false));
+        // Check that the JCR property is a MultiProperty - this call will throw an exception if the property is not.
+        mixinTypes.getValues();
+
+    }
+
 }



From dna-commits at lists.jboss.org  Wed Mar 18 12:54:55 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Mar 2009 12:54:55 -0400
Subject: [dna-commits] DNA SVN: r782 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr/cache and 1 other directory.
Message-ID: <E1Ljz2d-0008J1-3K@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-18 12:54:54 -0400 (Wed, 18 Mar 2009)
New Revision: 782

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.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/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
Log:
DNA-194 Implement update JCR capability

Improved the mutable versions of the cached reprentations, and added a large number of unit tests.  The mutable objects should be ready to track adding/removing children and adding/changing/removing properties.  Making the SessionCache to use them is next.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -68,31 +68,34 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(org.jboss.dna.jcr.cache.ChildNode,
-     *      org.jboss.dna.graph.property.PathFactory)
+     * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
      */
     @Override
-    public ChangedChildren without( ChildNode child,
+    public ChangedChildren without( UUID childUuid,
                                     PathFactory pathFactory ) {
-        // Make sure this object contains the child ...
-        if (!childrenByUuid.containsKey(child.getUuid())) {
+        // Remove the object that has the same UUID (regardless of the current SNS index) ...
+        ChildNode toBeRemoved = childrenByUuid.get(childUuid);
+        if (toBeRemoved == null) {
             return this;
         }
-        // Remove the child fro this object, then adjust the remaining child node instances that follow it ...
-        Name childName = child.getName();
+        // Remove the child from this object, then adjust the remaining child node instances that follow it ...
+        Name childName = toBeRemoved.getName();
         List<ChildNode> childrenWithSameName = childrenByName.get(childName);
-        int snsIndex = child.getSnsIndex();
+        int snsIndex = toBeRemoved.getSnsIndex();
         if (snsIndex > childrenWithSameName.size()) {
             // The child node (with that SNS index) is no longer here) ...
             return this;
         }
         ListIterator<ChildNode> iter = childrenWithSameName.listIterator(--snsIndex);
         assert iter.hasNext();
-        iter.next(); // start ...
+        ChildNode willBeRemoved = iter.next();
+        assert willBeRemoved == toBeRemoved;
+        childrenByUuid.remove(toBeRemoved.getUuid());
         iter.remove(); // removes the item that was last returned from 'next()'
         while (iter.hasNext()) {
             ChildNode next = iter.next();
             ChildNode newNext = next.with(pathFactory.createSegment(childName, ++snsIndex));
+            childrenByUuid.put(newNext.getUuid(), newNext);
             iter.set(newNext);
         }
         return this;

Added: 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	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,323 @@
+/*
+ * 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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+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.Path.Segment;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ * <p>
+ * Each instance maintains a reference to the original (usually immutable) NodeInfo representation that was probably read from the
+ * repository.
+ */
+ at Immutable
+public class ChangedNodeInfo implements NodeInfo {
+
+    protected static final PropertyInfo DELETED_PROPERTY = null;
+
+    /**
+     * A reference to the original representation of the node.
+     */
+    private final NodeInfo original;
+
+    /**
+     * The new parent for this node if it has been changed, or null if the parent has not be changed.
+     */
+    private UUID newParent;
+
+    /**
+     * The updated children, or null if the children have not been changed from the original's.
+     */
+    private ChangedChildren changedChildren;
+
+    /**
+     * This map, if it is non-null, contains the changed properties, overriding whatever is in the original. If a property is
+     * removed from the original, an entry is added to this map with the name of the removed property and a null PropertyInfo.
+     */
+    private Map<Name, PropertyInfo> changedProperties;
+
+    /**
+     * Create an immutable NodeInfo instance.
+     * 
+     * @param original the original node information, may not be null
+     */
+    public ChangedNodeInfo( NodeInfo original ) {
+        assert original != null;
+        this.original = original;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getOriginalLocation()
+     */
+    public Location getOriginalLocation() {
+        return original.getOriginalLocation();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getUuid()
+     */
+    public UUID getUuid() {
+        return original.getUuid();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getParent()
+     */
+    public UUID getParent() {
+        // Even if this is used for recording changes to the root node (which has no parent),
+        // the root node cannot be moved to a different node (and no other node can be moved to
+        // the root). Therefore, if this represents the root node, the original's parent UUID will
+        // be the correct parent (null), and this representation will not need to have a different
+        // (non-null) value.
+        if (newParent != null) return newParent;
+        return original.getParent();
+    }
+
+    /**
+     * Record that this node has been moved under a new parent. This method does <i>not</i> change the ChildNode references in the
+     * old or new parent.
+     * 
+     * @param parent the new parent, or null if the original's parent should be used
+     * @return the previous parent (either the original's or the last new parent); may be null
+     */
+    public UUID setParent( UUID parent ) {
+        UUID result = newParent != null ? newParent : original.getParent(); // may still be null
+        newParent = parent;
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPrimaryTypeName()
+     */
+    public Name getPrimaryTypeName() {
+        return original.getPrimaryTypeName();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
+     */
+    public NodeDefinitionId getDefinitionId() {
+        return original.getDefinitionId();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
+     */
+    public Children getChildren() {
+        if (changedChildren != null) return changedChildren;
+        return original.getChildren();
+    }
+
+    /**
+     * 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
+     * @param childUuid the UUID of the child that is to be added; may not be null
+     * @param factory the path factory that should be used to create a {@link Segment} for the new {@link ChildNode} object
+     * @return the child node that was just added; never null
+     */
+    public ChildNode addChild( Name childName,
+                               UUID childUuid,
+                               PathFactory factory ) {
+        if (changedChildren == null) {
+            // We need to capture the original children as a changed contained ...
+            changedChildren = new ChangedChildren(original.getChildren());
+        }
+        return changedChildren.add(childName, childUuid, factory);
+    }
+
+    /**
+     * Remove a child from the children. This method only uses the child's UUID to identify the contained ChildNode instance that
+     * should be removed.
+     * <p>
+     * Note that this method returns the new {@link Children} container, which is the same as would be returned by
+     * {@link #getChildren()} called immediately after this method.
+     * </p>
+     * 
+     * @param childUUID the UUID of the child that is to be removed; may not be null
+     * @param factory the path factory that should be used to create a {@link Segment} for replacement {@link ChildNode} objects
+     *        for nodes with the same name that and higher same-name-sibiling indexes.
+     * @return the Children object that has the modified children
+     */
+    public Children removeChild( UUID childUUID,
+                                 PathFactory factory ) {
+        if (changedChildren == null) {
+            // Create the changed children. First check whether there are 0 or 1 child ...
+            Children existing = original.getChildren();
+            int numExisting = existing.size();
+            if (numExisting == 0) {
+                // nothing to do, so return the original's children
+                return existing;
+            }
+            if (existing.getChild(childUUID) == null) {
+                // The requested child doesn't exist in the children, so return silently ...
+                return existing;
+            }
+            if (numExisting == 1) {
+                // We're removing the only child in the original ...
+                changedChildren = new ChangedChildren(existing.getParentUuid());
+                return changedChildren;
+            }
+            // There is at least one child, so create the new children container ...
+            assert existing instanceof InternalChildren;
+            InternalChildren internal = (InternalChildren)existing;
+            changedChildren = internal.without(childUUID, factory);
+        } else {
+            changedChildren = changedChildren.without(childUUID, factory);
+        }
+        return changedChildren;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#hasProperties()
+     */
+    public boolean hasProperties() {
+        if (changedProperties == null) return original.hasProperties();
+        int numUnchanged = original.getPropertyCount();
+        int numChangedOrDeleted = changedProperties.size();
+        if (numUnchanged > numChangedOrDeleted) return true; // more unchanged than could be deleted
+        // They could all be changed or deleted, so we need to find one changed property ...
+        for (Map.Entry<Name, PropertyInfo> entry : changedProperties.entrySet()) {
+            if (entry.getValue() != DELETED_PROPERTY) return true;
+        }
+        return false; // all properties must have been deleted ...
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyCount()
+     */
+    public int getPropertyCount() {
+        int numUnchanged = original.getPropertyCount();
+        if (changedProperties == null) return numUnchanged;
+        return getPropertyNames().size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
+     */
+    public Set<Name> getPropertyNames() {
+        if (changedProperties != null) {
+            Set<Name> result = new HashSet<Name>(original.getPropertyNames());
+            for (Map.Entry<Name, PropertyInfo> entry : changedProperties.entrySet()) {
+                if (entry.getValue() != DELETED_PROPERTY) {
+                    result.add(entry.getKey());
+                } else {
+                    result.remove(entry.getKey());
+                }
+            }
+            return result; // don't make unmod wrapper, since we've already made a copy ...
+        }
+        return original.getPropertyNames();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getProperty(org.jboss.dna.graph.property.Name)
+     */
+    public PropertyInfo getProperty( Name name ) {
+        if (changedProperties != null && changedProperties.containsKey(name)) {
+            return changedProperties.get(name); // either the changed PropertyInfo, or null if property was deleted
+        }
+        return original.getProperty(name);
+    }
+
+    public PropertyInfo setProperty( PropertyInfo newProperty ) {
+        Name name = newProperty.getPropertyName();
+        if (changedProperties == null) {
+            // There were no changes made yet ...
+
+            // Create the map of changed properties ...
+            changedProperties = new HashMap<Name, PropertyInfo>();
+            changedProperties.put(name, newProperty);
+
+            // And return the original property (or null if there was none) ...
+            return original.getProperty(name);
+        }
+        // The property may already have been changed, in which case we need to return the changed one ...
+        if (changedProperties.containsKey(name)) {
+            PropertyInfo changed = changedProperties.put(name, null);
+            // The named property was indeed deleted ...
+            return changed;
+        }
+        // Otherwise, the property was not yet changed or deleted ...
+        PropertyInfo changed = original.getProperty(name);
+        changedProperties.put(name, newProperty);
+        return changed;
+    }
+
+    public PropertyInfo removeProperty( Name name ) {
+        if (changedProperties == null) {
+            // Make sure the property was in the original ...
+            PropertyInfo existing = original.getProperty(name);
+            if (existing == null) {
+                // The named property didn't exist in the original, nor was it added and deleted in this object ...
+                return null;
+            }
+
+            // Create the map of changed properties ...
+            changedProperties = new HashMap<Name, PropertyInfo>();
+            changedProperties.put(name, DELETED_PROPERTY);
+            return existing;
+        }
+        // The property may already have been changed, in which case we need to return the changed one ...
+        if (changedProperties.containsKey(name)) {
+            PropertyInfo changed = changedProperties.put(name, null);
+            // The named property was indeed deleted ...
+            return changed;
+        }
+        // Otherwise, the property was not yet changed or deleted ...
+        PropertyInfo changed = original.getProperty(name);
+        changedProperties.put(name, null);
+        return changed;
+    }
+}


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

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -29,13 +29,19 @@
 import org.jboss.dna.graph.property.Path;
 
 /**
- * 
+ * The representation of a child node. This is an immutable representation of a child node within the collection of its siblings
+ * as the collection appeared at some point in time. This should be used as a guide to determine how long to hold onto references.
+ * <p>
+ * For example, adding and removing children may affect the {@link #getSnsIndex() same-name-sibling index} of the children, so
+ * these kinds of operations will result in the replacement of old ChildObject instances. Therefore, clients should generally find
+ * the ChildNode instances in a {@link Children} container, use the ChildNode objects quickly, then discard their references.
+ * </p>
+ * <p>
+ * There may be times when a client does wish to keep a representation of a ChildNode as it appeared at some moment in time, and
+ * so it may want to hold onto references to ChildNode objects for longer durations. This is fine, as long as it is understood
+ * that at some point the referenced ChildNode may no longer represent the current state.
+ * </p>
  */
-/**
- * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded. Clients
- * should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects and
- * replacing them with new instances.
- */
 @Immutable
 public final class ChildNode {
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -35,7 +35,7 @@
  * An immutable implementation of {@link Children}.
  */
 @Immutable
-public final class EmptyChildren implements Children {
+public final class EmptyChildren implements Children, InternalChildren {
 
     static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
 
@@ -100,12 +100,10 @@
     }
 
     /**
-     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * {@inheritDoc}
      * 
-     * @param newChildName the name of the new child; may not be null
-     * @param newChildUuid the UUID of the new child; may not be null
-     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
-     * @return the new Children object; never null
+     * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+     *      org.jboss.dna.graph.property.PathFactory)
      */
     public ChangedChildren with( Name newChildName,
                                  UUID newChildUuid,
@@ -116,14 +114,11 @@
     }
 
     /**
-     * Create another Children object that is equivalent to this node but without the supplied child. If the supplied child is not
-     * a current child, this method silently returns this same instance (since it has not changed).
+     * {@inheritDoc}
      * 
-     * @param child the child to be removed; may not be null
-     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
-     * @return the new Children object; never null
+     * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
      */
-    public ChangedChildren without( ChildNode child,
+    public ChangedChildren without( UUID childUuid,
                                     PathFactory pathFactory ) {
         return new ChangedChildren(this.parentUuid);
     }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -42,7 +42,7 @@
  * An immutable implementation of {@link Children}.
  */
 @Immutable
-public class ImmutableChildren implements Children {
+public class ImmutableChildren implements Children, InternalChildren {
     protected final UUID parentUuid;
     protected final Map<UUID, ChildNode> childrenByUuid;
     protected final ListMultimap<Name, ChildNode> childrenByName;
@@ -168,12 +168,10 @@
     }
 
     /**
-     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * {@inheritDoc}
      * 
-     * @param newChildName the name of the new child; may not be null
-     * @param newChildUuid the UUID of the new child; may not be null
-     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
-     * @return the new Children object; never null
+     * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+     *      org.jboss.dna.graph.property.PathFactory)
      */
     public ChangedChildren with( Name newChildName,
                                  UUID newChildUuid,
@@ -184,19 +182,17 @@
     }
 
     /**
-     * Create another Children object that is equivalent to this node but without the supplied child.
+     * {@inheritDoc}
      * 
-     * @param child the child to be removed; may not be null
-     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
-     * @return the new Children object; never null
+     * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
      */
-    public ChangedChildren without( ChildNode child,
+    public ChangedChildren without( UUID childUuid,
                                     PathFactory pathFactory ) {
-        if (this.childrenByUuid.containsKey(child.getUuid()) && this.size() == 1) {
+        if (this.childrenByUuid.containsKey(childUuid) && this.size() == 1) {
             return new ChangedChildren(this.parentUuid);
         }
         ChangedChildren newChildren = new ChangedChildren(this);
-        return newChildren.without(child, pathFactory);
+        return newChildren.without(childUuid, pathFactory);
     }
 
     /**

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-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -138,6 +138,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyCount()
+     */
+    public int getPropertyCount() {
+        return this.properties.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
      */
     public Set<Name> getPropertyNames() {

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -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.
+ *
+ * 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.UUID;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * An internal interface for the {@link Children} implementations. Methods on this interface are used internally and should not be
+ * used by components or clients.
+ */
+interface InternalChildren extends Children {
+
+    /**
+     * Create another Children object that is equivalent to this node but with the supplied child added.
+     * 
+     * @param newChildName the name of the new child; may not be null
+     * @param newChildUuid the UUID of the new child; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    ChangedChildren with( Name newChildName,
+                          UUID newChildUuid,
+                          PathFactory pathFactory );
+
+    /**
+     * Create another Children object that is equivalent to this node but without the supplied child.
+     * 
+     * @param childUuid the UUID of the child to be removed; may not be null
+     * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+     * @return the new Children object; never null
+     */
+    ChangedChildren without( UUID childUuid,
+                             PathFactory pathFactory );
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.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-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -30,7 +30,7 @@
 import org.jboss.dna.jcr.NodeDefinitionId;
 
 /**
- * The information that describes a node. This is the information that is kept in the cache.
+ * A representation of a node. This is the information that is kept in the cache.
  */
 public interface NodeInfo {
 
@@ -60,7 +60,9 @@
     public NodeDefinitionId getDefinitionId();
 
     /**
-     * Get the children for this node.
+     * Get the children for this node. Generally, clients should not hold onto the returned object but instead should simply use
+     * it and discard the reference. This is because implementations are not required to return the same instance with each call
+     * (although immutable implementations are expected to always return the same instance).
      * 
      * @return the immutable children; never null but possibly empty
      */
@@ -74,6 +76,13 @@
     public boolean hasProperties();
 
     /**
+     * Return the number of properties on this node.
+     * 
+     * @return the number of properties; never negative
+     */
+    public int getPropertyCount();
+
+    /**
      * Get the names of the properties that are owned by this node.
      * 
      * @return the unmodifiable set of property names

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -78,12 +78,12 @@
     @Test
     public void shouldReturnSameInstanceFromWithoutIfSuppliedChildIsNotFound() {
         ChildNode nonExistant = new ChildNode(UUID.randomUUID(), pathFactory.createSegment("some segment"));
-        assertThat(children.without(nonExistant, pathFactory), is(sameInstance((Children)children)));
+        assertThat(children.without(nonExistant.getUuid(), pathFactory), is(sameInstance((Children)children)));
     }
 
     @Test
     public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
-        Children newChildren = children.without(firstChild, pathFactory);
+        Children newChildren = children.without(firstChild.getUuid(), pathFactory);
         assertThat(newChildren.size(), is(0));
     }
 
@@ -113,7 +113,7 @@
         assertChildNodesWithName(children, "childA", firstChild, child4, child5);
 
         // Remove 'child4' ...
-        Children result = children.without(child4, pathFactory);
+        Children result = children.without(child4.getUuid(), pathFactory);
 
         // but the result should not have child4 ...
         assertChildNodesWithName(result, "childA", firstChild, "childA");

Added: 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	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,629 @@
+/*
+ * 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.IsNot.not;
+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.hasChild;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingNames.hasChildren;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingUuids.hasChild;
+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.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.NodeDefinitionId;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class ChangedNodeInfoTest {
+
+    private ExecutionContext context;
+    private PathFactory pathFactory;
+    private NodeInfo original;
+    private UUID uuid;
+    private Name primaryTypeName;
+    private Location location;
+    private NodeDefinitionId definitionId;
+    private ChangedChildren children;
+    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");
+        definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"));
+        children = new ChangedChildren(uuid);
+        properties = new HashMap<Name, PropertyInfo>();
+        original = new ImmutableNodeInfo(location, primaryTypeName, definitionId, uuid, children, properties);
+
+        // Create the changed node representation ...
+        changes = new ChangedNodeInfo(original);
+    }
+
+    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);
+        return propertyInfo;
+    }
+
+    protected ChildNode makeChildInOriginal( String childName ) {
+        ChildNode newChild = children.add(name(childName), UUID.randomUUID(), pathFactory);
+        return newChild;
+    }
+
+    protected ChildNode addChildInChanged( String childName ) {
+        ChildNode newChild = changes.addChild(name(childName), UUID.randomUUID(), pathFactory);
+        // Make sure that the 'changes' object no longer returns the original object's children object
+        assertThat(changes.getChildren(), is(not(sameInstance(original.getChildren()))));
+        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(sameInstance(changes.getChildren())));
+        // Verify it no longer exists ...
+        assertThat(changes.getChildren().getChild(child.getUuid()), is(nullValue()));
+    }
+
+    @Test
+    public void shouldHaveLocationFromOriginal() {
+        assertThat(changes.getOriginalLocation(), is(sameInstance(location)));
+    }
+
+    @Test
+    public void shouldHaveParentUuidFromOriginal() {
+        assertThat(changes.getParent(), is(sameInstance(uuid)));
+    }
+
+    @Test
+    public void shouldHavePrimaryTypeNameFromOriginal() {
+        assertThat(changes.getPrimaryTypeName(), is(sameInstance(primaryTypeName)));
+    }
+
+    @Test
+    public void shouldHaveNodeDefinitionIdFromOriginal() {
+        assertThat(changes.getDefinitionId(), is(sameInstance(definitionId)));
+    }
+
+    @Test
+    public void shouldHaveChildrenFromOriginal() {
+        assertThat(changes.getChildren(), is(sameInstance((Children)children)));
+
+        ChildNode childA = makeChildInOriginal("childA");
+        assertThat(changes.getChildren().size(), is(1));
+        assertThat(changes.getChildren().getChild(childA.getUuid()), is(sameInstance(childA)));
+        assertThat(changes.getChildren(), hasChild(segment("childA[1]")));
+        assertThat(changes.getChildren(), hasChild(childA.getUuid()));
+    }
+
+    @Test
+    public void shouldHaveChildrenAfterAddingChild() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("childA");
+        assertThat(changes.getChildren().size(), is(3));
+
+        // Add child ...
+        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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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();
+        Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+        assertThat(after.size(), is(beforeSize));
+        assertThat(after, is(sameInstance(before)));
+        assertThat(after, is(sameInstance(changes.getChildren())));
+    }
+
+    @Test
+    @SuppressWarnings( "unused" )
+    public void shouldNotRemoveFromNodeInfoWithSomeChildChangesAChildThatMatchesSegmentButNotUuid() {
+        // Set up the original ...
+        ChildNode childA1 = makeChildInOriginal("childA");
+        ChildNode childB1 = makeChildInOriginal("childB");
+        ChildNode childA2 = makeChildInOriginal("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();
+        Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+        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);
+        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/ChangedNodeInfoTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java	2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -76,7 +76,7 @@
 
     @Test
     public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
-        Children newChildren = children.without(firstChild, pathFactory);
+        Children newChildren = children.without(firstChild.getUuid(), pathFactory);
         assertThat(newChildren.size(), is(0));
     }
 
@@ -93,7 +93,7 @@
         assertChildNodesWithName(children, "childA", firstChild, child4, child5);
 
         // Remove 'child4' ...
-        Children result = children.without(child4, pathFactory);
+        Children result = children.without(child4.getUuid(), pathFactory);
 
         // the original should not have been changed ...
         assertChildNodesWithName(children, "childA", firstChild, child4, child5);

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,84 @@
+/*
+ * 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.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.basic.BasicPathSegment;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * A JUnit {@link TypeSafeMatcher matcher} that can be used to assert that a NodeInfo (or other
+ * <code>Iterable&lt;ChildNode></code>) has {@link ChildNode} instances with specific {@link ChildNode#getSegment() names}.
+ */
+public class IsNodeInfoWithChildrenHavingNames extends TypeSafeMatcher<Children> {
+    private final Matcher<Iterable<Path.Segment>> childMatcher;
+
+    public IsNodeInfoWithChildrenHavingNames( Matcher<Iterable<Path.Segment>> childMatcher ) {
+        this.childMatcher = childMatcher;
+    }
+
+    @Override
+    public boolean matchesSafely( Children children ) {
+        List<Path.Segment> childSegments = new ArrayList<Path.Segment>(children.size());
+        for (ChildNode child : children) {
+            childSegments.add(child.getSegment());
+        }
+        return childMatcher.matches(childSegments);
+    }
+
+    public void describeTo( Description description ) {
+        description.appendText("children").appendDescriptionOf(childMatcher);
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingNames hasChild( Name name,
+                                                              int sameNameSiblingIndex ) {
+        Path.Segment segment = new BasicPathSegment(name, sameNameSiblingIndex);
+        return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItem(segment));
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingNames hasChild( Path.Segment child ) {
+        return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItem(child));
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingNames hasChildren( Path.Segment... childSegments ) {
+        return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItems(childSegments));
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingNames isEmpty() {
+        Path.Segment[] childSegments = new Path.Segment[] {};
+        return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItems(childSegments));
+    }
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java	2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,75 @@
+/*
+ * 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.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * A JUnit {@link TypeSafeMatcher matcher} that can be used to assert that a NodeInfo (or other
+ * <code>Iterable&lt;ChildNode></code>) has {@link ChildNode} instances with specific {@link ChildNode#getUuid() UUIDs}.
+ */
+public class IsNodeInfoWithChildrenHavingUuids extends TypeSafeMatcher<Children> {
+    private final Matcher<Iterable<UUID>> childMatcher;
+
+    public IsNodeInfoWithChildrenHavingUuids( Matcher<Iterable<UUID>> childMatcher ) {
+        this.childMatcher = childMatcher;
+    }
+
+    @Override
+    public boolean matchesSafely( Children children ) {
+        List<UUID> childSegments = new ArrayList<UUID>(children.size());
+        for (ChildNode child : children) {
+            childSegments.add(child.getUuid());
+        }
+        return childMatcher.matches(childSegments);
+    }
+
+    public void describeTo( Description description ) {
+        description.appendText("children with UUIDs").appendDescriptionOf(childMatcher);
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingUuids hasChild( UUID uuid ) {
+        return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItem(uuid));
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingUuids hasChildren( UUID... uuids ) {
+        return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItems(uuids));
+    }
+
+    @Factory
+    public static IsNodeInfoWithChildrenHavingUuids isEmpty() {
+        UUID[] uuids = new UUID[] {};
+        return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItems(uuids));
+    }
+
+}


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



From dna-commits at lists.jboss.org  Wed Mar 18 13:05:16 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 18 Mar 2009 13:05:16 -0400
Subject: [dna-commits] DNA SVN: r783 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LjzCe-0003uC-DT@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-18 13:05:16 -0400 (Wed, 18 Mar 2009)
New Revision: 783

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Modified:
   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/JcrValue.java
   trunk/dna-jcr/src/test/resources/vehicles.xml
Log:
DNA-305 JcrNodeType.canSetProperty Does Not Enforce Constraints

Applied the patch, which is an interim commit to add in the constraint checking.  Future changes will remove the JcrSession references from JcrNodeTypeManager, JcrPropertyDefinition, etc. under DNA-302.

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-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -29,6 +29,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.Stack;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
@@ -413,6 +414,35 @@
     }
 
     /**
+     * Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
+     * conversion rules in section 6.2.6 of the JCR 1.0 specification AND <code>value</code> satisfies the constraints (if any)
+     * for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
+     * will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
+     * type specified in <code>value.getType()</code>.
+     * 
+     * @param propertyDefinition the property definition to validate against
+     * @param value the value to be validated
+     * @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
+     *         satisfies the constraints for the property (if any exist).
+     * @see PropertyDefinition#getValueConstraints()
+     * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+     */
+    private boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
+                                                        Value value ) {
+        try {
+            assert value instanceof JcrValue : "Illegal implementation of Value interface";
+            ((JcrValue)value).asType(propertyDefinition.getRequiredType());
+
+            return propertyDefinition.satisfiesConstraints(value);
+
+        } catch (javax.jcr.ValueFormatException vfe) {
+            // Cast failed
+            return false;
+        }
+
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value)
@@ -440,14 +470,7 @@
             return !property.isMandatory();
         }
 
-        try {
-            assert value instanceof JcrValue : "Illegal implementation of Value interface";
-            ((JcrValue)value).asType(property.getRequiredType());
-        } catch (javax.jcr.ValueFormatException vfe) {
-            // Cast failed
-            return false;
-        }
-        return true;
+        return canCastToTypeAndMatchesConstraints(property, value);
     }
 
     /**
@@ -480,11 +503,7 @@
 
         for (int i = 0; i < values.length; i++) {
             if (values[i] != null) {
-                try {
-                    assert values[i] instanceof JcrValue : "Illegal implementation of Value interface";
-                    ((JcrValue)values[i]).asType(property.getRequiredType());
-                } catch (javax.jcr.ValueFormatException vfe) {
-                    // Cast failed
+                if (!canCastToTypeAndMatchesConstraints(property, values[i])) {
                     return false;
                 }
             }

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-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -23,11 +23,24 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.regex.Pattern;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+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.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.JodaDateTime;
 /**
  * DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
  * through its constructor.
@@ -40,6 +53,7 @@
     private final String[] valueConstraints;
     private final boolean multiple;
     private PropertyDefinitionId id;
+    private ConstraintChecker checker = null;
 
     JcrPropertyDefinition( JcrSession session,
                            JcrNodeType declaringNodeType,
@@ -120,4 +134,432 @@
                                          this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
                                          this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
     }
+
+    boolean satisfiesConstraints( Value value ) {
+        assert value instanceof JcrValue;
+
+        if (valueConstraints == null || valueConstraints.length == 0) {
+            return true;
+        }
+
+        // Neither the 1.0 or 2.0 specification formally prohibit constraints on properties with no required type.
+        int type = requiredType == PropertyType.UNDEFINED ? value.getType() : requiredType;
+
+        /*
+         * Keep a method-local reference to the constraint checker in case another thread attempts to concurrently
+         * check the constraints with a different required type.
+         */
+        ConstraintChecker checker = this.checker;
+
+        if (checker == null || checker.getType() != type) {
+            checker = createChecker(session, type, valueConstraints);
+            this.checker = checker;
+        }
+
+        try {
+            return checker.matches(value);
+        } catch (ValueFormatException vfe) {
+            // The value was so wonky that we couldn't even convert it to an appropriate type
+            return false;
+        }
+    }
+
+    /**
+     * Returns a {@link ConstraintChecker} that will interpret the constraints described by <code>valueConstraints</code> using
+     * the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type indicated by <code>type</code> (where
+     * <code>type</code> is a value from {@link PropertyType}) for the given <code>session</code>. The session is required to
+     * handle the UUID lookups for reference constraints and provides the {@link ExecutionContext} that is used to provide
+     * namespace mappings and value factories for the other constraint checkers.
+     * 
+     * @param session the current session
+     * @param type the type of constraint checker that should be created (based on values from {@link PropertyType}).
+     *        Type-specific semantics are defined in section 6.7.16 of the JCR 1.0 specification.
+     * @param valueConstraints the constraints for the node as provided by {@link PropertyDefinition#getValueConstraints()}.
+     * @return a constraint checker that matches the given parameters
+     */
+    private ConstraintChecker createChecker( JcrSession session,
+                                                   int type,
+                                                   String[] valueConstraints ) {
+        ExecutionContext context = session.getExecutionContext();
+
+        switch (type) {
+            case PropertyType.BINARY:
+                return new BinaryConstraintChecker(valueConstraints, context);
+            case PropertyType.DATE:
+                return new DateTimeConstraintChecker(valueConstraints, context);
+            case PropertyType.DOUBLE:
+                return new DoubleConstraintChecker(valueConstraints, context);
+            case PropertyType.LONG:
+                return new LongConstraintChecker(valueConstraints, context);
+            case PropertyType.NAME:
+                return new NameConstraintChecker(valueConstraints, context);
+            case PropertyType.PATH:
+                return new PathConstraintChecker(valueConstraints, context);
+            case PropertyType.REFERENCE:
+                return new ReferenceConstraintChecker(valueConstraints, session);
+            case PropertyType.STRING:
+                return new StringConstraintChecker(valueConstraints, context);
+            default:
+                throw new IllegalStateException("Invalid property type: " + type);
+        }
+    }
+
+    /**
+     * 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}.
+     */
+    public interface ConstraintChecker {
+
+        /**
+         * Returns the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that defines the semantics used for interpretation
+         * for the constraint values.
+         * 
+         * @return the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that defines the semantics used for interpretation
+         *         for the constraint values
+         */
+        public abstract int getType();
+
+        /**
+         * Returns <code>true</code> if and only if <code>value</code> satisfies the constraints used to create this constraint
+         * checker.
+         * 
+         * @param value the value to test
+         * @return whether or not the value satisfies the constraints used to create this constraint checker
+         * @see PropertyDefinition#getValueConstraints()
+         * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+         */
+        public abstract boolean matches( Value value );
+    }
+
+    private interface Range<T> {
+        boolean accepts( T value );
+    }
+
+    /**
+     * Encapsulation of common parsing logic used for all ranged constraints. Binary, long, double, and date values all have their
+     * constraints interpreted as a set of ranges that may include or exclude each end-point in the range.
+     * 
+     * @param <T> the specific type of the constraint (e.g., Binary, Long, Double, or DateTime).
+     */
+    private static abstract class RangeConstraintChecker<T extends Comparable<T>> implements ConstraintChecker {
+        private final Range<T>[] constraints;
+        private final ValueFactory<T> valueFactory;
+
+        @SuppressWarnings( "unchecked" )
+        protected RangeConstraintChecker( String[] valueConstraints,
+                                          ExecutionContext context ) {
+            constraints = new Range[valueConstraints.length];
+            this.valueFactory = getValueFactory(context.getValueFactories());
+
+            for (int i = 0; i < valueConstraints.length; i++) {
+                constraints[i] = parseValueConstraint(valueConstraints[i]);
+            }
+        }
+
+        protected abstract ValueFactory<T> getValueFactory( ValueFactories valueFactories );
+
+        protected abstract Comparable<T> parseValue( String s );
+
+        /**
+         * Parses one constraint value into a {@link Range} that will accept only values which match the range described by the
+         * value constraint.
+         * 
+         * @param valueConstraint the individual value constraint to be parsed into a {@link Range}.
+         * @return a range that accepts values which match the given value constraint.
+         */
+        private Range<T> parseValueConstraint( String valueConstraint ) {
+            assert valueConstraint != null;
+
+            final boolean includeLower = valueConstraint.charAt(0) == '[';
+            final boolean includeUpper = valueConstraint.charAt(valueConstraint.length() - 1) == ']';
+
+            int commaInd = valueConstraint.indexOf(',');
+            String lval = commaInd > 1 ? valueConstraint.substring(1, commaInd) : null;
+            String rval = commaInd < valueConstraint.length() - 2 ? valueConstraint.substring(commaInd + 1,
+                                                                                              valueConstraint.length() - 1) : null;
+
+            final Comparable<T> lower = lval == null ? null : parseValue(lval.trim());
+            final Comparable<T> upper = rval == null ? null : parseValue(rval.trim());
+
+            return new Range<T>() {
+                public boolean accepts( T value ) {
+                    if (lower != null && (includeLower ? lower.compareTo(value) > 0 : lower.compareTo(value) >= 0)) {
+                        return false;
+                    }
+                    if (upper != null && (includeUpper ? upper.compareTo(value) < 0 : upper.compareTo(value) <= 0)) {
+                        return false;
+                    }
+                    return true;
+                }
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see ConstraintChecker#matches(javax.jcr.Value)
+         */
+        public boolean matches( Value value ) {
+            assert value != null;
+
+            T convertedValue = valueFactory.create(((JcrValue)value).value());
+
+            for (int i = 0; i < constraints.length; i++) {
+                if (constraints[i].accepts(convertedValue)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    @Immutable
+    private static class BinaryConstraintChecker extends LongConstraintChecker {
+        private final ValueFactories valueFactories;
+
+        protected BinaryConstraintChecker( String[] valueConstraints,
+                                           ExecutionContext context ) {
+            super(valueConstraints, context);
+
+            this.valueFactories = context.getValueFactories();
+        }
+
+        @Override
+        public int getType() {
+            return PropertyType.BINARY;
+        }
+
+        @Override
+        public boolean matches( Value value ) {
+            Binary binary = valueFactories.getBinaryFactory().create(((JcrValue)value).value());
+            return super.matches(new JcrValue(valueFactories, PropertyType.LONG, binary.getSize()));
+        }
+    }
+
+    @Immutable
+    private static class LongConstraintChecker extends RangeConstraintChecker<Long> {
+
+        protected LongConstraintChecker( String[] valueConstraints,
+                                         ExecutionContext context ) {
+            super(valueConstraints, context);
+        }
+
+        public int getType() {
+            return PropertyType.LONG;
+        }
+
+        @Override
+        protected ValueFactory<Long> getValueFactory( ValueFactories valueFactories ) {
+            return valueFactories.getLongFactory();
+        }
+
+        @Override
+        protected Comparable<Long> parseValue( String s ) {
+            return Long.parseLong(s);
+        }
+    }
+
+    @Immutable
+    private static class DateTimeConstraintChecker extends RangeConstraintChecker<DateTime> {
+
+        protected DateTimeConstraintChecker( String[] valueConstraints,
+                                             ExecutionContext context ) {
+            super(valueConstraints, context);
+        }
+
+        public int getType() {
+            return PropertyType.DATE;
+        }
+
+        @Override
+        protected ValueFactory<DateTime> getValueFactory( ValueFactories valueFactories ) {
+            return valueFactories.getDateFactory();
+        }
+
+        @Override
+        protected Comparable<DateTime> parseValue( String s ) {
+            return new JodaDateTime(s.trim());
+        }
+    }
+
+    @Immutable
+    private static class DoubleConstraintChecker extends RangeConstraintChecker<Double> {
+
+        protected DoubleConstraintChecker( String[] valueConstraints,
+                                           ExecutionContext context ) {
+            super(valueConstraints, context);
+        }
+
+        public int getType() {
+            return PropertyType.DOUBLE;
+        }
+
+        @Override
+        protected ValueFactory<Double> getValueFactory( ValueFactories valueFactories ) {
+            return valueFactories.getDoubleFactory();
+        }
+
+        @Override
+        protected Comparable<Double> parseValue( String s ) {
+            return Double.parseDouble(s);
+        }
+    }
+
+    @Immutable
+    private static class ReferenceConstraintChecker implements ConstraintChecker {
+        private final JcrSession session;
+
+        private final Name[] constraints;
+
+        protected ReferenceConstraintChecker( String[] valueConstraints,
+                                              JcrSession session ) {
+            this.session = session;
+
+            NameFactory factory = session.getExecutionContext().getValueFactories().getNameFactory();
+
+            constraints = new Name[valueConstraints.length];
+
+            for (int i = 0; i < valueConstraints.length; i++) {
+                constraints[i] = factory.create(valueConstraints[i]);
+            }
+        }
+
+        public int getType() {
+            return PropertyType.REFERENCE;
+        }
+
+        public boolean matches( Value value ) {
+            assert value != null;
+
+            Node node = null;
+            try {
+                node = session.getNodeByUUID(((JcrValue)value).value().toString());
+            } catch (RepositoryException re) {
+                return false;
+            }
+
+            for (int i = 0; i < constraints.length; i++) {
+                try {
+                    if (node.isNodeType(constraints[i].getString(session.getExecutionContext().getNamespaceRegistry()))) {
+                        return true;
+                    }
+                } catch (RepositoryException re) {
+                    throw new IllegalStateException(re);
+                }
+            }
+
+            return false;
+        }
+    }
+
+    @Immutable
+    private static class NameConstraintChecker implements ConstraintChecker {
+        private final Name[] constraints;
+        private final ValueFactory<Name> valueFactory;
+
+        protected NameConstraintChecker( String[] valueConstraints,
+                                         ExecutionContext context ) {
+            this.valueFactory = context.getValueFactories().getNameFactory();
+
+            constraints = new Name[valueConstraints.length];
+
+            for (int i = 0; i < valueConstraints.length; i++) {
+                constraints[i] = valueFactory.create(valueConstraints[i]);
+            }
+        }
+
+        public int getType() {
+            return PropertyType.NAME;
+        }
+
+        public boolean matches( Value value ) {
+            assert value != null;
+
+            Name name = valueFactory.create(((JcrValue)value).value());
+
+            for (int i = 0; i < constraints.length; i++) {
+                if (constraints[i].equals(name)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    @Immutable
+    private static class StringConstraintChecker implements ConstraintChecker {
+        private final Pattern[] constraints;
+        private ValueFactory<String> valueFactory;
+
+        protected StringConstraintChecker( String[] valueConstraints,
+                                           ExecutionContext context ) {
+            constraints = new Pattern[valueConstraints.length];
+            this.valueFactory = context.getValueFactories().getStringFactory();
+
+            for (int i = 0; i < valueConstraints.length; i++) {
+                constraints[i] = Pattern.compile(valueConstraints[i]);
+            }
+        }
+
+        public int getType() {
+            return PropertyType.STRING;
+        }
+
+        public boolean matches( Value value ) {
+            assert value != null;
+
+            String convertedValue = valueFactory.create(((JcrValue)value).value());
+
+            for (int i = 0; i < constraints.length; i++) {
+                if (constraints[i].matcher(convertedValue).matches()) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    @Immutable
+    private static class PathConstraintChecker implements ConstraintChecker {
+        private final ExecutionContext context;
+        private final String[] constraints;
+
+        protected PathConstraintChecker( String[] valueConstraints,
+                                         ExecutionContext context ) {
+            this.constraints = valueConstraints;
+            this.context = context;
+        }
+
+        public int getType() {
+            return PropertyType.PATH;
+        }
+
+        public boolean matches( Value valueToMatch ) {
+            assert valueToMatch != null;
+
+            PathFactory pathFactory = context.getValueFactories().getPathFactory();
+            Path value = pathFactory.create(((JcrValue)valueToMatch).value());
+            value = value.getNormalizedPath();
+
+            for (int i = 0; i < constraints.length; i++) {
+                boolean matchesDescendants = constraints[i].endsWith("*");
+                Path constraintPath = pathFactory.create(matchesDescendants ? constraints[i].substring(0,
+                                                                                                       constraints[i].length() - 2) : constraints[i]);
+
+                if (matchesDescendants && value.isDecendantOf(constraintPath)) {
+                    return true;
+                }
+
+                if (!matchesDescendants && value.equals(constraintPath)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
 }

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-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -71,6 +71,16 @@
     }
 
     /**
+     * Returns a direct reference to the internal value object wrapped by this {@link JcrValue}. Useful to avoid the expense of
+     * {@link #asType(int)} if the caller already knows the type of the value.
+     * 
+     * @return a reference to the {@link #value} field.
+     */
+    final Object value() {
+        return value;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Value#getBoolean()
@@ -240,8 +250,8 @@
 
                         Segment[] segments = path.getSegmentsArray();
                         if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
-                            return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory()
-                                                                                              .create(valueAsString));
+                            return new JcrValue(this.valueFactories, type,
+                                                this.valueFactories.getNameFactory().create(valueAsString));
 
                         }
                     }

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -0,0 +1,629 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+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;
+
+/**
+ * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)},
+ * which provides the wrapper around the factory that the rest of the API is expected to utilize.
+ *
+ */
+public class JcrPropertyDefinitionTest {
+
+    private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
+    private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] { "[,+1945-08-01T01:30:00.000Z]", "[+1975-08-01T01:30:00.000Z,)"};
+    private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] { "[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]" };
+    private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
+    private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] { "jcr:system", "dnatest:constrainedType" };
+    //private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c" };
+    private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c" };
+    private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] { "dna:root" };
+    private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] { "foo", "bar*", ".*baz" };
+
+    private String workspaceName;
+    private ExecutionContext context;
+    private InMemoryRepositorySource source;
+    private JcrWorkspace workspace;
+    private JcrSession session;
+    private Graph graph;
+    private RepositoryConnectionFactory connectionFactory;
+    private NodeTypeManager nodeTypeManager;
+    private Map<String, Object> sessionAttributes;
+    @Mock
+    private JcrRepository repository;
+
+    @Before
+    public void beforeEach() throws Exception {
+        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.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 ...
+        MockitoAnnotations.initMocks(this);
+        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();
+
+        nodeTypeManager = new JcrNodeTypeManager(this.session,
+                               new TestNodeTypeSource(this.session,
+                               new DnaBuiltinNodeTypeSource(this.session,
+                                                            new JcrBuiltinNodeTypeSource(this.session))));
+        stub(workspace.getNodeTypeManager()).toReturn(nodeTypeManager);
+        
+    }
+
+    @After
+    public void after() throws Exception {
+        if (session.isLive()) {
+            session.logout();
+        }
+    }
+
+    private JcrPropertyDefinition propertyDefinitionFor(NodeType nodeType, Name propertyName) {
+        PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+        String property = propertyName.getString(context.getNamespaceRegistry());
+        
+        for (int i = 0; i < propertyDefs.length; i++) {
+            if (propertyDefs[i].getName().equals(property)) {
+                return (JcrPropertyDefinition) propertyDefs[i];
+            }
+        }
+        throw new IllegalStateException("Could not find property definition name " + property + " for type " 
+                                        + nodeType.getName() + ".  Test setup is invalid.");
+    }
+
+    private void checkConstraints(NodeType nodeType, Name propertyName, String[] expectedConstraints) {
+        PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+        String property = propertyName.getString(context.getNamespaceRegistry());
+        String[] constraints = null;
+        
+        for (int i = 0; i < propertyDefs.length; i++) {
+            if (propertyDefs[i].getName().equals(property)) {
+                constraints = propertyDefs[i].getValueConstraints();
+                break;
+            }
+        }
+
+        if (!Arrays.equals(constraints, expectedConstraints)) {
+            throw new IllegalStateException("Unexpected constraints for property: " + property);
+        }
+    }
+    
+    private NodeType validateTypeDefinition() throws Exception {
+        NamespaceRegistry nsr = context.getNamespaceRegistry();
+        
+        NodeType constrainedType = nodeTypeManager.getNodeType(TestLexicon.CONSTRAINED_TYPE.getString(nsr));
+        assertThat(constrainedType, notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE), notNullValue());
+        assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING), notNullValue());
+        
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_BINARY, EXPECTED_BINARY_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DATE, EXPECTED_DATE_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DOUBLE, EXPECTED_DOUBLE_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_LONG, EXPECTED_LONG_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_NAME, EXPECTED_NAME_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_PATH, EXPECTED_PATH_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_REFERENCE, EXPECTED_REFERENCE_CONSTRAINTS);
+        checkConstraints(constrainedType, TestLexicon.CONSTRAINED_STRING, EXPECTED_STRING_CONSTRAINTS);
+        
+        return constrainedType;
+    }
+    
+    private Value valueFor(Object value, int jcrType) {
+        return new JcrValue(context.getValueFactories(), jcrType, value);
+    }
+    
+    private String stringOfLength(int length) {
+        StringBuffer buff = new StringBuffer(length);
+        for (int i = 0; i < length; i++) {
+            buff.append(i % 10);
+        }
+        
+        return buff.toString();
+    }
+
+    private boolean satisfiesConstraints(JcrPropertyDefinition property, Value[] values) {
+        for (int i = 0; i < values.length; i++) {
+            if (!property.satisfiesConstraints(values[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+     @Test( expected = AssertionError.class )
+    public void shouldNotAllowNullValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+        assertThat(prop.satisfiesConstraints((Value)null), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidBinaryValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+        // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(0), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(4), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(10), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(19), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(31), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(40), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(50), PropertyType.BINARY)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(99), PropertyType.BINARY)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidBinaryValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+        // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+        Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+            valueFor(stringOfLength(10), PropertyType.BINARY), valueFor(stringOfLength(19), PropertyType.BINARY),};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(0), PropertyType.BINARY)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidBinaryValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+        // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(5), PropertyType.BINARY)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(9), PropertyType.BINARY)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(20), PropertyType.BINARY)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(30), PropertyType.BINARY)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(41), PropertyType.BINARY)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(49), PropertyType.BINARY)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidBinaryValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+        // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+        Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+            valueFor(stringOfLength(10), PropertyType.BINARY), valueFor(stringOfLength(20), PropertyType.BINARY),};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(9), PropertyType.BINARY)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidDateValue() throws Exception {
+        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));
+    }
+
+    @Test
+    public void shouldAllowValidDateValues() throws Exception {
+        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),};
+        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, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidDateValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+        assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.001Z", PropertyType.DATE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:29:59.999Z", PropertyType.DATE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.000-05:00", PropertyType.DATE)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidDateValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+        Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE),
+            valueFor("+1945-08-01T01:30:00.000-05:00", PropertyType.DATE),};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("+1945-08-01T01:30:00.001Z", PropertyType.DATE)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidDoubleValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+        assertThat(prop.satisfiesConstraints(valueFor(Double.MIN_VALUE, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(20.19, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(30.31, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(40.4, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(50.5, PropertyType.DOUBLE)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(Double.MAX_VALUE, PropertyType.DOUBLE)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidDoubleValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+        Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19, PropertyType.DOUBLE),
+            valueFor(50.5, PropertyType.DOUBLE)};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99, PropertyType.DOUBLE)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidDoubleValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+        assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.DOUBLE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(9.99999999, PropertyType.DOUBLE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(20.2, PropertyType.DOUBLE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(30.3, PropertyType.DOUBLE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(40.41, PropertyType.DOUBLE)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(49.9, PropertyType.DOUBLE)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidDoubleValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+        Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19, PropertyType.DOUBLE),
+            valueFor(50.49, PropertyType.DOUBLE)};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(20.2, PropertyType.DOUBLE)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidLongValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+        assertThat(prop.satisfiesConstraints(valueFor(Long.MIN_VALUE, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(10, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(19, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(31, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(40, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(50, PropertyType.LONG)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor(Long.MAX_VALUE, PropertyType.LONG)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidLongValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+        Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10, PropertyType.LONG),
+            valueFor(50, PropertyType.LONG)};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99, PropertyType.LONG)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidLongValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+        assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.LONG)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(9, PropertyType.LONG)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(20, PropertyType.LONG)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(30, PropertyType.LONG)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(41, PropertyType.LONG)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor(49, PropertyType.LONG)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidLongValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+        Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10, PropertyType.LONG),
+            valueFor(49, PropertyType.LONG)};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor(30, PropertyType.LONG)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidNameValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+        assertThat(prop.satisfiesConstraints(valueFor("jcr:system", PropertyType.NAME)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType", PropertyType.NAME)), is(true));
+
+        // Test that names work across namespace remaps
+        session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+        assertThat(prop.satisfiesConstraints(valueFor("newprefix:constrainedType", PropertyType.NAME)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidNameValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+        Value[] values = new Value[] {valueFor("jcr:system", PropertyType.NAME),
+            valueFor("dnatest:constrainedType", PropertyType.NAME),};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("jcr:system", PropertyType.NAME)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidNameValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+        assertThat(prop.satisfiesConstraints(valueFor("system", PropertyType.NAME)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("jcr:system2", PropertyType.NAME)), is(false));
+
+        // Test that old names fail after namespace remaps
+        session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+        assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType", PropertyType.NAME)), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidNameValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+        Value[] values = new Value[] {valueFor("jcr:system", PropertyType.NAME),
+            valueFor("dnatest:constrainedType2", PropertyType.NAME),};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("jcr:system2", PropertyType.NAME)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidStringValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+        assertThat(prop.satisfiesConstraints(valueFor("foo", PropertyType.STRING)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("bar", PropertyType.STRING)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("barr", PropertyType.STRING)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("barrrrrrrrr", PropertyType.STRING)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("baz", PropertyType.STRING)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("shabaz", PropertyType.STRING)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidStringValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+        Value[] values = new Value[] {valueFor("foo", PropertyType.STRING), valueFor("barr", PropertyType.STRING),};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("baz", PropertyType.STRING)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidStringValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+        assertThat(prop.satisfiesConstraints(valueFor("", PropertyType.STRING)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("foot", PropertyType.STRING)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("abar", PropertyType.STRING)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("bard", PropertyType.STRING)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("baz!", PropertyType.STRING)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("bazzat", PropertyType.STRING)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidStringValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+        Value[] values = new Value[] {valueFor("foo", PropertyType.STRING), valueFor("bard", PropertyType.STRING),};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("bazzat", PropertyType.STRING)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidPathValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+        assertThat(prop.satisfiesConstraints(valueFor("b", PropertyType.PATH)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("/a/b/c", PropertyType.PATH)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:namespace", PropertyType.PATH)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/", PropertyType.PATH)), is(true));
+        assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:foo", PropertyType.PATH)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidPathValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+        Value[] values = new Value[] {valueFor("b", PropertyType.PATH), valueFor("/a/b/c", PropertyType.PATH),};
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a/b/c", PropertyType.PATH)}), is(true));
+        assertThat(satisfiesConstraints(prop, values), is(true));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidPathValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+        assertThat(prop.satisfiesConstraints(valueFor("a", PropertyType.PATH)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("/a/b", PropertyType.PATH)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("/jcr:system", PropertyType.PATH)), is(false));
+        assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/d", PropertyType.PATH)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidPathValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+        Value[] values = new Value[] {valueFor("b", PropertyType.PATH), valueFor("/a/b/c/d", PropertyType.PATH),};
+        assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a", PropertyType.PATH)}), is(false));
+        assertThat(satisfiesConstraints(prop, values), is(false));
+    }
+
+    @Test
+    public void shouldAllowValidReferenceValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+        
+        UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+        assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(true));
+    }
+
+    @Test
+    public void shouldAllowValidReferenceValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+        
+        UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+        assertThat(satisfiesConstraints(prop, new Value[] { }), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(true));
+    }
+    
+
+    @Test
+    public void shouldNotAllowInvalidReferenceValue() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+
+        Node aNode = session.getRootNode().getNode("a");
+        UUID uuid = ((JcrNode) aNode).nodeUuid;
+
+        
+        assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(false));
+    }
+
+    @Test
+    public void shouldNotAllowInvalidReferenceValues() throws Exception {
+        NodeType constrainedType = validateTypeDefinition();
+        JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+        
+        Node aNode = session.getRootNode().getNode("a");
+        UUID uuid = ((JcrNode) aNode).nodeUuid;
+        
+        assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(false));
+    }
+
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -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.
+ *
+ * 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.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * Namespace and names for DNA testing.
+ *
+ */
+public class TestLexicon {
+
+    public static class Namespace {
+        public static final String URI = "http://www.jboss.org/dna/test/1.0";
+        public static final String PREFIX = "dnatest";
+    }
+
+    public static final Name CONSTRAINED_TYPE = new BasicName(Namespace.URI, "constrainedType");
+    public static final Name CONSTRAINED_BINARY = new BasicName(Namespace.URI, "constrainedBinary");
+    public static final Name CONSTRAINED_DATE = new BasicName(Namespace.URI, "constrainedDate");
+    public static final Name CONSTRAINED_DOUBLE = new BasicName(Namespace.URI, "constrainedDouble");
+    public static final Name CONSTRAINED_LONG = new BasicName(Namespace.URI, "constrainedLong");
+    public static final Name CONSTRAINED_NAME = new BasicName(Namespace.URI, "constrainedName");
+    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");
+    
+}


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

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-18 17:05:16 UTC (rev 783)
@@ -0,0 +1,145 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.nodetype.NodeType;
+
+/**
+ * Node type source with additional node types that can be used for testing. This class defines its own namespace for its types.
+ */
+public class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+    /** The list of primary node types. */
+    private final List<JcrNodeType> primaryNodeTypes;
+    /** The list of mixin node types. */
+    private final List<JcrNodeType> mixinNodeTypes;
+
+    TestNodeTypeSource( JcrSession session,
+                        JcrNodeTypeSource predecessor ) {
+        super(predecessor);
+
+        primaryNodeTypes = new ArrayList<JcrNodeType>();
+        mixinNodeTypes = new ArrayList<JcrNodeType>();
+
+        JcrNodeType base = findType(JcrNtLexicon.BASE);
+
+        if (base == null) {
+            String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+            throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+        }
+
+        // Stubbing in child node and property definitions for now
+        JcrNodeType constrainedType = new JcrNodeType(
+                                                      session,
+                                                      TestLexicon.CONSTRAINED_TYPE,
+                                                      Arrays.asList(new NodeType[] {base}),
+                                                      NO_PRIMARY_ITEM_NAME,
+                                                      NO_CHILD_NODES,
+                                                      Arrays.asList(new JcrPropertyDefinition[] {
+                                                          new JcrPropertyDefinition(session, null,
+                                                                                    TestLexicon.CONSTRAINED_BINARY,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.BINARY, new String[] {"[,5)",
+                                                                                        "[10, 20)", "(30,40]", "[50,]"}, false),
+                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_DATE,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.DATE, new String[] {
+                                                                                        "[,+1945-08-01T01:30:00.000Z]",
+                                                                                        "[+1975-08-01T01:30:00.000Z,)"}, false),
+                                                          new JcrPropertyDefinition(session, null,
+                                                                                    TestLexicon.CONSTRAINED_DOUBLE,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.DOUBLE,
+                                                                                    new String[] {"[,5.0)", "[10.1, 20.2)",
+                                                                                        "(30.3,40.4]", "[50.5,]"}, false),
+
+                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_LONG,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.LONG, new String[] {"[,5)",
+                                                                                        "[10, 20)", "(30,40]", "[50,]"}, false),
+
+                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_NAME,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.NAME, new String[] {
+                                                                                        "jcr:system", "dnatest:constrainedType"},
+                                                                                    false),
+
+                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_PATH,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.PATH, new String[] {
+                                                              // "/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"}, false),
+                                                              "/jcr:system/*", "b", "/a/b/c"}, false),
+                                                          new JcrPropertyDefinition(session, null,
+                                                                                    TestLexicon.CONSTRAINED_REFERENCE,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.REFERENCE,
+                                                                                    new String[] {"dna:root",}, false),
+
+                                                          new JcrPropertyDefinition(session, null,
+                                                                                    TestLexicon.CONSTRAINED_STRING,
+                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                    false, false, false, NO_DEFAULT_VALUES,
+                                                                                    PropertyType.STRING, new String[] {"foo",
+                                                                                        "bar*", ".*baz",}, false),
+
+                                                      }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
+        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+     */
+    @Override
+    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+        return primaryNodeTypes;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+     */
+    @Override
+    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+        return mixinNodeTypes;
+    }
+
+}


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

Modified: trunk/dna-jcr/src/test/resources/vehicles.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/vehicles.xml	2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/test/resources/vehicles.xml	2009-03-18 17:05:16 UTC (rev 783)
@@ -27,7 +27,7 @@
 <Vehicles xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns="http://example.com/vehicles">
     <Cars>
         <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 Prius" jcr:mixinTypes="mix:referenceable" 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"/>
         </Hybrid>



From dna-commits at lists.jboss.org  Fri Mar 20 14:22:45 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 20 Mar 2009 14:22:45 -0400
Subject: [dna-commits] DNA SVN: r784 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 4 other directories.
Message-ID: <E1LkjMj-0006tF-Lj@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-20 14:22:45 -0400 (Fri, 20 Mar 2009)
New Revision: 784

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   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/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.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
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.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/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
Log:
DNA-194 Implement update JCR capability

Added most of the infrastructure to allow updates to the SessionCache.  The cached representations of the nodes are changed and corresponding requests are accumulated in a Graph.Batch that are sent to the repository store when SessionCache.save() is called.  This implementation hooks some of the infrastructure up to the JcrAbstractNode implementation, although more tests need to be written to verify the behavior.  

Currently, only deleting nodes has been tested and is thought to be complete.  Changing properties, creating children, and moving nodes are partially implemented.  (Some changes to the node type manager are required, but there is currently an ongoing refactoring of JcrNodeTypeManager.  This is the primary reason for committing this partially-complete work.)


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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -54,6 +54,7 @@
 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.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.PropertyFactory;
 import org.jboss.dna.graph.property.Reference;
@@ -66,6 +67,7 @@
 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;
@@ -77,6 +79,7 @@
 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.UnsupportedRequestException;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
@@ -217,6 +220,12 @@
      * </p>
      * 
      * @param request the request to be executed (may be a {@link CompositeRequest}.
+     * @throws PathNotFoundException if the request used a node that did not exist
+     * @throws InvalidRequestException if the request was not valid
+     * @throws InvalidWorkspaceException if the workspace used in the request was not valid
+     * @throws UnsupportedRequestException if the request was not supported by the source
+     * @throws RepositorySourceException if an error occurs during execution
+     * @throws RuntimeException if a runtime error occurs during execution
      */
     protected void execute( Request request ) {
         RepositoryConnection connection = Graph.this.getConnectionFactory().createConnection(getSourceName());
@@ -1948,6 +1957,24 @@
         }
 
         /**
+         * Return whether this batch has been {@link #execute() executed}.
+         * 
+         * @return true if this batch has already been executed, or false otherwise
+         */
+        public boolean hasExecuted() {
+            return executed;
+        }
+
+        /**
+         * Determine whether this batch needs to be executed (there are requests and the batch has not been executed yet).
+         * 
+         * @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;
+        }
+
+        /**
          * Obtain the graph that this batch uses.
          * 
          * @return the graph; never null
@@ -3318,6 +3345,11 @@
             };
         }
 
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.Graph.Executable#execute()
+         */
         public Results execute() {
             return this.requestQueue.execute();
         }
@@ -3406,6 +3438,12 @@
          * Stop accumulating the requests, submit them to the repository source, and return the results.
          * 
          * @return the results containing the requested information from the repository.
+         * @throws PathNotFoundException if a request used a node that did not exist
+         * @throws InvalidRequestException if a request was not valid
+         * @throws InvalidWorkspaceException if the workspace used in a request was not valid
+         * @throws UnsupportedRequestException if a request was not supported by the source
+         * @throws RepositorySourceException if an error occurs during execution
+         * @throws RuntimeException if a runtime error occurs during execution
          */
         Results execute();
     }
@@ -4539,7 +4577,15 @@
          * @param nodeName the name of the new node
          * @return the interface used to complete the request
          */
-        CreateAction<Next> nodeNamed( String nodeName );
+        Create<Next> nodeNamed( String nodeName );
+
+        /**
+         * Specify the name of the node that is to be created.
+         * 
+         * @param nodeName the name of the new node
+         * @return the interface used to complete the request
+         */
+        Create<Next> nodeNamed( Name nodeName );
     }
 
     /**
@@ -4653,6 +4699,7 @@
         void submit( Request request );
 
         void submit( List<Request> requests );
+
     }
 
     /**
@@ -4700,6 +4747,15 @@
             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 ...
@@ -5593,6 +5649,10 @@
             Name nameObj = factory.create(name);
             return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, nameObj);
         }
+
+        public CreateAction<T> nodeNamed( Name name ) {
+            return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, name);
+        }
     }
 
     @Immutable

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -166,33 +166,4 @@
         return path().size();
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Item#refresh(boolean)
-     */
-    public void refresh( boolean keepChanges ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Item#remove()
-     */
-    public void remove() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Item#save()
-     */
-    public void save() {
-        throw new UnsupportedOperationException();
-    }
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -990,4 +990,24 @@
         }
         return patterns;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Item#refresh(boolean)
+     */
+    public void refresh( boolean keepChanges ) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Item#save()
+     */
+    public void save() {
+        throw new UnsupportedOperationException();
+    }
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -264,4 +264,34 @@
     public final void setValue( Node value ) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Item#refresh(boolean)
+     */
+    public void refresh( boolean keepChanges ) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Item#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws UnsupportedOperationException always
+     * @see javax.jcr.Item#save()
+     */
+    public void save() {
+        throw new UnsupportedOperationException();
+    }
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -76,6 +76,10 @@
     public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
     public static I18n missingNodeTypeForExistingNode;
 
+    public static I18n unableToRemoveRootNode;
+    public static I18n unableToMoveNodeToBeChildOfDecendent;
+    public static I18n nodeHasAlreadyBeenRemovedFromThisSession;
+
     public static I18n typeNotFound;
     public static I18n supertypeNotFound;
 

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -24,6 +24,7 @@
 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;
@@ -85,4 +86,23 @@
     public String getPath() throws RepositoryException {
         return cache.getPathFor(nodeUuid).getString(namespaces());
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @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);
+        }
+    }
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -28,6 +28,7 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import net.jcip.annotations.NotThreadSafe;
 
 /**
@@ -115,4 +116,15 @@
         }
         throw new ItemNotFoundException(JcrI18n.tooDeep.text(depth));
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Item#remove()
+     */
+    public void remove() throws ConstraintViolationException {
+        String msg = JcrI18n.unableToRemoveRootNode.text(cache.workspaceName());
+        throw new ConstraintViolationException(msg);
+    }
+
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -633,10 +633,9 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#save()
      */
-    public void save() {
-        throw new UnsupportedOperationException();
+    public void save() throws RepositoryException {
+        cache.save();
     }
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -27,12 +27,15 @@
 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;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 import java.util.UUID;
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
@@ -40,6 +43,7 @@
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
@@ -60,6 +64,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.jcr.cache.ChangedNodeInfo;
 import org.jboss.dna.jcr.cache.ChildNode;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.EmptyChildren;
@@ -119,22 +124,26 @@
     private final String workspaceName;
     protected final ExecutionContext context;
     protected final PathFactory pathFactory;
-    private final NameFactory nameFactory;
-    private final ValueFactory<String> stringFactory;
-    private final NamespaceRegistry namespaces;
-    private final PropertyFactory propertyFactory;
+    protected final NameFactory nameFactory;
+    protected final ValueFactory<String> stringFactory;
+    protected final NamespaceRegistry namespaces;
+    protected final PropertyFactory propertyFactory;
     private final Graph store;
     private final Name defaultPrimaryTypeName;
     private final Property defaultPrimaryTypeProperty;
-    private final Path rootPath;
+    protected final Path rootPath;
+    protected final Name residualName;
 
     private UUID root;
     private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
     private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
 
-    private final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
-    private final HashMap<UUID, NodeInfo> changedNodes;
+    protected final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
+    protected final HashMap<UUID, ChangedNodeInfo> changedNodes;
+    protected final HashMap<UUID, NodeInfo> deletedNodes;
 
+    private Graph.Batch operations;
+
     public SessionCache( JcrSession session,
                          String workspaceName,
                          ExecutionContext context,
@@ -156,12 +165,17 @@
         this.defaultPrimaryTypeName = JcrNtLexicon.UNSTRUCTURED;
         this.defaultPrimaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, this.defaultPrimaryTypeName);
         this.rootPath = pathFactory.createRootPath();
+        this.residualName = nameFactory.create(JcrNodeType.RESIDUAL_ITEM_NAME);
 
         this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
         this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
 
         this.cachedNodes = new HashMap<UUID, ImmutableNodeInfo>();
-        this.changedNodes = new HashMap<UUID, NodeInfo>();
+        this.changedNodes = new HashMap<UUID, ChangedNodeInfo>();
+        this.deletedNodes = new HashMap<UUID, NodeInfo>();
+
+        // Create the batch operations ...
+        this.operations = this.store.batch();
     }
 
     JcrSession session() {
@@ -180,6 +194,39 @@
         return session.nodeTypeManager();
     }
 
+    final Graph.Batch operations() {
+        return operations;
+    }
+
+    /**
+     * 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()) {
+            // Execute the batched operations ...
+            try {
+                operations.execute();
+            } catch (RuntimeException e) {
+                throw new RepositoryException(e);
+            }
+
+            // Create a new batch for future operations ...
+            operations = store.batch();
+            // Remove all the cached items that have been changed or deleted ...
+            for (UUID changedUuid : changedNodes.keySet()) {
+                cachedNodes.remove(changedUuid);
+            }
+            for (UUID changedUuid : deletedNodes.keySet()) {
+                cachedNodes.remove(changedUuid);
+            }
+            // Remove all the changed and deleted infos ...
+            changedNodes.clear();
+            deletedNodes.clear();
+        }
+    }
+
     public JcrRootNode findJcrRootNode() throws RepositoryException {
         return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
     }
@@ -215,11 +262,13 @@
      * @return the information for the referenced node; never null
      * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
      * @throws PathNotFoundException if the node given by the relative path does not exist
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      * @throws RepositoryException if any other error occurs while reading information from the repository
      * @see #findNodeInfoForRoot()
      */
     public AbstractJcrNode findJcrNode( UUID uuidOfReferenceNode,
-                                        Path relativePath ) throws PathNotFoundException, RepositoryException {
+                                        Path relativePath )
+        throws PathNotFoundException, InvalidItemStateException, RepositoryException {
         // An existing JCR Node object was not found, so we'll have to create it by finding the underlying
         // NodeInfo for the node (from the changed state or the cache) ...
         NodeInfo info = findNodeInfo(uuidOfReferenceNode, relativePath);
@@ -268,11 +317,13 @@
      * @return the information for the referenced item; never null
      * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist, or if an item given by the
      *         supplied relative path does not exist
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      * @throws RepositoryException if any other error occurs while reading information from the repository
      * @see #findNodeInfoForRoot()
      */
     public AbstractJcrItem findJcrItem( UUID uuidOfReferenceNode,
-                                        Path relativePath ) throws ItemNotFoundException, RepositoryException {
+                                        Path relativePath )
+        throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         // A pathological case is an empty relative path ...
         if (relativePath.size() == 0) {
             return findJcrNode(uuidOfReferenceNode);
@@ -332,6 +383,412 @@
     }
 
     /**
+     * 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.
+     * 
+     * @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
+     * @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 ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+        // See if we already have something in the changed nodes ...
+        ChangedNodeInfo info = changedNodes.get(uuid);
+        Location currentLocation = null;
+        if (info == null) {
+            // Or in the cache ...
+            NodeInfo cached = cachedNodes.get(uuid);
+            if (cached == null) {
+                cached = loadFromGraph(uuid, null);
+            }
+            // Now put into the changed nodes ...
+            info = new ChangedNodeInfo(cached);
+            changedNodes.put(uuid, info);
+            currentLocation = info.getOriginalLocation();
+        } else {
+            // compute the current location ...
+            currentLocation = Location.create(getPathFor(info), uuid);
+        }
+        return new NodeEditor(info, currentLocation);
+    }
+
+    /**
+     * An interface used to manipulate a node's properties and children.
+     */
+    public final class NodeEditor {
+        private final ChangedNodeInfo node;
+        private final Location currentLocation;
+
+        protected NodeEditor( ChangedNodeInfo node,
+                              Location currentLocation ) {
+            this.node = node;
+            this.currentLocation = currentLocation;
+        }
+
+        /**
+         * 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.
+         * 
+         * @param name the property name; may not be null
+         * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
+         * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
+         *         definition constraint
+         */
+        public void setProperty( Name name,
+                                 Object value ) throws ConstraintViolationException {
+            Property dnaProp = propertyFactory.create(name, value);
+            setProperty(name, dnaProp);
+        }
+
+        /**
+         * 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 value the new property values, which may be converted to the appropriate {@link PropertyType type}
+         * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
+         *         definition constraint
+         */
+        public void setProperty( Name name,
+                                 Object[] value ) throws ConstraintViolationException {
+            Property dnaProp = propertyFactory.create(name, value);
+            setProperty(name, dnaProp);
+        }
+
+        protected final void setProperty( Name name,
+                                          Property dnaProp ) throws ConstraintViolationException {
+            PropertyInfo existing = node.getProperty(name);
+            PropertyInfo newProperty = null;
+            if (existing != null) {
+                // We're replacing an existing property, but we still need to check that the property definition
+                // defines a type. So, find the property definition for the existing property ...
+                JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId(),
+                                                                                     existing.isMultiValued());
+
+                // Look at the required property type ...
+                int propertyType = definition.getRequiredType();
+                if (propertyType == PropertyType.UNDEFINED) {
+                    // We need set the new type to that defined by the values ...
+                    propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+                }
+
+                // Csreate the property info ...
+                newProperty = new PropertyInfo(existing.getPropertyId(), existing.getDefinitionId(), propertyType, dnaProp,
+                                               existing.isMultiValued());
+            } else {
+                // It's a new property ...
+                PropertyId id = new PropertyId(node.getUuid(), name);
+                // Look find the property definition to use ...
+                JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProp);
+
+                // Figure out the property type ...
+                int propertyType = definition.getRequiredType();
+                if (propertyType == PropertyType.UNDEFINED) {
+                    propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+                }
+                // Create the property info ...
+                newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
+            }
+            node.setProperty(newProperty, context().getValueFactories());
+            operations().set(dnaProp).on(currentLocation);
+        }
+
+        /**
+         * Remove the existing property with the supplied name.
+         * 
+         * @param name the property name; may not be null
+         * @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) {
+                operations().remove(name).on(currentLocation);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * 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
+         * @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
+         * @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 RepositoryException if any other error occurs while reading information from the repository
+         */
+        public ChildNode moveToBeChild( UUID nodeUuid )
+            throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
+
+            if (nodeUuid.equals(node.getUuid()) || isAncestor(nodeUuid)) {
+                Path pathOfNode = getPathFor(nodeUuid);
+                Path thisPath = currentLocation.getPath();
+                String msg = JcrI18n.unableToMoveNodeToBeChildOfDecendent.text(pathOfNode, thisPath, workspaceName());
+                throw new RepositoryException(msg);
+            }
+
+            // Is the node already a child?
+            ChildNode child = node.getChildren().getChild(nodeUuid);
+            if (child != null) return child;
+
+            // 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;
+
+            // 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 = findBestChildNodeDefinition(childName, numSns);
+            if (!definition.getId().equals(node.getDefinitionId())) {
+                // The node definition changed, so try to set the property ...
+                try {
+                    setProperty(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
+                } 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.
+                    // When it is reloaded into a cache (after being persisted), the correct node definition
+                    // will be computed again ...
+                    node.setDefinitionId(definition.getId());
+
+                    // And remove the property from the info ...
+                    existingNodeEditor.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(child.getName(), child.getUuid(), pathFactory);
+
+            // Set the child's changed representation to point to this node as its parent ...
+            existingNodeInfo.setParent(node.getUuid());
+
+            // Now, record the operation to do this ...
+            operations().move(existingNodeEditor.currentLocation).into(currentLocation);
+
+            return newChild;
+        }
+
+        /**
+         * Create a new node as a child of this node, using the supplied name and (optionally) the supplied UUID.
+         * 
+         * @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 RepositoryException if any other error occurs while reading information from the repository
+         */
+        public ChildNode createChild( Name name,
+                                      UUID desiredUuid,
+                                      Name primaryTypeName,
+                                      NodeDefinitionId nodeDefinitionId )
+            throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
+            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);
+            JcrNodeDefinition definition = findBestChildNodeDefinition(name, numSns);
+
+            ChildNode result = node.addChild(name, desiredUuid, pathFactory);
+
+            // ---------------------------------------------------------
+            // Now create the child node representation in the cache ...
+            // ---------------------------------------------------------
+            Path newPath = pathFactory.create(currentLocation.getPath(), result.getSegment());
+            Location location = Location.create(newPath, desiredUuid);
+
+            // 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());
+
+            // Create the property info for the "jcr:primaryType" child property ...
+            JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(primaryTypeProp, primaryTypeName);
+            PropertyDefinitionId primaryTypeDefinitionId = primaryTypeDefn.getId();
+            PropertyInfo primaryTypeInfo = new PropertyInfo(new PropertyId(desiredUuid, primaryTypeProp.getName()),
+                                                            primaryTypeDefinitionId, PropertyType.NAME, primaryTypeProp, false);
+            properties.put(primaryTypeProp.getName(), primaryTypeInfo);
+
+            // Create the property info for the "dna:nodeDefinition" child property ...
+            JcrPropertyDefinition nodeDefnDefn = findBestPropertyDefintion(nodeDefinitionProp, primaryTypeName);
+            if (nodeDefnDefn != null) {
+                PropertyDefinitionId nodeDefnDefinitionId = nodeDefnDefn.getId();
+                PropertyInfo nodeDefinitionInfo = new PropertyInfo(new PropertyId(desiredUuid, nodeDefinitionProp.getName()),
+                                                                   nodeDefnDefinitionId, PropertyType.STRING, nodeDefinitionProp,
+                                                                   true);
+                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);
+
+            // ---------------------------------------
+            // Now record the changes to the store ...
+            // ---------------------------------------
+            Graph.Create<Graph.Batch> create = operations().createUnder(currentLocation)
+                                                           .nodeNamed(name)
+                                                           .with(desiredUuid)
+                                                           .with(primaryTypeProp);
+            if (nodeDefnDefn != null) {
+                create = create.with(nodeDefinitionProp);
+            }
+            create.and();
+            return result;
+        }
+
+        /**
+         * Destroy the child node with the supplied UUID and all nodes that exist below it, including any nodes that were created
+         * and haven't been persisted.
+         * 
+         * @param nodeUuid the UUID of the child node; may not be null
+         * @return true if the child was successfully removed, or false if the node did not exist as a child
+         */
+        public boolean destroyChild( UUID nodeUuid ) {
+            ChildNode deleted = node.removeChild(nodeUuid, pathFactory);
+
+            if (deleted != null) {
+                // Recursively mark the cached/changed information as deleted ...
+                deleteNodeInfos(nodeUuid);
+
+                // Now make the request to the source ...
+                Path childPath = pathFactory.create(currentLocation.getPath(), deleted.getSegment());
+                Location locationOfChild = Location.create(childPath, nodeUuid);
+                operations().delete(locationOfChild);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Find the best property definition in this node's
+         * 
+         * @param dnaProperty the new property that is to be set on this node
+         * @return the property definition that allows setting this property; never null
+         * @throws ConstraintViolationException if setting the property would violates this node's definition or the property's
+         *         definition
+         */
+        protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty ) throws ConstraintViolationException {
+            // First check the primary type ...
+            JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProperty, node.getPrimaryTypeName());
+            if (definition != null) {
+                // TODO: Does this definition allow this value? ...
+                return definition;
+            }
+            // Check the mixin types ...
+            for (Name mixinTypeName : node.getMixinTypeNames()) {
+                definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
+                if (definition != null) {
+                    // TODO: Does this definition allow this value? ...
+                    return definition;
+                }
+            }
+
+            // Nothing was found yet, so check the residual property definitions, starting with the primary type ...
+            definition = findBestPropertyDefintion(dnaProperty, residualName);
+            if (definition != null) {
+                // TODO: Does this definition allow this value? ...
+                return definition;
+            }
+            // Check the mixin types ...
+            for (Name mixinTypeName : node.getMixinTypeNames()) {
+                definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
+                if (definition != null) {
+                    // TODO: Does this definition allow this value? ...
+                    return definition;
+                }
+            }
+
+            // No definition that allowed the values ...
+            throw new ConstraintViolationException();
+        }
+
+        /**
+         * Find the best property definition in the named node type.
+         * 
+         * @param dnaProperty the new property that is to be set on this node
+         * @param nodeTypeName the name of the node type that should be checked; may not be null
+         * @return the property definition that allows setting this property; or null if no valid property definition could be
+         *         found in the given node type
+         * @see #findBestPropertyDefintion(Property)
+         */
+        protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty,
+                                                                   Name nodeTypeName ) {
+            JcrNodeType nodeType = nodeTypes().getNodeType(nodeTypeName);
+            Name name = dnaProperty.getName();
+            JcrPropertyDefinition definition = null;
+            if (dnaProperty.isSingle()) {
+                // First look for a single-valued property definition with a matching name ...
+                definition = nodeType.getPropertyDefinition(name, false);
+                if (definition != null) return definition;
+
+                // Then look for a residual definition ...
+                definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, false);
+                if (definition != null) return definition;
+            }
+
+            // Either the DNA property has 0 or 2+ values (and we couldn't use a single-valued definition)
+            // OR there was 1 value and we couldn't find a single-valued definition.
+            // So, we need to look for a multi-valued property definition ...
+
+            // First look for a definition that matches by name ...
+            definition = nodeType.getPropertyDefinition(name, true);
+            if (definition != null) return definition;
+
+            // Then look for a residual definition ...
+            definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
+            if (definition != null) return definition;
+
+            // Nothing found yet ...
+            if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+                // We can use the "nt:unstructured" property definitions for any property ...
+                JcrNodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
+                definition = unstructured.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
+                if (definition != null) return definition;
+            }
+
+            // No property definition could be found ...
+            return null;
+        }
+
+        /**
+         * @param childName
+         * @param numberOfExistingChildrenWithName
+         * @return the most specific node definition for the child; never null
+         * @throws ConstraintViolationException if the new child would violates this node's definition
+         */
+        protected JcrNodeDefinition findBestChildNodeDefinition( Name childName,
+                                                                 int numberOfExistingChildrenWithName )
+            throws ConstraintViolationException {
+
+            // No definition that allowed the values ...
+            throw new ConstraintViolationException();
+        }
+
+        protected boolean isAncestor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+            UUID ancestor = node.getParent();
+            while (ancestor != null) {
+                if (ancestor.equals(uuid)) return true;
+                NodeInfo info = findNodeInfo(ancestor);
+                ancestor = info.getParent();
+            }
+            return false;
+        }
+    }
+
+    /**
      * Utility method that creates and caches the appropriate kind of AbstractJcrNode implementation for node given by the
      * supplied information.
      * 
@@ -387,20 +844,18 @@
      * @param uuid the UUID for the node; may not be null
      * @return the information for the node with the supplied UUID, or null if the information is not in the cache
      * @throws ItemNotFoundException if there is no node with the supplied UUID
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      * @throws RepositoryException if any other error occurs while reading information from the repository
      * @see #findNodeInfoInCache(UUID)
      * @see #findNodeInfo(UUID, Path)
      * @see #findNodeInfoForRoot()
      */
-    NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, RepositoryException {
-        // See if we already have something in the changed nodes ...
-        NodeInfo info = changedNodes.get(uuid);
+    NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+        // See if we already have something in the cache ...
+        NodeInfo info = findNodeInfoInCache(uuid);
         if (info == null) {
-            // Or in the cache ...
-            info = cachedNodes.get(uuid);
-            if (info == null) {
-                info = loadFromGraph(uuid, null);
-            }
+            // Nope, so go ahead and load it ...
+            info = loadFromGraph(uuid, null);
         }
         return info;
     }
@@ -414,13 +869,20 @@
      * @see #findNodeInfo(UUID)
      * @see #findNodeInfo(UUID, Path)
      * @see #findNodeInfoForRoot()
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      */
-    NodeInfo findNodeInfoInCache( UUID uuid ) {
+    NodeInfo findNodeInfoInCache( UUID uuid ) throws InvalidItemStateException {
         // See if we already have something in the changed nodes ...
         NodeInfo info = changedNodes.get(uuid);
         if (info == null) {
             // Or in the cache ...
             info = cachedNodes.get(uuid);
+            if (info == null) {
+                // Finally check if the node was deleted ...
+                if (deletedNodes.containsKey(uuid)) {
+                    throw new InvalidItemStateException();
+                }
+            }
         }
         return info;
     }
@@ -452,11 +914,13 @@
      * @return the information for the referenced node; never null
      * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
      * @throws PathNotFoundException if the node given by the relative path does not exist
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      * @throws RepositoryException if any other error occurs while reading information from the repository
      * @see #findNodeInfoForRoot()
      */
     NodeInfo findNodeInfo( UUID node,
-                           Path relativePath ) throws ItemNotFoundException, PathNotFoundException, RepositoryException {
+                           Path relativePath )
+        throws ItemNotFoundException, InvalidItemStateException, PathNotFoundException, RepositoryException {
         // The relative path must be normalized ...
         assert relativePath.isNormalized();
 
@@ -583,19 +1047,21 @@
      * @param propertyId the identifier for the property; may not be null
      * @return the property information, or null if the node does not contain the specified property
      * @throws PathNotFoundException if the node containing this property does not exist
+     * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
      * @throws RepositoryException if there is an error while obtaining the information
      */
-    PropertyInfo findPropertyInfo( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+    PropertyInfo findPropertyInfo( PropertyId propertyId )
+        throws PathNotFoundException, InvalidItemStateException, RepositoryException {
         NodeInfo info = findNodeInfo(propertyId.getNodeId());
         return info.getProperty(propertyId.getPropertyName());
     }
 
-    Path getPathFor( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+    Path getPathFor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         if (uuid == root) return rootPath;
         return getPathFor(findNodeInfo(uuid));
     }
 
-    Path getPathFor( NodeInfo info ) throws ItemNotFoundException, RepositoryException {
+    Path getPathFor( NodeInfo info ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         if (info != null && info.getUuid() == root) return rootPath;
         LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
         while (info != null) {
@@ -619,7 +1085,7 @@
         return getPathFor(findPropertyInfo(propertyId));
     }
 
-    protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+    protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         findNodeInfoForRoot();
         if (nodeUuid == root) return nameFactory.create("");
         // Get the parent ...
@@ -629,7 +1095,7 @@
         return child.getName();
     }
 
-    protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+    protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
         findNodeInfoForRoot();
         if (nodeUuid == root) return 1;
         // Get the parent ...
@@ -718,7 +1184,9 @@
         if (uuid != null) {
             // Check for an identification property ...
             uuidProperty = location.getIdProperty(JcrLexicon.UUID);
-            if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+            if (uuidProperty == null) {
+                uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
+            }
         }
         if (uuidProperty == null) {
             uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
@@ -843,9 +1311,12 @@
         }
         // The process the mixin types ...
         org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
+        Set<Name> mixinTypeNames = null;
         if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
             for (Object mixinTypeValue : mixinTypesProperty) {
                 Name mixinTypeName = nameFactory.create(mixinTypeValue);
+                if (mixinTypeNames == null) mixinTypeNames = new HashSet<Name>();
+                mixinTypeNames.add(mixinTypeName);
                 if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
                 String mixinTypeNameString = mixinTypeName.getString(namespaces);
                 NodeType mixinType = nodeTypes().getNodeType(mixinTypeNameString);
@@ -958,7 +1429,7 @@
         List<Location> locations = graphNode.getChildren();
         Children children = locations.isEmpty() ? new EmptyChildren(parentUuid) : new ImmutableChildren(parentUuid, locations);
         props = Collections.unmodifiableMap(props);
-        return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
+        return new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definition.getId(), parentUuid, children, props);
     }
 
     /**
@@ -985,4 +1456,73 @@
         // TODO: should this also check the mixins?
         return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
     }
+
+    /**
+     * 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.
+     * <p>
+     * Note that this method only processes those nodes that are actually represented in this cache. Any branches that are not
+     * loaded are not processed. This is an acceptable assumption, since all ancestors of a cached node should also be cached.
+     * </p>
+     * <p>
+     * Also be aware that the returned count of deleted node info representations will only reflect the total number of nodes in
+     * the branch if and only if all branch nodes were cached. In all other cases, the count returned will be fewer than the
+     * number of actual nodes in the branch.
+     * </p>
+     * 
+     * @param uuid the UUID of the node that should be marked as deleted; may not be null
+     * @return the number of node info representations that were marked as deleted
+     */
+    protected int deleteNodeInfos( UUID uuid ) {
+        Queue<UUID> nodesToDelete = new LinkedList<UUID>();
+        int numDeleted = 0;
+        nodesToDelete.add(uuid);
+        while (!nodesToDelete.isEmpty()) {
+            UUID toDelete = nodesToDelete.remove();
+            // Remove the node info from the changed map ...
+            NodeInfo info = changedNodes.remove(toDelete);
+            if (info == null) {
+                // Wasn't changed, so remove it from the cache map ...
+                info = cachedNodes.remove(toDelete);
+            }
+            // Whether or not we found an info, add it to the deleted map ...
+            this.deletedNodes.put(toDelete, info);
+
+            if (info != null) {
+                // Get all the children and add them to the queue ...
+                for (ChildNode child : info.getChildren()) {
+                    nodesToDelete.add(child.getUuid());
+                }
+            }
+            ++numDeleted;
+        }
+        return numDeleted;
+    }
+
+    /**
+     * Find the best {@link JcrNodeDefinition child definition} for a child with the specified name given the named primary type
+     * and mixin types.
+     * 
+     * @param childName the name of the child that is to be added
+     * @param childPrimaryTypeName the name of the child's primary type, or null if the child's primary type is not known
+     * @param requiresMultipleSns true if there is at least one existing child with the same name, requiring the child node type
+     *        to allow same-name-siblings, or false if the child will be the first child with the supplied name
+     * @param primaryTypeName the name of the primary type for the parent; may not be null
+     * @param mixinTypeNames the names of the mixin types for the parent; may be null or empty if the parent has no mixins
+     * @return the child node definition that can be used (which may be a residual definition), or null if there is no such node
+     *         type
+     */
+    protected JcrNodeDefinition findBestChildDefinition( Name childName,
+                                                         Name childPrimaryTypeName,
+                                                         boolean requiresMultipleSns,
+                                                         Name primaryTypeName,
+                                                         Collection<Name> mixinTypeNames ) {
+        // First check the primary type for a child definition with the supplied name ...
+        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        JcrNodeDefinition definition = primaryType.findBestNodeDefinitionForChild(childName, childPrimaryTypeName);
+        if (definition != null) {
+            // definition.
+        }
+        return null;
+    }
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -28,11 +28,15 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
-import net.jcip.annotations.Immutable;
+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.NameFactory;
 import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.DnaLexicon;
 import org.jboss.dna.jcr.NodeDefinitionId;
 
 /**
@@ -41,7 +45,6 @@
  * Each instance maintains a reference to the original (usually immutable) NodeInfo representation that was probably read from the
  * repository.
  */
- at Immutable
 public class ChangedNodeInfo implements NodeInfo {
 
     protected static final PropertyInfo DELETED_PROPERTY = null;
@@ -68,6 +71,18 @@
     private Map<Name, PropertyInfo> changedProperties;
 
     /**
+     * The updated list of mixin node type names. This is merely a cached version of what's already in the
+     * {@link JcrLexicon#MIXIN_TYPES "jcr:mixinTypes"} property.
+     */
+    private Set<Name> changedMixinTypeNames;
+
+    /**
+     * The updated node definition, which may be changed when this node is moved to a different parent (with a different node
+     * type)
+     */
+    private NodeDefinitionId changedDefinitionId;
+
+    /**
      * Create an immutable NodeInfo instance.
      * 
      * @param original the original node information, may not be null
@@ -135,13 +150,39 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
+     */
+    public Set<Name> getMixinTypeNames() {
+        if (changedMixinTypeNames != null) return changedMixinTypeNames;
+        return original.getMixinTypeNames();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
      */
     public NodeDefinitionId getDefinitionId() {
+        if (changedDefinitionId != null) return changedDefinitionId;
         return original.getDefinitionId();
     }
 
     /**
+     * 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).
+     * 
+     * @param definitionId the new property definition identifier; may not be null
+     * @see #setProperty(PropertyInfo, ValueFactories)
+     */
+    public void setDefinitionId( NodeDefinitionId definitionId ) {
+        if (!getDefinitionId().equals(definitionId)) {
+            assert definitionId != null;
+            changedDefinitionId = definitionId;
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
@@ -172,43 +213,42 @@
     /**
      * Remove a child from the children. This method only uses the child's UUID to identify the contained ChildNode instance that
      * should be removed.
-     * <p>
-     * Note that this method returns the new {@link Children} container, which is the same as would be returned by
-     * {@link #getChildren()} called immediately after this method.
-     * </p>
      * 
      * @param childUUID the UUID of the child that is to be removed; may not be null
      * @param factory the path factory that should be used to create a {@link Segment} for replacement {@link ChildNode} objects
      *        for nodes with the same name that and higher same-name-sibiling indexes.
-     * @return the Children object that has the modified children
+     * @return the child node that was removed, or null if no such child could be removed
      */
-    public Children removeChild( UUID childUUID,
-                                 PathFactory factory ) {
+    public ChildNode removeChild( UUID childUUID,
+                                  PathFactory factory ) {
+        ChildNode deleted = null;
         if (changedChildren == null) {
             // Create the changed children. First check whether there are 0 or 1 child ...
             Children existing = original.getChildren();
             int numExisting = existing.size();
             if (numExisting == 0) {
                 // nothing to do, so return the original's children
-                return existing;
+                return null;
             }
-            if (existing.getChild(childUUID) == null) {
-                // The requested child doesn't exist in the children, so return silently ...
-                return existing;
+            deleted = existing.getChild(childUUID);
+            if (deleted == null) {
+                // The requested child doesn't exist in the children, so return ...
+                return null;
             }
             if (numExisting == 1) {
                 // We're removing the only child in the original ...
                 changedChildren = new ChangedChildren(existing.getParentUuid());
-                return changedChildren;
+                return existing.getChild(childUUID);
             }
             // There is at least one child, so create the new children container ...
             assert existing instanceof InternalChildren;
             InternalChildren internal = (InternalChildren)existing;
             changedChildren = internal.without(childUUID, factory);
         } else {
+            deleted = changedChildren.getChild(childUUID);
             changedChildren = changedChildren.without(childUUID, factory);
         }
-        return changedChildren;
+        return deleted;
     }
 
     /**
@@ -271,8 +311,10 @@
         return original.getProperty(name);
     }
 
-    public PropertyInfo setProperty( PropertyInfo newProperty ) {
+    public PropertyInfo setProperty( PropertyInfo newProperty,
+                                     ValueFactories factories ) {
         Name name = newProperty.getPropertyName();
+        PropertyInfo previous = null;
         if (changedProperties == null) {
             // There were no changes made yet ...
 
@@ -281,18 +323,33 @@
             changedProperties.put(name, newProperty);
 
             // And return the original property (or null if there was none) ...
-            return original.getProperty(name);
+            previous = original.getProperty(name);
+        } else if (changedProperties.containsKey(name)) {
+            // The property was already changed, in which case we need to return the changed one ...
+            previous = changedProperties.put(name, newProperty);
+        } else {
+            // Otherwise, the property was not yet changed or deleted ...
+            previous = original.getProperty(name);
+            changedProperties.put(name, newProperty);
         }
-        // The property may already have been changed, in which case we need to return the changed one ...
-        if (changedProperties.containsKey(name)) {
-            PropertyInfo changed = changedProperties.put(name, null);
-            // The named property was indeed deleted ...
-            return changed;
+        // If this property was the "jcr:mixinTypes" property, update the cached values ...
+        if (name.equals(JcrLexicon.MIXIN_TYPES)) {
+            if (changedMixinTypeNames == null) {
+                changedMixinTypeNames = new HashSet<Name>();
+            } else {
+                changedMixinTypeNames.clear();
+            }
+            NameFactory nameFactory = factories.getNameFactory();
+            for (Object value : newProperty.getProperty()) {
+                changedMixinTypeNames.add(nameFactory.create(value));
+            }
+        } else if (name.equals(DnaLexicon.NODE_DEFINITON)) {
+            ValueFactory<String> stringFactory = factories.getStringFactory();
+            String value = stringFactory.create(newProperty.getProperty().getFirstValue());
+            changedDefinitionId = NodeDefinitionId.fromString(value, factories.getNameFactory());
         }
-        // Otherwise, the property was not yet changed or deleted ...
-        PropertyInfo changed = original.getProperty(name);
-        changedProperties.put(name, newProperty);
-        return changed;
+
+        return previous;
     }
 
     public PropertyInfo removeProperty( Name name ) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -73,4 +73,12 @@
      */
     Iterator<ChildNode> getChildren( Name name );
 
+    /**
+     * Get the number of same-name-siblings that all share the supplied name.
+     * 
+     * @param name the name for the children; may not be null
+     * @return the number of same-name-siblings with the supplied name
+     */
+    int getCountOfSameNameSiblingsWithName( Name name );
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -102,6 +102,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name)
+     */
+    public int getCountOfSameNameSiblingsWithName( Name name ) {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
      *      org.jboss.dna.graph.property.PathFactory)
      */

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -170,6 +170,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name)
+     */
+    public int getCountOfSameNameSiblingsWithName( Name name ) {
+        return this.childrenByName.get(name).size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
      *      org.jboss.dna.graph.property.PathFactory)
      */

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr.cache;
 
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -43,19 +44,22 @@
     private final NodeDefinitionId definition;
     private final Children children;
     private final Map<Name, PropertyInfo> properties;
+    private final Set<Name> mixinTypeNames;
 
     /**
      * Create an immutable NodeInfo instance.
      * 
      * @param originalLocation the original location
      * @param primaryTypeName the name of the node's primary type
+     * @param mixinTypeNames the names of the mixin types for this node, or null if there are none
      * @param definition the definition used when creating the node
      * @param parent the parent
-     * @param children the immutable children
-     * @param properties the unmodifiable map of properties
+     * @param children the immutable children; may be null if there are no children
+     * @param properties the unmodifiable map of properties; may be null if there are no properties
      */
     public ImmutableNodeInfo( Location originalLocation,
                               Name primaryTypeName,
+                              Set<Name> mixinTypeNames,
                               NodeDefinitionId definition,
                               UUID parent,
                               Children children,
@@ -65,11 +69,17 @@
         this.definition = definition;
         this.parent = parent;
         this.uuid = this.originalLocation.getUuid();
-        this.children = children;
+        this.children = children != null ? children : new EmptyChildren(this.uuid);
+        if (properties == null) properties = Collections.emptyMap();
         this.properties = properties;
+        if (mixinTypeNames == null) mixinTypeNames = Collections.emptySet();
+        this.mixinTypeNames = mixinTypeNames;
         assert this.uuid != null;
         assert this.definition != null;
         assert this.primaryTypeName != null;
+        assert this.children != null;
+        assert this.mixinTypeNames != null;
+        assert this.properties != null;
     }
 
     /**
@@ -111,6 +121,15 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
+     */
+    public Set<Name> getMixinTypeNames() {
+        return mixinTypeNames;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
      */
     public NodeDefinitionId getDefinitionId() {

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -55,6 +55,13 @@
     public Name getPrimaryTypeName();
 
     /**
+     * Get the names of the mixin types for this node.
+     * 
+     * @return the unmodifiable set of mixin type names; never null but possibly empty
+     */
+    public Set<Name> getMixinTypeNames();
+
+    /**
      * @return definition
      */
     public NodeDefinitionId getDefinitionId();

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-20 18:22:45 UTC (rev 784)
@@ -66,6 +66,10 @@
 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}"
 
+unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
+unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}" cannot be moved under a decendant node ("{1}") 
+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
 SPEC_NAME_DESC = Content Repository for Java Technology API

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -72,6 +72,18 @@
             public boolean isNode() {
                 return false;
             }
+
+            public void refresh( boolean keepChanges ) {
+                throw new UnsupportedOperationException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            public void save() {
+                throw new UnsupportedOperationException();
+            }
         };
     }
 

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -100,6 +100,10 @@
         public String getPath() throws RepositoryException {
             return cache.getPathFor(nodeInfo()).getString(namespaces());
         }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
     }
 
     private ExecutionContext context;

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -25,8 +25,11 @@
 
 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.graph.IsNodeWithChildren.hasChildren;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 import static org.junit.matchers.JUnitMatchers.hasItems;
 import static org.mockito.Mockito.stub;
 import java.io.File;
@@ -39,16 +42,20 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.nodetype.NodeType;
 import org.jboss.dna.common.statistic.Stopwatch;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.NodeInfo;
 import org.jboss.dna.jcr.cache.PropertyInfo;
@@ -63,7 +70,6 @@
  */
 public class SessionCacheTest {
 
-    private String workspaceName;
     private ExecutionContext context;
     private JcrNodeTypeManager nodeTypes;
     private Stopwatch sw;
@@ -85,8 +91,6 @@
         context = new ExecutionContext();
         context.getNamespaceRegistry().register("vehix", "http://example.com/vehicles");
 
-        workspaceName = "theWorkspace";
-
         stub(session.getExecutionContext()).toReturn(context);
         stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
 
@@ -97,15 +101,9 @@
         nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
         stub(session.nodeTypeManager()).toReturn(nodeTypes);
 
-        InMemoryRepositorySource source = new InMemoryRepositorySource();
-        source.setName("store");
-        source.setDefaultWorkspaceName(workspaceName);
-        store = Graph.create(source, context);
-
-        // Import the "cars.xml" file into the repository
-        store.importXmlFrom(new File("src/test/resources/vehicles.xml")).into("/");
-
-        cache = new SessionCache(session, workspaceName, context, nodeTypes, store);
+        // Now set up the graph and session cache ...
+        store = getGraph("vehicles"); // imports the "/src/test/resources/vehicles.xml" file
+        cache = getCache("vehicles");
     }
 
     /**
@@ -164,8 +162,8 @@
                                 File file ) throws IOException, SAXException {
         InMemoryRepositorySource source = new InMemoryRepositorySource();
         source.setName(repositoryName);
-        source.setDefaultWorkspaceName(workspaceName);
         Graph graph = Graph.create(source, context);
+        graph.createWorkspace().named(workspaceName);
 
         if (file != null) {
             graph.importXmlFrom(file).into("/");
@@ -229,6 +227,71 @@
         }
     }
 
+    protected void assertDoesExist( Graph graph,
+                                    Location location ) {
+        Node node = store.getNodeAt(location);
+        assertThat(node, is(notNullValue()));
+        assertThat(node.getLocation(), is(location));
+    }
+
+    protected void assertDoesNotExist( Graph graph,
+                                       Location location ) {
+        try {
+            store.getNodeAt(location);
+            fail("Shouldn't have found the node " + location);
+        } catch (PathNotFoundException e) {
+            // expected
+        }
+    }
+
+    protected void assertDoesNotExist( Graph graph,
+                                       Path path ) {
+        try {
+            store.getNodeAt(path);
+            fail("Shouldn't have found the node " + path);
+        } catch (PathNotFoundException e) {
+            // expected
+        }
+    }
+
+    protected void assertDoesNotExist( Graph graph,
+                                       UUID uuid ) {
+        try {
+            store.getNodeAt(uuid);
+            fail("Shouldn't have found the node " + uuid);
+        } catch (PathNotFoundException e) {
+            // expected
+        }
+    }
+
+    protected void assertDoNotExist( Graph graph,
+                                     List<Location> locations ) {
+        for (Location location : locations)
+            assertDoesNotExist(graph, location);
+    }
+
+    protected void assertDoesNotExist( SessionCache cache,
+                                       UUID uuid ) throws InvalidItemStateException {
+        assertThat(cache.findNodeInfoInCache(uuid), is(nullValue()));
+    }
+
+    protected void assertIsDeleted( SessionCache cache,
+                                    UUID uuid ) {
+        try {
+            cache.findNodeInfoInCache(uuid);
+            fail("Shouldn't have found the node " + uuid);
+        } catch (InvalidItemStateException err) {
+            // expected
+        }
+    }
+
+    protected void assertDoesExist( SessionCache cache,
+                                    UUID uuid ) throws InvalidItemStateException {
+        NodeInfo info = cache.findNodeInfoInCache(uuid);
+        assertThat(info, is(notNullValue()));
+        assertThat(info.getUuid(), is(uuid));
+    }
+
     @Test
     public void shouldCreateWithValidParameters() {
         assertThat(cache, is(notNullValue()));
@@ -386,10 +449,6 @@
 
     @Test
     public void shouldFindInfoForNodeUsingRelativePathFromNonRoot() throws Exception {
-        String sourceName = "vehicles";
-        Graph store = getGraph(sourceName);
-        SessionCache cache = getCache(sourceName);
-
         // Verify that the node does exist in the source ...
         Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
         Node carsNode = store.getNodeAt(carsAbsolutePath);
@@ -430,10 +489,6 @@
 
     @Test
     public void shouldFindJcrNodeUsingAbsolutePaths() throws Exception {
-        String sourceName = "vehicles";
-        Graph store = getGraph(sourceName);
-        SessionCache cache = getCache(sourceName);
-
         // Verify that the node does exist in the source ...
         Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
         Node carsNode = store.getNodeAt(carsAbsolutePath);
@@ -477,4 +532,99 @@
         assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
         assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
     }
+
+    @Test
+    public void shouldDeleteLeafNode() throws Exception {
+        // Find the state of some Graph nodes we'll be using in the test ...
+        Node utility = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility");
+        Node lr2Node = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR2");
+        Node lr3Node = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        int numChildrenOfUtility = utility.getChildren().size();
+        assertThat(numChildrenOfUtility, is(4));
+        assertThat(utility.getChildren(), hasChildren(segment("vehix:Land Rover LR2"),
+                                                      segment("vehix:Land Rover LR3"),
+                                                      segment("vehix:Hummer H3"),
+                                                      segment("vehix:Ford F-150")));
+
+        // Now get the editor for the 'vehix:Utility' node ...
+        NodeEditor editor = cache.getEditorFor(utility.getLocation().getUuid());
+        assertThat(editor, is(notNullValue()));
+
+        // Destroy the LR3 node, which is a leaf ...
+        editor.destroyChild(lr3Node.getLocation().getUuid());
+
+        // Verify that the store has not yet been changed ...
+        assertDoesExist(store, lr3Node.getLocation());
+
+        // Save the session and verify that the node was deleted ...
+        cache.save();
+        Node utilityNode2 = store.getNodeAt(utility.getLocation());
+        assertThat(utilityNode2.getChildren().size(), is(numChildrenOfUtility - 1));
+        assertThat(utilityNode2.getChildren(), hasChildren(segment("vehix:Land Rover LR2"), // no LR3!
+                                                           segment("vehix:Hummer H3"),
+                                                           segment("vehix:Ford F-150")));
+        // Should no longer find the LR3 node in the graph ...
+        assertDoesNotExist(store, lr3Node.getLocation().getUuid());
+        assertDoesExist(store, lr2Node.getLocation());
+    }
+
+    @Test
+    public void shouldDeleteNonLeafNode() throws Exception {
+        // Find the state of some Graph nodes we'll be using in the test ...
+        Node carsNode = store.getNodeAt("/vehix:Vehicles/vehix:Cars");
+        Node utility = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility");
+        Node hybrid = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Hybrid");
+        Node luxury = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Luxury");
+        Node sports = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Sports");
+        Node lr3 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+        Node lr2 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR2");
+        Node f150 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Ford F-150");
+        int numChildrenOfCars = carsNode.getChildren().size();
+        assertThat(numChildrenOfCars, is(4));
+        assertThat(carsNode.getChildren(), hasChildren(segment("vehix:Hybrid"),
+                                                       segment("vehix:Sports"),
+                                                       segment("vehix:Luxury"),
+                                                       segment("vehix:Utility")));
+
+        // Load the LR2 and Hummer nodes ...
+        NodeInfo h3i = cache.findNodeInfo(utility.getLocation().getUuid(), path("vehix:Hummer H3"));
+        NodeInfo lr2i = cache.findNodeInfo(utility.getLocation().getUuid(), path("vehix:Land Rover LR2"));
+        assertThat(h3i, is(notNullValue()));
+        assertThat(lr2i, is(notNullValue()));
+        assertThat(lr2i.getUuid(), is(lr2.getLocation().getUuid()));
+
+        // Now get the editor for the 'vehix:Cars' node ...
+        NodeEditor editor = cache.getEditorFor(carsNode.getLocation().getUuid());
+        assertThat(editor, is(notNullValue()));
+
+        // Destroy the Utility node, which is NOT a leaf ...
+        editor.destroyChild(utility.getLocation().getUuid());
+
+        // Verify that the store has not yet been changed ...
+        assertDoesExist(store, utility.getLocation());
+
+        // ... but the utility node and its two loaded children have been marked for deletion ...
+        assertIsDeleted(cache, utility.getLocation().getUuid());
+        assertIsDeleted(cache, h3i.getUuid());
+        assertIsDeleted(cache, lr2i.getUuid());
+
+        // Save the session and verify that the Utility node and its children were deleted ...
+        cache.save();
+        Node carsNode2 = store.getNodeAt(carsNode.getLocation());
+        assertThat(carsNode2.getChildren().size(), is(numChildrenOfCars - 1));
+        assertThat(carsNode2.getChildren(),
+                   hasChildren(segment("vehix:Hybrid"), segment("vehix:Sports"), segment("vehix:Luxury")));
+        // Should no longer find the Utility node in the graph ...
+        assertDoesNotExist(store, utility.getLocation());
+        assertDoesNotExist(cache, utility.getLocation().getUuid());
+        assertDoesNotExist(cache, h3i.getUuid());
+        assertDoesNotExist(cache, lr2i.getUuid());
+        assertDoesNotExist(cache, lr3.getLocation().getUuid());
+        assertDoesNotExist(cache, f150.getLocation().getUuid());
+        assertDoNotExist(store, utility.getChildren());
+        assertDoesExist(store, hybrid.getLocation());
+        assertDoesExist(store, luxury.getLocation());
+        assertDoesExist(store, sports.getLocation());
+    }
+
 }

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-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java	2009-03-20 18:22:45 UTC (rev 784)
@@ -37,13 +37,16 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 import java.util.HashMap;
+import java.util.HashSet;
 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;
@@ -63,6 +66,7 @@
     private NodeDefinitionId definitionId;
     private ChangedChildren children;
     private Map<Name, PropertyInfo> properties;
+    private Set<Name> mixinTypeNames;
     private ChangedNodeInfo changes;
 
     @Before
@@ -78,7 +82,8 @@
         definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"));
         children = new ChangedChildren(uuid);
         properties = new HashMap<Name, PropertyInfo>();
-        original = new ImmutableNodeInfo(location, primaryTypeName, definitionId, uuid, children, properties);
+        mixinTypeNames = new HashSet<Name>();
+        original = new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definitionId, uuid, children, properties);
 
         // Create the changed node representation ...
         changes = new ChangedNodeInfo(original);
@@ -116,7 +121,7 @@
         Name propName = name(name);
         PropertyInfo propertyInfo = mock(PropertyInfo.class);
         stub(propertyInfo.getPropertyName()).toReturn(propName);
-        changes.setProperty(propertyInfo);
+        changes.setProperty(propertyInfo, context.getValueFactories());
         return propertyInfo;
     }
 
@@ -137,7 +142,7 @@
         // 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(sameInstance(changes.getChildren())));
+        assertThat(changes.removeChild(child.getUuid(), pathFactory), is(notNullValue()));
         // Verify it no longer exists ...
         assertThat(changes.getChildren().getChild(child.getUuid()), is(nullValue()));
     }
@@ -158,6 +163,28 @@
     }
 
     @Test
+    public void shouldHaveMixinTypeNamesFromOriginal() {
+        assertThat(changes.getMixinTypeNames(), is(sameInstance(mixinTypeNames)));
+    }
+
+    @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 shouldHaveNodeDefinitionIdFromOriginal() {
         assertThat(changes.getDefinitionId(), is(sameInstance(definitionId)));
     }
@@ -428,7 +455,8 @@
         // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
         Children before = changes.getChildren();
         int beforeSize = before.size();
-        Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+        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())));
@@ -451,7 +479,8 @@
         // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
         Children before = changes.getChildren();
         int beforeSize = before.size();
-        Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+        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())));
@@ -471,7 +500,7 @@
         // Modify the property ...
         PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
         stub(newPropertyInfo.getPropertyName()).toReturn(name("test"));
-        PropertyInfo previous = changes.setProperty(newPropertyInfo);
+        PropertyInfo previous = changes.setProperty(newPropertyInfo, context.getValueFactories());
         assertThat(previous, is(sameInstance(propertyInfo)));
 
         // Verify we can find the new property ...



From dna-commits at lists.jboss.org  Fri Mar 20 14:40:20 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 20 Mar 2009 14:40:20 -0400
Subject: [dna-commits] DNA SVN: r785 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lkjdk-0004g2-7R@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-20 14:40:19 -0400 (Fri, 20 Mar 2009)
New Revision: 785

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.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/JcrNodeTypeManager.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.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/JcrValue.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/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Log:
DNA-302 Make JcrNodeTypeManager not dependent on JcrSession

Applied the patch, which splits JcrNodeTypeManager into repository-scoped functionality (RepositoryNodeTypeManager) and session-scoped functionality (JcrNodeTypeManager) with the key difference being that the session-scoped node type manager is able to translate names based on transient namespace remappings. 

The RepositoryNodeTypeManager is the maintainer of node type information for the entire repository at run-time. The 
repository manager maintains a list of all node types and the ability to retrieve node types by Name. 

The JCR 1.0 and 2.0 specifications both require that node type information be shared across all sessions within a 
repository and that the javax.jcr.NodeTypeManager perform operations based on the string versions of Names based 
on the permanent (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by 
maintaining a single master repository of all node type information (the RepositoryNodeTypeManager) and per-session 
wrappers (JcrNodeTypeManager) for this master repository that perform String to Name translation based on the 
javax.jcr.Session's transient mappings and then delegating node type lookups to the repository manager. 

Also removed JcrSession references from all the type-related objects and replaced them with ExecutionContexts.

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -100,14 +100,11 @@
      */
     public final String getUUID() throws RepositoryException {
         // Return "jcr:uuid" only if node is referenceable
-        Property mixinsProp = getProperty(JcrLexicon.MIXIN_TYPES);
-        if (mixinsProp != null) {
-            String referenceableMixinName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
-            for (Value value : mixinsProp.getValues()) {
-                if (referenceableMixinName.equals(value.getString())) return nodeUuid.toString();
-            }
+        String referenceableTypeName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
+        if (!isNodeType(referenceableTypeName)) {
+            throw new UnsupportedRepositoryOperationException();
         }
-        throw new UnsupportedRepositoryOperationException();
+        return nodeUuid.toString();
     }
 
     /**
@@ -171,7 +168,7 @@
      * @see javax.jcr.Node#getMixinNodeTypes()
      */
     public NodeType[] getMixinNodeTypes() throws RepositoryException {
-        NodeTypeManager nodeTypeManager = session().getWorkspace().getNodeTypeManager();
+        NodeTypeManager nodeTypeManager = session().nodeTypeManager();
         Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
         if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
         List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -57,6 +57,9 @@
     protected static final boolean ORDERABLE_CHILD_NODES = true;
     protected static final boolean UNORDERABLE_CHILD_NODES = false;
 
+    /** Link to the repository node type manager to which the nodes defined in this source belong. */
+    protected static final RepositoryNodeTypeManager NO_NODE_TYPE_MANAGER = null;
+
     /** The predecessor node type source. */
     private final JcrNodeTypeSource predecessor;
 

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -78,7 +78,7 @@
     }
 
     JcrValue createValue( Object value ) throws RepositoryException {
-        return new JcrValue(context().getValueFactories(), propertyInfo().getPropertyType(), value);
+        return new JcrValue(context().getValueFactories(), this.cache, propertyInfo().getPropertyType(), value);
     }
 
     @Override

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -30,6 +30,7 @@
 import javax.jcr.PropertyType;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrMixLexicon;
 
 /**
@@ -43,7 +44,7 @@
     /** The list of mixin node types. */
     private final List<JcrNodeType> mixinNodeTypes;
 
-    DnaBuiltinNodeTypeSource( JcrSession session,
+    DnaBuiltinNodeTypeSource( ExecutionContext context,
                               JcrNodeTypeSource predecessor ) {
         super(predecessor);
 
@@ -53,28 +54,29 @@
         JcrNodeType base = findType(JcrNtLexicon.BASE);
 
         if (base == null) {
-            String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
-            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+            String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
         JcrNodeType referenceable = findType(JcrMixLexicon.REFERENCEABLE);
 
         if (referenceable == null) {
-            String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(session.getExecutionContext().getNamespaceRegistry());
-            String namespaceTypeName = DnaLexicon.SYSTEM.getString(session.getExecutionContext().getNamespaceRegistry());
+            String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.SYSTEM.getString(context.getNamespaceRegistry());
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
         // Stubbing in child node and property definitions for now
         JcrNodeType namespace = new JcrNodeType(
-                                                session,
+                                                context,
+                                                NO_NODE_TYPE_MANAGER,
                                                 DnaLexicon.NAMESPACE,
                                                 Arrays.asList(new NodeType[] {base}),
                                                 DnaLexicon.URI,
                                                 NO_CHILD_NODES,
                                                 Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                     session,
+                                                                                                                     context,
                                                                                                                      null,
                                                                                                                      DnaLexicon.URI,
                                                                                                                      OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -88,12 +90,13 @@
                                                 NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType namespaces = new JcrNodeType(
-                                                 session,
+                                                 context,
+                                                 NO_NODE_TYPE_MANAGER,
                                                  DnaLexicon.NAMESPACES,
                                                  Arrays.asList(new NodeType[] {base}),
                                                  NO_PRIMARY_ITEM_NAME,
                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                              session,
+                                                                                                              context,
                                                                                                               null,
                                                                                                               null,
                                                                                                               OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -106,12 +109,13 @@
                                                  NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType system = new JcrNodeType(
-                                             session,
+                                             context,
+                                             NO_NODE_TYPE_MANAGER,
                                              DnaLexicon.SYSTEM,
                                              Arrays.asList(new NodeType[] {base}),
                                              NO_PRIMARY_ITEM_NAME,
                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                          session,
+                                                                                                          context,
                                                                                                           null,
                                                                                                           DnaLexicon.NAMESPACES,
                                                                                                           OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -123,25 +127,18 @@
                                                                                                           new NodeType[] {namespaces})}),
                                              NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType root = new JcrNodeType(session, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base, referenceable}),
-                                           NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
-                                               new JcrNodeDefinition(session, null, JcrLexicon.SYSTEM,
-                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
-                                                                     true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
-                                               new JcrNodeDefinition(session, null, ALL_NODES,
-                                                                     OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
-                                                                     false, true, JcrNtLexicon.UNSTRUCTURED,
-                                                                     new NodeType[] {base}),
+        JcrNodeType root = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base,
+            referenceable}), NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
+            new JcrNodeDefinition(context, null, JcrLexicon.SYSTEM, OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
+                                  true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
+            new JcrNodeDefinition(context, null, ALL_NODES, OnParentVersionBehavior.VERSION.getJcrValue(), false, false, false,
+                                  true, JcrNtLexicon.UNSTRUCTURED, new NodeType[] {base}),
 
-                                           }), Arrays.asList(new JcrPropertyDefinition[] {
-                                               new JcrPropertyDefinition(session, null, ALL_NODES,
-                                                                         OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                         false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
-                                                                         NO_CONSTRAINTS, false),
-                                               new JcrPropertyDefinition(session, null, ALL_NODES,
-                                                                         OnParentVersionBehavior.COPY.getJcrValue(), false,
-                                                                         false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
-                                                                         NO_CONSTRAINTS, true),}), NOT_MIXIN,
+        }), 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,
                                            ORDERABLE_CHILD_NODES);
 
         primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -31,6 +31,7 @@
 import javax.jcr.Value;
 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.basic.BasicName;
 
@@ -45,27 +46,30 @@
     /** The list of mixin node types. */
     private final List<JcrNodeType> mixinNodeTypes;
 
-    JcrBuiltinNodeTypeSource( JcrSession session ) {
-        this(session, null);
+    JcrBuiltinNodeTypeSource( ExecutionContext context ) {
+        this(context, null);
     }
 
-    JcrBuiltinNodeTypeSource( JcrSession session,
+    JcrBuiltinNodeTypeSource( ExecutionContext context,
                               JcrNodeTypeSource predecessor ) {
         super(predecessor);
 
         primaryNodeTypes = new ArrayList<JcrNodeType>();
 
-        Value trueValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN, Boolean.TRUE);
-        Value ntBaseValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.NAME, JcrNtLexicon.BASE);
+        /*
+         * 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 ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
 
         // Stubbing in child node and property definitions for now
-        JcrNodeType base = new JcrNodeType(session, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
-                                           Arrays.asList(new JcrPropertyDefinition[] {
-                                               new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_TYPE,
+        JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE, NO_SUPERTYPES, 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,
                                                                          NO_CONSTRAINTS, false),
-                                               new JcrPropertyDefinition(session, null, JcrLexicon.MIXIN_TYPES,
+                                               new JcrPropertyDefinition(context, null, JcrLexicon.MIXIN_TYPES,
                                                                          OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
                                                                          false, true, NO_DEFAULT_VALUES, PropertyType.NAME,
                                                                          NO_CONSTRAINTS, true)}), NOT_MIXIN,
@@ -73,13 +77,14 @@
 
         // This needs to be declared early, as some of the primary types reference it
         JcrNodeType referenceable = new JcrNodeType(
-                                                    session,
+                                                    context,
+                                                    NO_NODE_TYPE_MANAGER,
                                                     JcrMixLexicon.REFERENCEABLE,
                                                     NO_SUPERTYPES,
                                                     NO_PRIMARY_ITEM_NAME,
                                                     NO_CHILD_NODES,
                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                         session,
+                                                                                                                         context,
                                                                                                                          null,
                                                                                                                          JcrLexicon.UUID,
                                                                                                                          OnParentVersionBehavior.INITIALIZE.getJcrValue(),
@@ -93,14 +98,15 @@
                                                     IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType childNodeDefinition = new JcrNodeType(
-                                                          session,
+                                                          context,
+                                                          NO_NODE_TYPE_MANAGER,
                                                           JcrNtLexicon.CHILD_NODE_DEFINITION,
                                                           Arrays.asList(new NodeType[] {base}),
                                                           NO_PRIMARY_ITEM_NAME,
                                                           NO_CHILD_NODES,
                                                           Arrays.asList(new JcrPropertyDefinition[] {
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.AUTO_CREATED,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -108,14 +114,14 @@
                                                                                         PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                         false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.DEFAULT_PRIMARY_TYPE,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                         false, false, false, NO_DEFAULT_VALUES,
                                                                                         PropertyType.NAME, NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.MANDATORY,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -123,14 +129,14 @@
                                                                                         PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                         false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.NAME,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                         false, false, false, NO_DEFAULT_VALUES,
                                                                                         PropertyType.NAME, NO_CONSTRAINTS, false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.ON_PARENT_VERSION,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -138,7 +144,7 @@
                                                                                         PropertyType.STRING, NO_CONSTRAINTS,
                                                                                         false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.PROTECTED,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -146,7 +152,7 @@
                                                                                         PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                         false),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.REQUIRED_PRIMARY_TYPES,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -154,7 +160,7 @@
                                                                                         new Value[] {ntBaseValue},
                                                                                         PropertyType.NAME, NO_CONSTRAINTS, true),
                                                               new JcrPropertyDefinition(
-                                                                                        session,
+                                                                                        context,
                                                                                         null,
                                                                                         JcrLexicon.SAME_NAME_SIBLINGS,
                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -164,13 +170,14 @@
                                                           UNORDERABLE_CHILD_NODES);
 
         JcrNodeType hierarchyNode = new JcrNodeType(
-                                                    session,
+                                                    context,
+                                                    NO_NODE_TYPE_MANAGER,
                                                     JcrNtLexicon.HIERARCHY_NODE,
                                                     Arrays.asList(new NodeType[] {base}),
                                                     NO_PRIMARY_ITEM_NAME,
                                                     NO_CHILD_NODES,
                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                         session,
+                                                                                                                         context,
                                                                                                                          null,
                                                                                                                          JcrLexicon.CREATED,
                                                                                                                          OnParentVersionBehavior.INITIALIZE.getJcrValue(),
@@ -184,12 +191,13 @@
                                                     NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType file = new JcrNodeType(
-                                           session,
+                                           context,
+                                           NO_NODE_TYPE_MANAGER,
                                            JcrNtLexicon.FILE,
                                            Arrays.asList(new NodeType[] {hierarchyNode}),
                                            JcrLexicon.CONTENT,
                                            Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                        session,
+                                                                                                        context,
                                                                                                         null,
                                                                                                         JcrLexicon.CONTENT,
                                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
@@ -199,12 +207,13 @@
                                            NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType folder = new JcrNodeType(
-                                             session,
+                                             context,
+                                             NO_NODE_TYPE_MANAGER,
                                              JcrNtLexicon.FOLDER,
                                              Arrays.asList(new NodeType[] {hierarchyNode}),
                                              NO_PRIMARY_ITEM_NAME,
                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                          session,
+                                                                                                          context,
                                                                                                           null,
                                                                                                           null,
                                                                                                           OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -217,12 +226,13 @@
                                              NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType frozenNode = new JcrNodeType(
-                                                 session,
+                                                 context,
+                                                 NO_NODE_TYPE_MANAGER,
                                                  JcrNtLexicon.FROZEN_NODE,
                                                  Arrays.asList(new NodeType[] {base, referenceable}),
                                                  NO_PRIMARY_ITEM_NAME,
                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                              session,
+                                                                                                              context,
                                                                                                               null,
                                                                                                               ALL_NODES,
                                                                                                               OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -233,36 +243,37 @@
                                                                                                               null,
                                                                                                               new NodeType[] {base})}),
                                                  Arrays.asList(new JcrPropertyDefinition[] {
-                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_MIXIN_TYPES,
+                                                     new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_MIXIN_TYPES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                false, false, true, NO_DEFAULT_VALUES,
                                                                                PropertyType.NAME, NO_CONSTRAINTS, true),
-                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_PRIMARY_TYPE,
+                                                     new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_PRIMARY_TYPE,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                                true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
                                                                                NO_CONSTRAINTS, false),
-                                                     new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_UUID,
+                                                     new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_UUID,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                                true, true, NO_DEFAULT_VALUES,
                                                                                PropertyType.STRING, NO_CONSTRAINTS, false),
-                                                     new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                     new JcrPropertyDefinition(context, null, ALL_NODES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                false, false, true, NO_DEFAULT_VALUES,
                                                                                PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
-                                                     new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                     new JcrPropertyDefinition(context, null, ALL_NODES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                false, false, true, NO_DEFAULT_VALUES,
                                                                                PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
                                                  NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
         JcrNodeType linkedFile = new JcrNodeType(
-                                                 session,
+                                                 context,
+                                                 NO_NODE_TYPE_MANAGER,
                                                  JcrNtLexicon.LINKED_FILE,
                                                  Arrays.asList(new NodeType[] {hierarchyNode}),
                                                  JcrLexicon.CONTENT,
                                                  NO_CHILD_NODES,
                                                  Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                      session,
+                                                                                                                      context,
                                                                                                                       null,
                                                                                                                       JcrLexicon.CONTENT,
                                                                                                                       OnParentVersionBehavior.COPY.getJcrValue(),
@@ -277,14 +288,15 @@
 
         // Had to be moved above nodeType due to dependency
         JcrNodeType propertyDefinition = new JcrNodeType(
-                                                         session,
+                                                         context,
+                                                         NO_NODE_TYPE_MANAGER,
                                                          JcrNtLexicon.PROPERTY_DEFINITION,
                                                          Arrays.asList(new NodeType[] {base}),
                                                          NO_PRIMARY_ITEM_NAME,
                                                          NO_CHILD_NODES,
                                                          Arrays.asList(new JcrPropertyDefinition[] {
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.AUTO_CREATED,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -292,7 +304,7 @@
                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                        false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.DEFAULT_VALUES,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -300,7 +312,7 @@
                                                                                        PropertyType.UNDEFINED, NO_CONSTRAINTS,
                                                                                        true),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.MANDATORY,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -308,7 +320,7 @@
                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                        false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.MULTIPLE,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -316,21 +328,21 @@
                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                        false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.NAME,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                        false, false, false, NO_DEFAULT_VALUES,
                                                                                        PropertyType.NAME, NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.ON_PARENT_VERSION,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                        false, true, false, NO_DEFAULT_VALUES,
                                                                                        PropertyType.STRING, NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.PROTECTED,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -338,14 +350,14 @@
                                                                                        PropertyType.BOOLEAN, NO_CONSTRAINTS,
                                                                                        false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.REQUIRED_TYPE,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                        false, true, false, NO_DEFAULT_VALUES,
                                                                                        PropertyType.STRING, NO_CONSTRAINTS, false),
                                                              new JcrPropertyDefinition(
-                                                                                       session,
+                                                                                       context,
                                                                                        null,
                                                                                        JcrLexicon.VALUE_CONSTRAINTS,
                                                                                        OnParentVersionBehavior.COPY.getJcrValue(),
@@ -353,79 +365,82 @@
                                                                                        PropertyType.STRING, NO_CONSTRAINTS, true)}),
                                                          NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType nodeType = new JcrNodeType(session, JcrNtLexicon.NODE_TYPE, Arrays.asList(new NodeType[] {base}),
-                                               NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
-                                                   new JcrNodeDefinition(session, null, JcrLexicon.CHILD_NODE_DEFINITION,
+        JcrNodeType nodeType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.NODE_TYPE,
+                                               Arrays.asList(new NodeType[] {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,
                                                                          new NodeType[] {childNodeDefinition}),
-                                                   new JcrNodeDefinition(session, null, JcrLexicon.PROPERTY_DEFINITION,
+                                                   new JcrNodeDefinition(context, null, JcrLexicon.PROPERTY_DEFINITION,
                                                                          OnParentVersionBehavior.VERSION.getJcrValue(), false,
                                                                          false, false, true, JcrNtLexicon.PROPERTY_DEFINITION,
                                                                          new NodeType[] {propertyDefinition})}),
                                                Arrays.asList(new JcrPropertyDefinition[] {
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES,
                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.IS_MIXIN,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.IS_MIXIN,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES,
                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.NODE_TYPE_NAME,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.NODE_TYPE_NAME,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.NAME,
                                                                              NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_ITEM_NAME,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_ITEM_NAME,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
                                                                              NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.SUPERTYPES,
+                                                   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);
 
-        JcrNodeType query = new JcrNodeType(session, JcrNtLexicon.QUERY, Arrays.asList(new NodeType[] {base}),
-                                            NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
-                                                new JcrPropertyDefinition(session, null, JcrLexicon.LANGUAGE,
+        JcrNodeType query = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.QUERY,
+                                            Arrays.asList(new NodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                            Arrays.asList(new JcrPropertyDefinition[] {
+                                                new JcrPropertyDefinition(context, null, JcrLexicon.LANGUAGE,
                                                                           OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                           false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
                                                                           NO_CONSTRAINTS, false),
-                                                new JcrPropertyDefinition(session, null, JcrLexicon.STATEMENT,
+                                                new JcrPropertyDefinition(context, null, JcrLexicon.STATEMENT,
                                                                           OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                           false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
                                                                           NO_CONSTRAINTS, false),}), NOT_MIXIN,
                                             UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType resource = new JcrNodeType(session, JcrNtLexicon.RESOURCE,
+        JcrNodeType resource = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.RESOURCE,
                                                Arrays.asList(new NodeType[] {base, referenceable}), JcrLexicon.DATA,
                                                NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.DATA,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.DATA,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.BINARY,
                                                                              NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.ENCODING,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.ENCODING,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              false, false, NO_DEFAULT_VALUES,
                                                                              PropertyType.STRING, NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LAST_MODIFIED,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.LAST_MODIFIED,
                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.DATE,
                                                                              NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.MIMETYPE,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.MIMETYPE,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
                                                                              true, false, NO_DEFAULT_VALUES, PropertyType.STRING,
                                                                              NO_CONSTRAINTS, false),}), NOT_MIXIN,
                                                UNORDERABLE_CHILD_NODES);
 
         JcrNodeType unstructured = new JcrNodeType(
-                                                   session,
+                                                   context,
+                                                   NO_NODE_TYPE_MANAGER,
                                                    JcrNtLexicon.UNSTRUCTURED,
                                                    Arrays.asList(new NodeType[] {base}),
                                                    NO_PRIMARY_ITEM_NAME,
                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                                session,
+                                                                                                                context,
                                                                                                                 null,
                                                                                                                 ALL_NODES,
                                                                                                                 OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -436,23 +451,24 @@
                                                                                                                 JcrNtLexicon.UNSTRUCTURED,
                                                                                                                 new NodeType[] {base}),}),
                                                    Arrays.asList(new JcrPropertyDefinition[] {
-                                                       new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                       new JcrPropertyDefinition(context, null, ALL_NODES,
                                                                                  OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                  false, false, false, NO_DEFAULT_VALUES,
                                                                                  PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
-                                                       new JcrPropertyDefinition(session, null, ALL_NODES,
+                                                       new JcrPropertyDefinition(context, null, ALL_NODES,
                                                                                  OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                  false, false, false, NO_DEFAULT_VALUES,
                                                                                  PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
                                                    NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
         JcrNodeType version = new JcrNodeType(
-                                              session,
+                                              context,
+                                              NO_NODE_TYPE_MANAGER,
                                               JcrNtLexicon.VERSION,
                                               Arrays.asList(new NodeType[] {base, referenceable}),
                                               NO_PRIMARY_ITEM_NAME,
                                               Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
-                                                                                                           session,
+                                                                                                           context,
                                                                                                            null,
                                                                                                            JcrLexicon.FROZEN_NODE,
                                                                                                            OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -463,28 +479,29 @@
                                                                                                            null,
                                                                                                            new NodeType[] {frozenNode}),}),
                                               Arrays.asList(new JcrPropertyDefinition[] {
-                                                  new JcrPropertyDefinition(session, null, JcrLexicon.CREATED,
+                                                  new JcrPropertyDefinition(context, null, JcrLexicon.CREATED,
                                                                             OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                             true, true, NO_DEFAULT_VALUES, PropertyType.DATE,
                                                                             NO_CONSTRAINTS, false),
-                                                  new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+                                                  new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
                                                                             OnParentVersionBehavior.ABORT.getJcrValue(), false,
                                                                             false, true, NO_DEFAULT_VALUES,
                                                                             PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                  new JcrPropertyDefinition(session, null, JcrLexicon.SUCCESSORS,
+                                                  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(
-                                                    session,
+                                                    context,
+                                                    NO_NODE_TYPE_MANAGER,
                                                     JcrNtLexicon.VERSION_LABELS,
                                                     Arrays.asList(new NodeType[] {base}),
                                                     NO_PRIMARY_ITEM_NAME,
                                                     NO_CHILD_NODES,
                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                         session,
+                                                                                                                         context,
                                                                                                                          null,
                                                                                                                          ALL_NODES,
                                                                                                                          OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -498,25 +515,26 @@
                                                     NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType versionHistory = new JcrNodeType(
-                                                     session,
+                                                     context,
+                                                     NO_NODE_TYPE_MANAGER,
                                                      JcrNtLexicon.VERSION_HISTORY,
                                                      Arrays.asList(new NodeType[] {base, referenceable}),
                                                      NO_PRIMARY_ITEM_NAME,
                                                      Arrays.asList(new JcrNodeDefinition[] {
-                                                         new JcrNodeDefinition(session, null, JcrLexicon.ROOT_VERSION,
+                                                         new JcrNodeDefinition(context, null, JcrLexicon.ROOT_VERSION,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                                true, true, false, JcrNtLexicon.VERSION,
                                                                                new NodeType[] {version}),
-                                                         new JcrNodeDefinition(session, null, JcrLexicon.VERSION_LABELS,
+                                                         new JcrNodeDefinition(context, null, JcrLexicon.VERSION_LABELS,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                                true, true, false, JcrNtLexicon.VERSION_LABELS,
                                                                                new NodeType[] {versionLabels}),
-                                                         new JcrNodeDefinition(session, null, ALL_NODES,
+                                                         new JcrNodeDefinition(context, null, ALL_NODES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                false, false, true, false, JcrNtLexicon.VERSION,
                                                                                new NodeType[] {version}),}),
                                                      Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                          session,
+                                                                                                                          context,
                                                                                                                           null,
                                                                                                                           JcrLexicon.VERSIONABLE_UUID,
                                                                                                                           OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -531,13 +549,14 @@
 
         Name CHILD_VERSION_HISTORY = new BasicName(JcrLexicon.Namespace.URI, "childVersionHistory");
         JcrNodeType versionedChild = new JcrNodeType(
-                                                     session,
+                                                     context,
+                                                     NO_NODE_TYPE_MANAGER,
                                                      JcrNtLexicon.VERSIONED_CHILD,
                                                      Arrays.asList(new NodeType[] {base}),
                                                      NO_PRIMARY_ITEM_NAME,
                                                      NO_CHILD_NODES,
                                                      Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
-                                                                                                                          session,
+                                                                                                                          context,
                                                                                                                           null,
                                                                                                                           CHILD_VERSION_HISTORY,
                                                                                                                           OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -556,42 +575,43 @@
 
         mixinNodeTypes = new ArrayList<JcrNodeType>();
 
-        JcrNodeType lockable = new JcrNodeType(session, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME,
-                                               NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_IS_DEEP,
+        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,
                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(), false,
                                                                              false, true, NO_DEFAULT_VALUES,
                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
-                                                   new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_OWNER,
+                                                   new JcrPropertyDefinition(context, null, JcrLexicon.LOCK_OWNER,
                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(), false,
                                                                              false, true, NO_DEFAULT_VALUES, PropertyType.STRING,
                                                                              NO_CONSTRAINTS, false)}), IS_A_MIXIN,
                                                UNORDERABLE_CHILD_NODES);
 
         JcrNodeType versionable = new JcrNodeType(
-                                                  session,
+                                                  context,
+                                                  NO_NODE_TYPE_MANAGER,
                                                   JcrMixLexicon.VERSIONABLE,
                                                   Arrays.asList(new NodeType[] {referenceable}),
                                                   NO_PRIMARY_ITEM_NAME,
                                                   NO_CHILD_NODES,
                                                   Arrays.asList(new JcrPropertyDefinition[] {
-                                                      new JcrPropertyDefinition(session, null, JcrLexicon.BASE_VERSION,
+                                                      new JcrPropertyDefinition(context, null, JcrLexicon.BASE_VERSION,
                                                                                 OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                 false, true, true, NO_DEFAULT_VALUES,
                                                                                 PropertyType.REFERENCE, NO_CONSTRAINTS, false),
-                                                      new JcrPropertyDefinition(session, null, JcrLexicon.IS_CHECKED_OUT,
+                                                      new JcrPropertyDefinition(context, null, JcrLexicon.IS_CHECKED_OUT,
                                                                                 OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                 true, true, true, new Value[] {trueValue},
                                                                                 PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
-                                                      new JcrPropertyDefinition(session, null, JcrLexicon.MERGE_FAILED,
+                                                      new JcrPropertyDefinition(context, null, JcrLexicon.MERGE_FAILED,
                                                                                 OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                 false, false, true, NO_DEFAULT_VALUES,
                                                                                 PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                      new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+                                                      new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
                                                                                 OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                 false, true, true, NO_DEFAULT_VALUES,
                                                                                 PropertyType.REFERENCE, NO_CONSTRAINTS, true),
-                                                      new JcrPropertyDefinition(session, null, JcrLexicon.VERSION_HISTORY,
+                                                      new JcrPropertyDefinition(context, null, JcrLexicon.VERSION_HISTORY,
                                                                                 OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                 false, true, true, NO_DEFAULT_VALUES,
                                                                                 PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -26,6 +26,7 @@
 import javax.jcr.nodetype.ItemDefinition;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -35,7 +36,7 @@
 @Immutable
 class JcrItemDefinition implements ItemDefinition {
 
-    protected final JcrSession session;
+    protected final ExecutionContext context;
 
     protected final JcrNodeType declaringNodeType;
     protected final Name name;
@@ -44,7 +45,7 @@
     private final boolean mandatory;
     private final boolean protectedItem;
 
-    JcrItemDefinition( JcrSession session,
+    JcrItemDefinition( ExecutionContext context,
                        JcrNodeType declaringNodeType,
                        Name name,
                        int onParentVersion,
@@ -52,12 +53,9 @@
                        boolean mandatory,
                        boolean protectedItem ) {
         super();
-        this.session = session;
+        this.context = context;
         this.declaringNodeType = declaringNodeType;
-        this.name = name != null ? name : session.getExecutionContext()
-                                                 .getValueFactories()
-                                                 .getNameFactory()
-                                                 .create(JcrNodeType.RESIDUAL_ITEM_NAME);
+        this.name = name != null ? name : context.getValueFactories().getNameFactory().create(JcrNodeType.RESIDUAL_ITEM_NAME) ;
         this.onParentVersion = onParentVersion;
         this.autoCreated = autoCreated;
         this.mandatory = mandatory;
@@ -87,7 +85,7 @@
             return JcrNodeType.RESIDUAL_ITEM_NAME;
         }
 
-        return name.getString(session.getExecutionContext().getNamespaceRegistry());
+        return name.getString(context.getNamespaceRegistry());
     }
 
     /**

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -23,10 +23,10 @@
  */
 package org.jboss.dna.jcr;
 
-import javax.jcr.RepositoryException;
 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;
 
 /**
@@ -50,7 +50,10 @@
     /** A durable identifier for this node definition. */
     private NodeDefinitionId id;
 
-    JcrNodeDefinition( JcrSession session,
+    /** Link to the repository node type manager */
+    private final RepositoryNodeTypeManager nodeTypeManager;
+
+    JcrNodeDefinition( ExecutionContext context,
                        JcrNodeType declaringNodeType,
                        Name name,
                        int onParentVersion,
@@ -60,12 +63,31 @@
                        boolean allowsSameNameSiblings,
                        Name defaultPrimaryTypeName,
                        NodeType[] requiredPrimaryTypes ) {
-        super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        this.nodeTypeManager = null;
         this.allowsSameNameSiblings = allowsSameNameSiblings;
         this.defaultPrimaryTypeName = defaultPrimaryTypeName;
         this.requiredPrimaryTypes = requiredPrimaryTypes;
     }
 
+    JcrNodeDefinition( ExecutionContext context,
+                       RepositoryNodeTypeManager nodeTypeManager,
+                       JcrNodeType declaringNodeType,
+                       Name name,
+                       int onParentVersion,
+                       boolean autoCreated,
+                       boolean mandatory,
+                       boolean protectedItem,
+                       boolean allowsSameNameSiblings,
+                       Name defaultPrimaryTypeName,
+                       NodeType[] requiredPrimaryTypes ) {
+        super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        this.nodeTypeManager = nodeTypeManager;
+        this.allowsSameNameSiblings = allowsSameNameSiblings;
+        this.defaultPrimaryTypeName = defaultPrimaryTypeName;
+        this.requiredPrimaryTypes = requiredPrimaryTypes;
+    }
+
     /**
      * Get the durable identifier for this node definition.
      * 
@@ -98,21 +120,7 @@
             return null;
         }
 
-        /*
-         * Translate the name to a prefixed type based on the current transient (session) and persistent (workspace) 
-         * prefix to URI mappings.
-         */
-        String mappedTypeName = defaultPrimaryTypeName.getString(session.getExecutionContext().getNamespaceRegistry());
-
-        try {
-            return session.getWorkspace().getNodeTypeManager().getNodeType(mappedTypeName);
-        } catch (RepositoryException re) {
-            /*
-             * The spec doesn't allow us to throw a checked exception at this point, but a corrupted namespace mapping
-             * would be pretty severe. 
-             */
-            throw new IllegalStateException(JcrI18n.typeNotFound.text(mappedTypeName));
-        }
+        return nodeTypeManager.getNodeType(defaultPrimaryTypeName);
     }
 
     /**
@@ -133,7 +141,15 @@
      *         <code>declaringNodeType</code>.
      */
     JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
-        return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), isAutoCreated(), isMandatory(),
-                                     isProtected(), allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
+        return new JcrNodeDefinition(this.context, declaringNodeType.nodeTypeManager(), declaringNodeType, name,
+                                     getOnParentVersion(), isAutoCreated(), isMandatory(), isProtected(),
+                                     allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
     }
+
+    JcrNodeDefinition with( ExecutionContext context ) {
+        return new JcrNodeDefinition(context, this.nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
+                                     isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
+                                     defaultPrimaryTypeName, requiredPrimaryTypes);
+    }
+
 }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -30,13 +30,13 @@
 import java.util.Set;
 import java.util.Stack;
 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 net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -65,12 +65,16 @@
     private boolean orderableChildNodes;
 
     /**
-     * A reference to the session in which this node type exists, used to remap the internal names to their appropriate prefixed
-     * version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>&quot;nt:base&quot;</code>.).
+     * A reference to the execution context in which this node type exists, used to remap the internal names to their appropriate
+     * prefixed version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>&quot;nt:base&quot;</code>.).
      */
-    private JcrSession session;
+    private ExecutionContext context;
 
-    JcrNodeType( JcrSession session,
+    /** Link to the repository node type manager for the repository to which this node type belongs. */
+    private RepositoryNodeTypeManager nodeTypeManager;
+
+    JcrNodeType( ExecutionContext context,
+                 RepositoryNodeTypeManager nodeTypeManager,
                  Name name,
                  List<NodeType> declaredSupertypes,
                  Name primaryItemName,
@@ -78,7 +82,8 @@
                  Collection<JcrPropertyDefinition> propertyDefinitions,
                  boolean mixin,
                  boolean orderableChildNodes ) {
-        this.session = session;
+        this.context = context;
+        this.nodeTypeManager = nodeTypeManager;
         this.name = name;
         this.primaryItemName = primaryItemName;
         this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<NodeType>emptyList();
@@ -224,35 +229,6 @@
      * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
      *         are not allowed given this node type
      */
-    @Deprecated
-    JcrNodeDefinition findBestNodeDefinitionForChild( String childName,
-                                                      String primaryNodeTypeName ) {
-        // First, try to find a child node definition with the given name
-        JcrNodeDefinition childNode = getChildNodeDefinition(childName);
-
-        // If there are no named definitions in the type hierarchy, try to find a residual node definition
-        if (childNode == null) {
-            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
-        }
-
-        // Check if the node can be added with the named child node definition
-        if (childNode != null && primaryNodeTypeName != null) {
-            NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
-            if (primaryNodeType == null) return null;
-            if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) return null;
-        }
-        return childNode;
-    }
-
-    /**
-     * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
-     * primary type is not supplied, then only the name is considered when finding a best match.
-     * 
-     * @param childName the name of the child
-     * @param primaryNodeTypeName the name of the primary node type for the child
-     * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
-     *         are not allowed given this node type
-     */
     JcrNodeDefinition findBestNodeDefinitionForChild( Name childName,
                                                       Name primaryNodeTypeName ) {
         // First, try to find a child node definition with the given name
@@ -314,17 +290,8 @@
         return false;
     }
 
-    protected final NodeType getPrimaryNodeType( String primaryNodeTypeName ) {
-        try {
-            return session.getWorkspace().getNodeTypeManager().getNodeType(primaryNodeTypeName);
-        } catch (RepositoryException re) {
-            // If the node type doesn't exist, you can't add a child node with that type
-            return null;
-        }
-    }
-
     protected final NodeType getPrimaryNodeType( Name primaryNodeTypeName ) {
-        return session.nodeTypeManager().getNodeType(primaryNodeTypeName);
+        return nodeTypeManager.getNodeType(primaryNodeTypeName);
     }
 
     /**
@@ -338,7 +305,8 @@
         CheckArg.isNotNull(childNodeName, "childNodeName");
         CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
 
-        NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+        Name nodeTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
+        NodeType primaryNodeType = getPrimaryNodeType(nodeTypeName);
         if (primaryNodeType == null) {
             // The node type doesn't exist, so you can't add a child node with that type
             return false;
@@ -585,7 +553,7 @@
      */
     public String getName() {
         // Translate the name to the correct prefix. Need to check the session to support url-remapping.
-        return name.getString(session.getExecutionContext().getNamespaceRegistry());
+        return name.getString(context.getNamespaceRegistry());
     }
 
     /**
@@ -609,7 +577,7 @@
         }
 
         // Translate the name to the correct prefix. Need to check the session to support url-remapping.
-        return primaryItemName.getString(session.getExecutionContext().getNamespaceRegistry());
+        return primaryItemName.getString(context.getNamespaceRegistry());
     }
 
     /**
@@ -695,4 +663,33 @@
     public String toString() {
         return getName();
     }
+
+    /**
+     * Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different repository node type
+     * manager. This method should only be called during the initialization of the repository node type manager, unless some kind
+     * of cross-repository type shipping is implemented.
+     * 
+     * @param nodeTypeManager the new repository node type manager
+     * @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
+     */
+    final JcrNodeType with( RepositoryNodeTypeManager nodeTypeManager ) {
+        return new JcrNodeType(this.context, nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
+                               this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.orderableChildNodes);
+    }
+
+    /**
+     * Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different execution context.
+     * 
+     * @param context the new execution context
+     * @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
+     * @see JcrNodeTypeManager
+     */
+    final JcrNodeType with( ExecutionContext context ) {
+        return new JcrNodeType(context, this.nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
+                               this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.orderableChildNodes);
+    }
+
+    final RepositoryNodeTypeManager nodeTypeManager() {
+        return nodeTypeManager;
+    }
 }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -25,44 +25,35 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
-import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 
 /**
- * Local implementation of @{link NodeTypeManager}. Initialized with {@link NodeType} source data when it is created (in the
- * {@link JcrWorkspace} constructor.
+ * Local implementation of @{link NodeTypeManager}. This class handles translation between {@link Name}s and {@link String}s based
+ * on the namespace registry from the session's execution context in order to support transient namespace remappings. All
+ * {@link NodeType}s returned by this implementation are wrapped with the execution context of the session to allow proper ongoing
+ * handling of names. This implies that reference equality is not a safe test for node type equivalence.
+ * 
+ * @see RepositoryNodeTypeManager
  */
- at NotThreadSafe
+ at Immutable
 class JcrNodeTypeManager implements NodeTypeManager {
 
-    private final Map<Name, JcrNodeType> primaryNodeTypes;
-    private final Map<Name, JcrNodeType> mixinNodeTypes;
-    private final JcrSession session;
+    private final ExecutionContext context;
+    private final RepositoryNodeTypeManager repositoryTypeManager;
 
-    JcrNodeTypeManager( JcrSession session,
-                        JcrNodeTypeSource source ) {
-        this.session = session;
-        Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
-        Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
-
-        primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
-        for (JcrNodeType nodeType : primary) {
-            primaryNodeTypes.put(nodeType.getInternalName(), nodeType);
-        }
-
-        mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
-        for (JcrNodeType nodeType : mixins) {
-            mixinNodeTypes.put(nodeType.getInternalName(), nodeType);
-        }
+    JcrNodeTypeManager( ExecutionContext context,
+                        RepositoryNodeTypeManager repositoryTypeManager ) {
+        this.context = context;
+        this.repositoryTypeManager = repositoryTypeManager;
     }
 
     /**
@@ -72,6 +63,9 @@
      */
     public NodeTypeIterator getAllNodeTypes() {
 
+        Collection<JcrNodeType> mixinNodeTypes = repositoryTypeManager.getMixinNodeTypes();
+        Collection<JcrNodeType> primaryNodeTypes = repositoryTypeManager.getPrimaryNodeTypes();
+
         // TODO: Can revisit this approach later if it becomes a performance issue
         /*
          * Note also that this creates a subtle difference in behavior for concurrent modification
@@ -82,8 +76,16 @@
          */
 
         List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
-        allTypes.addAll(primaryNodeTypes.values());
-        allTypes.addAll(mixinNodeTypes.values());
+
+        // Need to return a version of the node type with the current context
+        for (JcrNodeType type : primaryNodeTypes) {
+            allTypes.add(type.with(context));
+        }
+
+        for (JcrNodeType type : mixinNodeTypes) {
+            allTypes.add(type.with(context));
+        }
+
         return new JcrNodeTypeIterator(allTypes);
     }
 
@@ -93,16 +95,15 @@
      * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
      */
     public NodeTypeIterator getMixinNodeTypes() {
-        return new JcrNodeTypeIterator(mixinNodeTypes.values());
-    }
+        Collection<JcrNodeType> rawTypes = repositoryTypeManager.getMixinNodeTypes();
+        List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
 
-    JcrNodeType getNodeType( Name nodeTypeName ) {
+        // Need to return a version of the node type with the current context
+        for (JcrNodeType type : rawTypes) {
+            types.add(type.with(context));
+        }
 
-        JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
-        if (nodeType == null) {
-            nodeType = mixinNodeTypes.get(nodeTypeName);
-        }
-        return nodeType;
+        return new JcrNodeTypeIterator(types);
     }
 
     /**
@@ -111,19 +112,47 @@
      * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
      */
     public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
-        Name ntName = session.getExecutionContext().getValueFactories().getNameFactory().create(nodeTypeName);
-        NodeType type = getNodeType(ntName);
-        if (type != null) return type;
+        Name ntName = context.getValueFactories().getNameFactory().create(nodeTypeName);
+        JcrNodeType type = repositoryTypeManager.getNodeType(ntName);
+        if (type != null) {
+            type = type.with(context);
+            return type;
+        }
         throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
     }
 
     /**
+     * Returns the node type with the given name (if one exists)
+     * 
+     * @param nodeTypeName the name of the node type to be returned
+     * @return the node type with the given name (if one exists)
+     * @see RepositoryNodeTypeManager#getNodeType(Name)
+     */
+    JcrNodeType getNodeType( Name nodeTypeName ) {
+        JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
+
+        if (nodeType != null) {
+            nodeType = nodeType.with(context);
+        }
+
+        return nodeType;
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
      */
     public NodeTypeIterator getPrimaryNodeTypes() {
-        return new JcrNodeTypeIterator(primaryNodeTypes.values());
+        Collection<JcrNodeType> rawTypes = repositoryTypeManager.getPrimaryNodeTypes();
+        List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
+
+        // Need to return a version of the node type with the current context
+        for (JcrNodeType type : rawTypes) {
+            types.add(type.with(context));
+        }
+
+        return new JcrNodeTypeIterator(types);
     }
 
     /**
@@ -134,7 +163,7 @@
      * @throws NoSuchNodeTypeException
      */
     JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException {
-        for (NodeDefinition definition : getNodeType(DnaLexicon.ROOT).getChildNodeDefinitions()) {
+        for (NodeDefinition definition : repositoryTypeManager.getNodeType(DnaLexicon.ROOT).getChildNodeDefinitions()) {
             if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition;
         }
         assert false; // should not get here
@@ -150,7 +179,7 @@
     JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
         if (definitionId == null) return null;
         Name nodeTypeName = definitionId.getNodeTypeName();
-        JcrNodeType nodeType = getNodeType(nodeTypeName);
+        JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
         return nodeType.getChildNodeDefinition(definitionId.getChildDefinitionName());
     }
 
@@ -165,7 +194,7 @@
                                                  boolean prefersMultiValued ) {
         if (definitionId == null) return null;
         Name nodeTypeName = definitionId.getNodeTypeName();
-        JcrNodeType nodeType = getNodeType(nodeTypeName);
+        JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
         return nodeType.getPropertyDefinition(definitionId.getPropertyDefinitionName(), prefersMultiValued);
     }
 

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.UUID;
 import java.util.regex.Pattern;
 import javax.jcr.Node;
 import javax.jcr.PropertyType;
@@ -41,6 +42,7 @@
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.property.basic.JodaDateTime;
+
 /**
  * DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
  * through its constructor.
@@ -55,7 +57,7 @@
     private PropertyDefinitionId id;
     private ConstraintChecker checker = null;
 
-    JcrPropertyDefinition( JcrSession session,
+    JcrPropertyDefinition( ExecutionContext context,
                            JcrNodeType declaringNodeType,
                            Name name,
                            int onParentVersion,
@@ -66,7 +68,7 @@
                            int requiredType,
                            String[] valueConstraints,
                            boolean multiple ) {
-        super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+        super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
         this.defaultValues = defaultValues;
         this.requiredType = requiredType;
         this.valueConstraints = valueConstraints;
@@ -130,7 +132,7 @@
      *         <code>declaringNodeType</code>.
      */
     JcrPropertyDefinition with( JcrNodeType declaringNodeType ) {
-        return new JcrPropertyDefinition(this.session, declaringNodeType, this.name, this.getOnParentVersion(),
+        return new JcrPropertyDefinition(this.context, declaringNodeType, this.name, this.getOnParentVersion(),
                                          this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
                                          this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
     }
@@ -152,7 +154,7 @@
         ConstraintChecker checker = this.checker;
 
         if (checker == null || checker.getType() != type) {
-            checker = createChecker(session, type, valueConstraints);
+            checker = createChecker(context, type, valueConstraints);
             this.checker = checker;
         }
 
@@ -167,21 +169,18 @@
     /**
      * Returns a {@link ConstraintChecker} that will interpret the constraints described by <code>valueConstraints</code> using
      * the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type indicated by <code>type</code> (where
-     * <code>type</code> is a value from {@link PropertyType}) for the given <code>session</code>. The session is required to
-     * handle the UUID lookups for reference constraints and provides the {@link ExecutionContext} that is used to provide
-     * namespace mappings and value factories for the other constraint checkers.
+     * <code>type</code> is a value from {@link PropertyType}) for the given <code>context</code>. The {@link ExecutionContext} is
+     * used to provide namespace mappings and value factories for the other constraint checkers.
      * 
-     * @param session the current session
+     * @param context the execution context
      * @param type the type of constraint checker that should be created (based on values from {@link PropertyType}).
      *        Type-specific semantics are defined in section 6.7.16 of the JCR 1.0 specification.
      * @param valueConstraints the constraints for the node as provided by {@link PropertyDefinition#getValueConstraints()}.
      * @return a constraint checker that matches the given parameters
      */
-    private ConstraintChecker createChecker( JcrSession session,
-                                                   int type,
-                                                   String[] valueConstraints ) {
-        ExecutionContext context = session.getExecutionContext();
-
+    private ConstraintChecker createChecker( ExecutionContext context,
+                                             int type,
+                                             String[] valueConstraints ) {
         switch (type) {
             case PropertyType.BINARY:
                 return new BinaryConstraintChecker(valueConstraints, context);
@@ -196,7 +195,7 @@
             case PropertyType.PATH:
                 return new PathConstraintChecker(valueConstraints, context);
             case PropertyType.REFERENCE:
-                return new ReferenceConstraintChecker(valueConstraints, session);
+                return new ReferenceConstraintChecker(valueConstraints, context);
             case PropertyType.STRING:
                 return new StringConstraintChecker(valueConstraints, context);
             default:
@@ -333,7 +332,8 @@
         @Override
         public boolean matches( Value value ) {
             Binary binary = valueFactories.getBinaryFactory().create(((JcrValue)value).value());
-            return super.matches(new JcrValue(valueFactories, PropertyType.LONG, binary.getSize()));
+            Value sizeValue = ((JcrValue)value).sessionCache().session().getValueFactory().createValue(binary.getSize());
+            return super.matches(sizeValue);
         }
     }
 
@@ -408,15 +408,15 @@
 
     @Immutable
     private static class ReferenceConstraintChecker implements ConstraintChecker {
-        private final JcrSession session;
+        // private final ExecutionContext context;
 
         private final Name[] constraints;
 
         protected ReferenceConstraintChecker( String[] valueConstraints,
-                                              JcrSession session ) {
-            this.session = session;
+                                              ExecutionContext context ) {
+            // this.context = context;
 
-            NameFactory factory = session.getExecutionContext().getValueFactories().getNameFactory();
+            NameFactory factory = context.getValueFactories().getNameFactory();
 
             constraints = new Name[valueConstraints.length];
 
@@ -430,18 +430,21 @@
         }
 
         public boolean matches( Value value ) {
-            assert value != null;
+            assert value instanceof JcrValue;
 
+            JcrValue jcrValue = (JcrValue)value;
+            SessionCache cache = jcrValue.sessionCache();
+
             Node node = null;
             try {
-                node = session.getNodeByUUID(((JcrValue)value).value().toString());
+                node = cache.findJcrNode((UUID)jcrValue.value());
             } catch (RepositoryException re) {
                 return false;
             }
 
             for (int i = 0; i < constraints.length; i++) {
                 try {
-                    if (node.isNodeType(constraints[i].getString(session.getExecutionContext().getNamespaceRegistry()))) {
+                    if (node.isNodeType(constraints[i].getString(cache.session().namespaces()))) {
                         return true;
                     }
                 } catch (RepositoryException re) {
@@ -474,9 +477,11 @@
         }
 
         public boolean matches( Value value ) {
-            assert value != null;
+            assert value instanceof JcrValue;
 
-            Name name = valueFactory.create(((JcrValue)value).value());
+            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());
 
             for (int i = 0; i < constraints.length; i++) {
                 if (constraints[i].equals(name)) {
@@ -561,5 +566,4 @@
             return false;
         }
     }
-
 }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -75,6 +75,7 @@
     private final Map<String, String> descriptors;
     private final ExecutionContext executionContext;
     private final RepositoryConnectionFactory connectionFactory;
+    private final RepositoryNodeTypeManager repositoryTypeManager;
 
     /**
      * Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -145,9 +146,22 @@
         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);
     }
 
     /**
+     * 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.
      * 
      * @return the name of the RepositorySource

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -146,6 +146,11 @@
         assert this.graph != null;
     }
 
+    // Added to facilitate mock testing of items without necessarily requiring an entire repository structure to be built
+    final SessionCache cache() {
+        return this.cache;
+    }
+
     ExecutionContext getExecutionContext() {
         return this.executionContext;
     }
@@ -439,39 +444,41 @@
      */
     public ValueFactory getValueFactory() {
         final ValueFactories valueFactories = executionContext.getValueFactories();
+        final SessionCache sessionCache = this.cache;
+
         return new ValueFactory() {
 
             public Value createValue( String value,
                                       int propertyType ) {
-                return new JcrValue(valueFactories, propertyType, value);
+                return new JcrValue(valueFactories, sessionCache, propertyType, value);
             }
 
             public Value createValue( Node value ) throws RepositoryException {
-                return new JcrValue(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
+                return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
             }
 
             public Value createValue( InputStream value ) {
-                return new JcrValue(valueFactories, PropertyType.BINARY, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, value);
             }
 
             public Value createValue( Calendar value ) {
-                return new JcrValue(valueFactories, PropertyType.DATE, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.DATE, value);
             }
 
             public Value createValue( boolean value ) {
-                return new JcrValue(valueFactories, PropertyType.BOOLEAN, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.BOOLEAN, value);
             }
 
             public Value createValue( double value ) {
-                return new JcrValue(valueFactories, PropertyType.DOUBLE, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.DOUBLE, value);
             }
 
             public Value createValue( long value ) {
-                return new JcrValue(valueFactories, PropertyType.LONG, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.LONG, value);
             }
 
             public Value createValue( String value ) {
-                return new JcrValue(valueFactories, PropertyType.STRING, value);
+                return new JcrValue(valueFactories, sessionCache, PropertyType.STRING, value);
             }
         };
     }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -43,19 +43,27 @@
 @NotThreadSafe
 final class JcrValue implements Value {
 
+    private final SessionCache sessionCache;
     private final ValueFactories valueFactories;
     private final int type;
     private final Object value;
 
     JcrValue( ValueFactories valueFactories,
+              SessionCache sessionCache,
               int type,
               Object value ) {
         assert valueFactories != null;
         assert type == PropertyType.BINARY || type == PropertyType.BOOLEAN || type == PropertyType.DATE
                || type == PropertyType.DOUBLE || type == PropertyType.LONG || type == PropertyType.NAME
                || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING;
+        
+        // Leaving this assertion out for now so that values can be created in node type sources, which are created outside
+        // the context of any particular session.
+        // assert sessionCache != null;
         assert value != null;
+
         this.valueFactories = valueFactories;
+        this.sessionCache = sessionCache;
         this.type = type;
         this.value = value;
     }
@@ -81,6 +89,14 @@
     }
 
     /**
+     * Returns the session cache for the session that created this value.
+     * @return the session cache for the session that created this value.
+     */
+    final SessionCache sessionCache() {
+        return sessionCache;
+    }
+    
+    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Value#getBoolean()
@@ -195,6 +211,11 @@
         return type;
     }
 
+    private JcrValue withTypeAndValue( int type,
+                                       Object value ) {
+        return new JcrValue(this.valueFactories, this.sessionCache, type, value);
+    }
+
     /**
      * Returns a copy of the current {@link JcrValue} cast to the JCR type specified by the <code>type</code> argument. If the
      * value cannot be converted base don the JCR type conversion rules, a {@link ValueFormatException} will be thrown.
@@ -207,14 +228,14 @@
     JcrValue asType( int type ) throws ValueFormatException {
 
         if (type == this.type) {
-            return new JcrValue(this.valueFactories, this.type, this.value);
+            return this.withTypeAndValue(this.type, this.value);
         }
 
         switch (type) {
             case PropertyType.BOOLEAN:
                 try {
                     if (this.type == PropertyType.STRING || this.type == PropertyType.BINARY) {
-                        return new JcrValue(this.valueFactories, type, valueFactories.getBooleanFactory().create(value));
+                        return this.withTypeAndValue(type, valueFactories.getBooleanFactory().create(value));
                     }
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
@@ -225,7 +246,7 @@
                 try {
                     if (this.type == PropertyType.DOUBLE || this.type == PropertyType.LONG || this.type == PropertyType.STRING
                         || this.type == PropertyType.BINARY) {
-                        return new JcrValue(this.valueFactories, type, valueFactories.getDateFactory().create(value));
+                        return this.withTypeAndValue(type, valueFactories.getDateFactory().create(value));
                     }
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
@@ -236,12 +257,12 @@
             case PropertyType.NAME:
                 try {
                     if (this.type == PropertyType.STRING) {
-                        return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(value));
+                        return this.withTypeAndValue(type, valueFactories.getNameFactory().create(value));
                     }
 
                     String valueAsString = this.valueFactories.getStringFactory().create(value);
                     if (this.type == PropertyType.BINARY) {
-                        return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(valueAsString));
+                        return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
 
                     }
 
@@ -250,9 +271,7 @@
 
                         Segment[] segments = path.getSegmentsArray();
                         if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
-                            return new JcrValue(this.valueFactories, type,
-                                                this.valueFactories.getNameFactory().create(valueAsString));
-
+                            return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
                         }
                     }
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
@@ -264,7 +283,8 @@
             case PropertyType.PATH:
                 try {
                     if (this.type == PropertyType.STRING) {
-                        return new JcrValue(this.valueFactories, type, this.valueFactories.getPathFactory().create(value));
+                        return this.withTypeAndValue(type, valueFactories.getPathFactory().create(value));
+
                     }
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
@@ -282,18 +302,21 @@
                 // Anything can be converted to these types
             case PropertyType.BINARY:
                 try {
-                    return new JcrValue(this.valueFactories, type, valueFactories.getBinaryFactory().create(value));
+                    return this.withTypeAndValue(type, valueFactories.getBinaryFactory().create(value));
+
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
             case PropertyType.STRING:
                 try {
-                    return new JcrValue(this.valueFactories, type, valueFactories.getStringFactory().create(value));
+                    return this.withTypeAndValue(type, valueFactories.getStringFactory().create(value));
+
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
             case PropertyType.UNDEFINED:
-                return new JcrValue(this.valueFactories, this.type, this.value);
+                return this.withTypeAndValue(this.type, this.value);
+
             default:
                 assert false : "Unexpected JCR property type " + type;
                 // This should still throw an exception even if assertions are turned off

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -148,10 +148,7 @@
         this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
 
         // This must be initialized after the session
-        JcrNodeTypeSource source = null;
-        source = new JcrBuiltinNodeTypeSource(this.session);
-        source = new DnaBuiltinNodeTypeSource(this.session, source);
-        this.nodeTypeManager = new JcrNodeTypeManager(this.session, source);
+        this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repository.getRepositoryTypeManager());
     }
 
     final String getSourceName() {

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -0,0 +1,81 @@
+/*
+ * 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.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
+ * repository manager maintains a list of all node types and the ability to retrieve node types by {@link Name}. </p> The JCR 1.0
+ * and 2.0 specifications both require that node type information be shared across all sessions within a repository and that the
+ * {@link javax.jcr.nodetype.NodeTypeManager} perform operations based on the string versions of {@link Name}s based on the permanent
+ * (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by maintaining a single master
+ * repository of all node type information (the {@link RepositoryNodeTypeManager}) and per-session wrappers (
+ * {@link JcrNodeTypeManager}) for this master repository that perform {@link String} to {@link Name} translation based on the
+ * {@link javax.jcr.Session}'s transient mappings and then delegating node type lookups to the repository manager.
+ */
+ at Immutable
+class RepositoryNodeTypeManager {
+
+    private final Map<Name, JcrNodeType> primaryNodeTypes;
+    private final Map<Name, JcrNodeType> mixinNodeTypes;
+
+    RepositoryNodeTypeManager( ExecutionContext context,
+                               JcrNodeTypeSource source ) {
+        Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
+        Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+
+        primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
+        for (JcrNodeType nodeType : primary) {
+            primaryNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+        }
+
+        mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
+        for (JcrNodeType nodeType : mixins) {
+            mixinNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+        }
+    }
+
+    public Collection<JcrNodeType> getMixinNodeTypes() {
+        return mixinNodeTypes.values();
+    }
+
+    public Collection<JcrNodeType> getPrimaryNodeTypes() {
+        return primaryNodeTypes.values();
+    }
+
+    JcrNodeType getNodeType( Name nodeTypeName ) {
+
+        JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
+        if (nodeType == null) {
+            nodeType = mixinNodeTypes.get(nodeTypeName);
+        }
+        return nodeType;
+    }
+}


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

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -1441,7 +1441,7 @@
      * @param primaryTypeOfChild the name of the child's primary type
      * @return the node definition for this child, as best as can be determined, or null if the node definition could not be
      *         determined
-     * @throws RepositoryException if the parent's pimary node type cannot be found in the {@link NodeTypeManager}
+     * @throws RepositoryException if the parent's primary node type cannot be found in the {@link NodeTypeManager}
      */
     protected JcrNodeDefinition findNodeDefinitionForChild( NodeInfo parentInfo,
                                                             Name childName,

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -151,7 +151,7 @@
     }
 
     protected Value stringValueFor( Object value ) {
-        return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
+        return new JcrValue(context.getValueFactories(), cache, PropertyType.STRING, value);
     }
 
     protected void addChild( Name childName,
@@ -463,37 +463,7 @@
         assertThat((JcrSession)node.getSession(), is(session));
     }
 
-    @Test
-    public void shouldProvideUuidIfReferenceable() throws Exception {
-        // Create the property ...
-        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
-        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
-        stub(cache.findJcrProperty(propertyId)).toReturn(property);
-        stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:referenceable")});
-        // Call the method ...
-        assertThat(node.getUUID(), is(uuid.toString()));
-    }
-
     @Test( expected = UnsupportedRepositoryOperationException.class )
-    public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
-        // Create the property ...
-        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
-        AbstractJcrProperty property = mock(AbstractJcrProperty.class);
-        stub(cache.findJcrProperty(propertyId)).toReturn(property);
-        stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:notReferenceable")});
-        // Call the method ...
-        node.getUUID();
-    }
-
-    @Test( expected = UnsupportedRepositoryOperationException.class )
-    public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
-        PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
-        stub(cache.findJcrProperty(propertyId)).toReturn(null);
-        // Call the method ...
-        node.getUUID();
-    }
-
-    @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotAllowGetVersionHistory() throws Exception {
         node.getVersionHistory();
     }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -30,8 +30,6 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.UUID;
-import javax.jcr.Node;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
@@ -52,21 +50,22 @@
 import org.mockito.MockitoAnnotations.Mock;
 
 /**
- * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)},
- * which provides the wrapper around the factory that the rest of the API is expected to utilize.
- *
+ * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)}, which
+ * provides the wrapper around the factory that the rest of the API is expected to utilize.
  */
 public class JcrPropertyDefinitionTest {
 
-    private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
-    private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] { "[,+1945-08-01T01:30:00.000Z]", "[+1975-08-01T01:30:00.000Z,)"};
-    private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] { "[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]" };
-    private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
-    private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] { "jcr:system", "dnatest:constrainedType" };
-    //private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c" };
-    private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c" };
-    private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] { "dna:root" };
-    private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] { "foo", "bar*", ".*baz" };
+    private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+    private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] {"[,+1945-08-01T01:30:00.000Z]",
+        "[+1975-08-01T01:30:00.000Z,)"};
+    private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] {"[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]"};
+    private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+    private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] {"jcr:system", "dnatest:constrainedType"};
+    // private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b",
+    // "/a/b/c" };
+    private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c"};
+    private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] {"dna:root"};
+    private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] {"foo", "bar*", ".*baz"};
 
     private String workspaceName;
     private ExecutionContext context;
@@ -75,6 +74,7 @@
     private JcrSession session;
     private Graph graph;
     private RepositoryConnectionFactory connectionFactory;
+    private RepositoryNodeTypeManager repoTypeManager;
     private NodeTypeManager nodeTypeManager;
     private Map<String, Object> sessionAttributes;
     @Mock
@@ -82,6 +82,8 @@
 
     @Before
     public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
         workspaceName = "workspace1";
         final String repositorySourceName = "repository";
 
@@ -97,7 +99,7 @@
 
         // 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.create("/a").and().create("/a/b").and().create("/a/b/c");
@@ -118,7 +120,12 @@
         };
 
         // Stub out the repository, since we only need a few methods ...
-        MockitoAnnotations.initMocks(this);
+        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);
 
@@ -132,39 +139,37 @@
         // Create the session and log in ...
         session = (JcrSession)workspace.getSession();
 
-        nodeTypeManager = new JcrNodeTypeManager(this.session,
-                               new TestNodeTypeSource(this.session,
-                               new DnaBuiltinNodeTypeSource(this.session,
-                                                            new JcrBuiltinNodeTypeSource(this.session))));
-        stub(workspace.getNodeTypeManager()).toReturn(nodeTypeManager);
-        
+        nodeTypeManager = workspace.getNodeTypeManager();
     }
 
     @After
     public void after() throws Exception {
-        if (session.isLive()) {
+        if (session != null && session.isLive()) {
             session.logout();
         }
     }
 
-    private JcrPropertyDefinition propertyDefinitionFor(NodeType nodeType, Name propertyName) {
+    private JcrPropertyDefinition propertyDefinitionFor( NodeType nodeType,
+                                                         Name propertyName ) {
         PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
         String property = propertyName.getString(context.getNamespaceRegistry());
-        
+
         for (int i = 0; i < propertyDefs.length; i++) {
             if (propertyDefs[i].getName().equals(property)) {
-                return (JcrPropertyDefinition) propertyDefs[i];
+                return (JcrPropertyDefinition)propertyDefs[i];
             }
         }
-        throw new IllegalStateException("Could not find property definition name " + property + " for type " 
-                                        + nodeType.getName() + ".  Test setup is invalid.");
+        throw new IllegalStateException("Could not find property definition name " + property + " for type " + nodeType.getName()
+                                        + ".  Test setup is invalid.");
     }
 
-    private void checkConstraints(NodeType nodeType, Name propertyName, String[] expectedConstraints) {
+    private void checkConstraints( NodeType nodeType,
+                                   Name propertyName,
+                                   String[] expectedConstraints ) {
         PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
         String property = propertyName.getString(context.getNamespaceRegistry());
         String[] constraints = null;
-        
+
         for (int i = 0; i < propertyDefs.length; i++) {
             if (propertyDefs[i].getName().equals(property)) {
                 constraints = propertyDefs[i].getValueConstraints();
@@ -176,10 +181,10 @@
             throw new IllegalStateException("Unexpected constraints for property: " + property);
         }
     }
-    
+
     private NodeType validateTypeDefinition() throws Exception {
         NamespaceRegistry nsr = context.getNamespaceRegistry();
-        
+
         NodeType constrainedType = nodeTypeManager.getNodeType(TestLexicon.CONSTRAINED_TYPE.getString(nsr));
         assertThat(constrainedType, notNullValue());
         assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY), notNullValue());
@@ -190,7 +195,7 @@
         assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH), notNullValue());
         assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE), notNullValue());
         assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING), notNullValue());
-        
+
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_BINARY, EXPECTED_BINARY_CONSTRAINTS);
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DATE, EXPECTED_DATE_CONSTRAINTS);
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DOUBLE, EXPECTED_DOUBLE_CONSTRAINTS);
@@ -199,24 +204,26 @@
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_PATH, EXPECTED_PATH_CONSTRAINTS);
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_REFERENCE, EXPECTED_REFERENCE_CONSTRAINTS);
         checkConstraints(constrainedType, TestLexicon.CONSTRAINED_STRING, EXPECTED_STRING_CONSTRAINTS);
-        
+
         return constrainedType;
     }
-    
-    private Value valueFor(Object value, int jcrType) {
-        return new JcrValue(context.getValueFactories(), jcrType, value);
+
+    private Value valueFor( Object value,
+                            int jcrType ) {
+        return new JcrValue(context.getValueFactories(), session.cache(), jcrType, value);
     }
-    
-    private String stringOfLength(int length) {
+
+    private String stringOfLength( int length ) {
         StringBuffer buff = new StringBuffer(length);
         for (int i = 0; i < length; i++) {
             buff.append(i % 10);
         }
-        
+
         return buff.toString();
     }
 
-    private boolean satisfiesConstraints(JcrPropertyDefinition property, Value[] values) {
+    private boolean satisfiesConstraints( JcrPropertyDefinition property,
+                                          Value[] values ) {
         for (int i = 0; i < values.length; i++) {
             if (!property.satisfiesConstraints(values[i])) {
                 return false;
@@ -224,8 +231,8 @@
         }
         return true;
     }
-    
-     @Test( expected = AssertionError.class )
+
+    @Test( expected = AssertionError.class )
     public void shouldNotAllowNullValue() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
@@ -585,45 +592,41 @@
     public void shouldAllowValidReferenceValue() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-        
-        UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
 
-        assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(true));
+        Value value = session.getValueFactory().createValue(session.getRootNode());
+
+        assertThat(prop.satisfiesConstraints(value), is(true));
     }
 
     @Test
     public void shouldAllowValidReferenceValues() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-        
-        UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
 
-        assertThat(satisfiesConstraints(prop, new Value[] { }), is(true));
-        assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(true));
+        Value value = session.getValueFactory().createValue(session.getRootNode());
+
+        assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+        assertThat(satisfiesConstraints(prop, new Value[] {value}), is(true));
     }
-    
 
     @Test
     public void shouldNotAllowInvalidReferenceValue() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
 
-        Node aNode = session.getRootNode().getNode("a");
-        UUID uuid = ((JcrNode) aNode).nodeUuid;
+        Value value = session.getValueFactory().createValue(session.getRootNode().getNode("a"));
 
-        
-        assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(false));
+        assertThat(prop.satisfiesConstraints(value), is(false));
     }
 
     @Test
     public void shouldNotAllowInvalidReferenceValues() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-        
-        Node aNode = session.getRootNode().getNode("a");
-        UUID uuid = ((JcrNode) aNode).nodeUuid;
-        
-        assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(false));
+
+        Value value = session.getValueFactory().createValue(session.getRootNode().getNode("a"));
+
+        assertThat(satisfiesConstraints(prop, new Value[] {value}), is(false));
     }
 
 }

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -50,7 +50,9 @@
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.ValueFactory;
+import javax.jcr.nodetype.NodeType;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 import org.jboss.dna.graph.ExecutionContext;
@@ -81,6 +83,7 @@
     private JcrSession session;
     private Graph graph;
     private RepositoryConnectionFactory connectionFactory;
+    private RepositoryNodeTypeManager repoTypeManager;
     private Map<String, Object> sessionAttributes;
     @Mock
     private JcrRepository repository;
@@ -103,6 +106,7 @@
         graph.create("/a").and().create("/a/b").and().create("/a/b/c");
         graph.set("booleanProperty").on("/a/b").to(true);
         graph.set("stringProperty").on("/a/b/c").to("value");
+        graph.set("jcr:mixinTypes").on("/a").to("mix:lockable");
         graph.set("jcr:mixinTypes").on("/a/b").to("mix:referenceable");
         graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
 
@@ -122,11 +126,18 @@
             }
         };
 
+        // Set up the repo type manager
+        JcrNodeTypeSource nodeTypes = null;
+        nodeTypes = new JcrBuiltinNodeTypeSource(context, nodeTypes);
+        nodeTypes = new DnaBuiltinNodeTypeSource(context, nodeTypes);
+        repoTypeManager = new RepositoryNodeTypeManager(context, nodeTypes);
+        
         // Stub out the repository, since we only need a few methods ...
         MockitoAnnotations.initMocks(this);
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
         stub(repository.getConnectionFactory()).toReturn(connectionFactory);
-
+        stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
+        
         // Set up the session attributes ...
         sessionAttributes = new HashMap<String, Object>();
         sessionAttributes.put("attribute1", "value1");
@@ -404,7 +415,10 @@
     public void rootNodeShouldHaveProperType() throws Exception {
         Node rootNode = session.getRootNode();
 
-        assertTrue(rootNode.getPrimaryNodeType().equals(session.nodeTypeManager().getNodeType(DnaLexicon.ROOT)));
+        NodeType rootNodePrimaryType = rootNode.getPrimaryNodeType();
+        NodeType dnaRootType = session.nodeTypeManager().getNodeType(DnaLexicon.ROOT);
+        
+        assertThat(rootNodePrimaryType.getName(), is(dnaRootType.getName()));
 
     }
 
@@ -447,4 +461,34 @@
 
     }
 
+    /*
+     * Moved these three tests over from AbstractJcrNode as they require more extensive scaffolding that is already implemented in
+     * this test.
+     */
+
+    @Test
+    public void shouldProvideUuidIfReferenceable() throws Exception {
+        // The root node is referenceable in DNA
+        Node rootNode = session.getRootNode();
+
+        UUID uuid = ((AbstractJcrNode)rootNode).internalUuid();
+        assertThat(rootNode.getUUID(), is(uuid.toString()));
+    }
+
+    @Test( expected = UnsupportedRepositoryOperationException.class )
+    public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
+        // The b node was not set up to be referenceable in this test, but does have a mixin type
+        Node node = session.getRootNode().getNode("a");
+
+        node.getUUID();
+    }
+
+    @Test( expected = UnsupportedRepositoryOperationException.class )
+    public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
+        // The c node was not set up to be referenceable in this test and has no mixin types
+        Node node = session.getRootNode().getNode("a").getNode("b").getNode("c");
+
+        node.getUUID();
+    }
+
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java	2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -38,6 +38,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
 
 /**
  * @author jverhaeg
@@ -46,11 +48,15 @@
 
     private ValueFactories factories;
     private JcrValue value;
+    @Mock
+    private SessionCache cache;
 
     @Before
     public void before() {
+        MockitoAnnotations.initMocks(this);
+
         factories = new StandardValueFactories(Mockito.mock(NamespaceRegistry.class));
-        value = new JcrValue(factories, PropertyType.BOOLEAN, true);
+        value = new JcrValue(factories, cache, PropertyType.BOOLEAN, true);
     }
 
     @Test
@@ -102,30 +108,30 @@
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForDate() throws Exception {
-        new JcrValue(factories, PropertyType.DATE, new Date()).getBoolean();
+        new JcrValue(factories, cache, PropertyType.DATE, new Date()).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue(factories, PropertyType.DATE, date).getDate().getTime(), is(date));
+        assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getDate().getTime(), is(date));
     }
 
     @Test
     public void shouldProvideDoubleForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue(factories, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
+        assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
     }
 
     @Test
     public void shouldProvideLongForDate() throws Exception {
         Date date = new Date();
-        assertThat(new JcrValue(factories, PropertyType.DATE, date).getLong(), is(date.getTime()));
+        assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getLong(), is(date.getTime()));
     }
 
     @Test
     public void shouldProvideStreamForDate() throws Exception {
-        testProvidesStream(new JcrValue(factories, PropertyType.DATE, new Date()));
+        testProvidesStream(new JcrValue(factories, cache, PropertyType.DATE, new Date()));
     }
 
     @Test
@@ -134,158 +140,160 @@
         date.set(2008, 7, 18, 12, 0, 0);
         date.set(Calendar.MILLISECOND, 0);
         String expectedValue = "2008-08-18T12:00:00.000";
-        assertThat(new JcrValue(factories, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
+        assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
                    is(expectedValue));
-        assertThat(new JcrValue(factories, PropertyType.DATE, date.getTime()).getString().substring(0, expectedValue.length()),
+        assertThat(new JcrValue(factories, cache, PropertyType.DATE, date.getTime()).getString().substring(0,
+                                                                                                           expectedValue.length()),
                    is(expectedValue));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForDouble() throws Exception {
-        new JcrValue(factories, PropertyType.DOUBLE, 0.0).getBoolean();
+        new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForDouble() throws Exception {
         Calendar expectedValue = Calendar.getInstance();
         expectedValue.setTime(new Date(0L));
-        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
+        assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
     }
 
     @Test
     public void shouldProvideDoubleForDouble() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
+        assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
     }
 
     @Test
     public void shouldProvideLongForDouble() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
-        assertThat(new JcrValue(factories, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
+        assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
+        assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
     }
 
     @Test
     public void shouldProvideStreamForDouble() throws Exception {
-        testProvidesStream(new JcrValue(factories, PropertyType.DOUBLE, 1.0));
+        testProvidesStream(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0));
     }
 
     @Test
     public void shouldProvideStringForDouble() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
+        assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideBooleanForLong() throws Exception {
-        new JcrValue(factories, PropertyType.LONG, 0L).getBoolean();
+        new JcrValue(factories, cache, PropertyType.LONG, 0L).getBoolean();
     }
 
     @Test
     public void shouldProvideDateForLong() throws Exception {
         Calendar expectedValue = Calendar.getInstance();
         expectedValue.setTime(new Date(0L));
-        assertThat(new JcrValue(factories, PropertyType.LONG, 0L).getDate(), is(expectedValue));
+        assertThat(new JcrValue(factories, cache, PropertyType.LONG, 0L).getDate(), is(expectedValue));
     }
 
     @Test
     public void shouldProvideDoubleForLong() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getDouble(), is(1.0));
+        assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getDouble(), is(1.0));
     }
 
     @Test
     public void shouldProvideLongForLong() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getLong(), is(1L));
+        assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getLong(), is(1L));
     }
 
     @Test
     public void shouldProvideStreamForLong() throws Exception {
-        testProvidesStream(new JcrValue(factories, PropertyType.LONG, 1L));
+        testProvidesStream(new JcrValue(factories, cache, PropertyType.LONG, 1L));
     }
 
     @Test
     public void shouldProvideStringForLong() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getString(), is("1"));
+        assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getString(), is("1"));
     }
 
     @Test
     public void shouldProvideBooleanForString() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "true").getBoolean(), is(true));
-        assertThat(new JcrValue(factories, PropertyType.STRING, "yes").getBoolean(), is(false));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "true").getBoolean(), is(true));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "yes").getBoolean(), is(false));
     }
 
     @Test
     public void shouldProvideDateForString() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "2008").getDate(), notNullValue());
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "2008").getDate(), notNullValue());
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDateForInvalidString() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, "true").getDate();
+        new JcrValue(factories, cache, PropertyType.STRING, "true").getDate();
     }
 
     @Test
     public void shouldProvideDoubleForString() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "1").getDouble(), is(1.0));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "1").getDouble(), is(1.0));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDoubleForInvalidString() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, "true").getDouble();
+        new JcrValue(factories, cache, PropertyType.STRING, "true").getDouble();
     }
 
     @Test
     public void shouldProvideLongForString() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "1").getLong(), is(1L));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "1").getLong(), is(1L));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideLongForInvalidString() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, "true").getLong();
+        new JcrValue(factories, cache, PropertyType.STRING, "true").getLong();
     }
 
     @Test
     public void shouldProvideStreamForString() throws Exception {
-        testProvidesStream(new JcrValue(factories, PropertyType.STRING, "true"));
+        testProvidesStream(new JcrValue(factories, cache, PropertyType.STRING, "true"));
     }
 
     @Test
     public void shouldProvideStringForString() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "true").getString(), is("true"));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "true").getString(), is("true"));
     }
 
     @Test
     public void shouldProvideBooleanForUuid() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDateForUuid() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDate();
+        new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getDate();
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideDoubleForUuid() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDouble();
+        new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getDouble();
     }
 
     @Test( expected = ValueFormatException.class )
     public void shouldNotProvideLongForUuid() throws Exception {
-        new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getLong();
+        new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getLong();
     }
 
     @Test
     public void shouldProvideStreamForUuid() throws Exception {
-        testProvidesStream(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()));
+        testProvidesStream(new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()));
     }
 
     @Test
     public void shouldProvideStringForUuid() throws Exception {
         String expectedValue = "40d373f7-75ad-4d84-900e-c72ebd98abb9";
-        assertThat(new JcrValue(factories, PropertyType.STRING, UUID.fromString(expectedValue)).getString(), is(expectedValue));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, UUID.fromString(expectedValue)).getString(),
+                   is(expectedValue));
     }
 
     @Test
     public void shouldProvideLength() throws Exception {
-        assertThat(new JcrValue(factories, PropertyType.STRING, "test").getLength(), is(4L));
-        assertThat(new JcrValue(factories, PropertyType.BINARY, "test").getLength(), is(4L));
+        assertThat(new JcrValue(factories, cache, PropertyType.STRING, "test").getLength(), is(4L));
+        assertThat(new JcrValue(factories, cache, PropertyType.BINARY, "test").getLength(), is(4L));
     }
 
     private void testProvidesStream( JcrValue value ) throws Exception {

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-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -71,6 +71,7 @@
 public class SessionCacheTest {
 
     private ExecutionContext context;
+    private RepositoryNodeTypeManager repoTypes;
     private JcrNodeTypeManager nodeTypes;
     private Stopwatch sw;
     private Graph store;
@@ -95,10 +96,12 @@
         stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
 
         // Load up all the node types ...
-        JcrNodeTypeSource nodeTypeSource = new JcrBuiltinNodeTypeSource(session);
-        nodeTypeSource = new DnaBuiltinNodeTypeSource(session, nodeTypeSource);
-        nodeTypeSource = new VehixNodeTypeSource(session, nodeTypeSource);
-        nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
+        JcrNodeTypeSource nodeTypeSource = null;
+        nodeTypeSource = new JcrBuiltinNodeTypeSource(this.context, nodeTypeSource);
+        nodeTypeSource = new DnaBuiltinNodeTypeSource(this.context, nodeTypeSource);
+        nodeTypeSource = new VehixNodeTypeSource(context, nodeTypeSource);
+        repoTypes = new RepositoryNodeTypeManager(context, nodeTypeSource);
+        nodeTypes = new JcrNodeTypeManager(this.context, repoTypes);
         stub(session.nodeTypeManager()).toReturn(nodeTypes);
 
         // Now set up the graph and session cache ...
@@ -113,24 +116,24 @@
         private final List<JcrNodeType> primaryNodeTypes;
         private final List<JcrNodeType> mixinNodeTypes;
 
-        public VehixNodeTypeSource( JcrSession session,
+        public VehixNodeTypeSource( ExecutionContext context,
                                     JcrNodeTypeSource predecessor ) {
             super(predecessor);
             this.primaryNodeTypes = new ArrayList<JcrNodeType>();
             this.mixinNodeTypes = new ArrayList<JcrNodeType>();
-            Name carName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:car");
-            Name aircraftName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:aircraft");
+            Name carName = context.getValueFactories().getNameFactory().create("vehix:car");
+            Name aircraftName = context.getValueFactories().getNameFactory().create("vehix:aircraft");
             JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
 
             // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
-            JcrNodeType car = new JcrNodeType(session, carName, Arrays.asList(new NodeType[] {unstructured}),
-                                              NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
-                                              ORDERABLE_CHILD_NODES);
+            JcrNodeType car = new JcrNodeType(context, (RepositoryNodeTypeManager)null, carName,
+                                              Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                              NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
             // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
-            JcrNodeType aircraft = new JcrNodeType(session, aircraftName, Arrays.asList(new NodeType[] {unstructured}),
-                                                   NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
-                                                   ORDERABLE_CHILD_NODES);
+            JcrNodeType aircraft = new JcrNodeType(context, (RepositoryNodeTypeManager)null, aircraftName,
+                                                   Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
+                                                   NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
             primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
         }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-20 18:40:19 UTC (rev 785)
@@ -29,6 +29,7 @@
 import java.util.List;
 import javax.jcr.PropertyType;
 import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.graph.ExecutionContext;
 
 /**
  * Node type source with additional node types that can be used for testing. This class defines its own namespace for its types.
@@ -40,7 +41,7 @@
     /** The list of mixin node types. */
     private final List<JcrNodeType> mixinNodeTypes;
 
-    TestNodeTypeSource( JcrSession session,
+    TestNodeTypeSource( ExecutionContext context,
                         JcrNodeTypeSource predecessor ) {
         super(predecessor);
 
@@ -50,32 +51,35 @@
         JcrNodeType base = findType(JcrNtLexicon.BASE);
 
         if (base == null) {
-            String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
-            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+            String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+            String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
+        
+        
         // Stubbing in child node and property definitions for now
         JcrNodeType constrainedType = new JcrNodeType(
-                                                      session,
+                                                      context,
+                                                      NO_NODE_TYPE_MANAGER,
                                                       TestLexicon.CONSTRAINED_TYPE,
                                                       Arrays.asList(new NodeType[] {base}),
                                                       NO_PRIMARY_ITEM_NAME,
                                                       NO_CHILD_NODES,
                                                       Arrays.asList(new JcrPropertyDefinition[] {
-                                                          new JcrPropertyDefinition(session, null,
+                                                          new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_BINARY,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.BINARY, new String[] {"[,5)",
                                                                                         "[10, 20)", "(30,40]", "[50,]"}, false),
-                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_DATE,
+                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_DATE,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.DATE, new String[] {
                                                                                         "[,+1945-08-01T01:30:00.000Z]",
                                                                                         "[+1975-08-01T01:30:00.000Z,)"}, false),
-                                                          new JcrPropertyDefinition(session, null,
+                                                          new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_DOUBLE,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
@@ -83,33 +87,33 @@
                                                                                     new String[] {"[,5.0)", "[10.1, 20.2)",
                                                                                         "(30.3,40.4]", "[50.5,]"}, false),
 
-                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_LONG,
+                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_LONG,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.LONG, new String[] {"[,5)",
                                                                                         "[10, 20)", "(30,40]", "[50,]"}, false),
 
-                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_NAME,
+                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_NAME,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.NAME, new String[] {
                                                                                         "jcr:system", "dnatest:constrainedType"},
                                                                                     false),
 
-                                                          new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_PATH,
+                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_PATH,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.PATH, new String[] {
                                                               // "/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"}, false),
                                                               "/jcr:system/*", "b", "/a/b/c"}, false),
-                                                          new JcrPropertyDefinition(session, null,
+                                                          new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_REFERENCE,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.REFERENCE,
                                                                                     new String[] {"dna:root",}, false),
 
-                                                          new JcrPropertyDefinition(session, null,
+                                                          new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_STRING,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,



From dna-commits at lists.jboss.org  Mon Mar 23 15:31:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 15:31:51 -0400
Subject: [dna-commits] DNA SVN: r786 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/property/basic and 1 other directory.
Message-ID: <E1LlpsF-0007ei-9V@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 15:31:51 -0400 (Mon, 23 Mar 2009)
New Revision: 786

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java
Log:
DNA-334 Should not allow creating a Name from a string with multiple non-escaped colons

Changed regular expression used in the NameValueFactory to create values, allowing only a single non-escaped colon in the name.  Added a test case to verify that this change indeed was required and has the correct behavior.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-20 18:40:19 UTC (rev 785)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-23 19:31:51 UTC (rev 786)
@@ -62,8 +62,8 @@
     protected static final String FULLY_QUALFIED_NAME_PATTERN_STRING = "\\{([^}]*)\\}(.*)";
     protected static final Pattern FULLY_QUALIFIED_NAME_PATTERN = Pattern.compile(FULLY_QUALFIED_NAME_PATTERN_STRING);
 
-    // Original pattern: (([^:/]*):)?(.*)
-    private static final String PREFIXED_NAME_PATTERN_STRING = "(([^:/]*):)?(.*)";
+    // Original pattern: (([^:/]*):)?([^:]*)
+    private static final String PREFIXED_NAME_PATTERN_STRING = "(([^:/]*):)?([^:]*)";
     private static final Pattern PREFIXED_NAME_PATTERN = Pattern.compile(PREFIXED_NAME_PATTERN_STRING);
 
     private static Name BLANK_NAME;

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java	2009-03-20 18:40:19 UTC (rev 785)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/NameValueFactoryTest.java	2009-03-23 19:31:51 UTC (rev 786)
@@ -34,6 +34,7 @@
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -126,6 +127,12 @@
         factory.create("a", "", decoder);
     }
 
+    @Test( expected = ValueFormatException.class )
+    public void shouldNotCreateNameFromStringWithMultipleNonEscapedColons() {
+        // This is a requirement of JCR, per the JCR TCK
+        factory.create("a:b:c");
+    }
+
     @Test
     public void shouldCreateIteratorOverValuesWhenSuppliedIteratorOfUnknownObjects() {
         List<String> values = new ArrayList<String>();



From dna-commits at lists.jboss.org  Mon Mar 23 16:41:31 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 16:41:31 -0400
Subject: [dna-commits] DNA SVN: r787 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/property/basic and 1 other directory.
Message-ID: <E1Llqxf-0004eZ-HQ@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 16:41:31 -0400 (Mon, 23 Mar 2009)
New Revision: 787

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicName.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicNameTest.java
Log:
DNA-333 BasicName.getString Doesn't Use Provided TextEncoder When the Name Has No Namespace

Applied the patch that corrects BasicName to use the encoder if suppliedto getString(), specifically when the name contains no namespace (e.g., "*").  The patch also changes the behavior of BasicName.getString(TextEncoder) to return the encoded local name ONLY when called. It used to return "{}" + localName.

Verified that all tests pass.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicName.java	2009-03-23 19:31:51 UTC (rev 786)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/BasicName.java	2009-03-23 20:41:31 UTC (rev 787)
@@ -91,7 +91,11 @@
             if (this.getLocalName().equals(Path.PARENT)) return Path.PARENT;
         }
         if (encoder == null) encoder = Path.DEFAULT_ENCODER;
-        return "{" + encoder.encode(this.namespaceUri) + "}" + encoder.encode(this.localName);
+        
+        if (namespaceUri.length() > 0) {
+            return "{" + encoder.encode(this.namespaceUri) + "}" + encoder.encode(this.localName);
+        }
+        return encoder.encode(this.localName);
     }
 
     /**
@@ -117,7 +121,7 @@
         if (prefix != null && prefix.length() != 0) {
             return encoder.encode(prefix) + ":" + encoder.encode(this.localName);
         }
-        return this.localName;
+        return encoder.encode(this.localName);
     }
 
     /**
@@ -148,7 +152,7 @@
             String delim = delimiterEncoder != null ? delimiterEncoder.encode(":") : ":";
             return encoder.encode(prefix) + delim + encoder.encode(this.localName);
         }
-        return this.localName;
+        return encoder.encode(this.localName);
     }
 
     /**

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicNameTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicNameTest.java	2009-03-23 19:31:51 UTC (rev 786)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/property/basic/BasicNameTest.java	2009-03-23 20:41:31 UTC (rev 787)
@@ -24,6 +24,7 @@
 package org.jboss.dna.graph.property.basic;
 
 import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.containsString;
 import org.jboss.dna.common.text.Jsr283Encoder;
@@ -180,7 +181,7 @@
         validLocalName = "some:name:with:colons";
         name = new BasicName(validNamespaceUri, validLocalName);
         result = name.getString(namespaceRegistry, encoder);
-        assertThat(result, is("some:name:with:colons"));
+        assertThat(result, is(encoder.encode(validLocalName)));
     }
 
     @Test
@@ -191,4 +192,21 @@
         assertThat(name.getString(null, encoder, delimiterEncoder), is("\\{" + encoder.encode(DnaLexicon.Namespace.URI)
                                                                        + "\\}some\uf03aname\uf03awith\uf03acolons"));
     }
+
+    @Test
+    public void shouldEncodeWhenNoNamespace() {
+        String nameForEncoding = "test name";
+        String encodedNameForEncoding = encoder.encode(nameForEncoding);
+        // Make sure that we're not testing a trivial encoding
+        assertThat(encodedNameForEncoding, not(nameForEncoding));
+        
+        name = new BasicName(null, nameForEncoding);
+
+        String result = name.getString(namespaceRegistry, encoder);
+        assertThat(result, is(encodedNameForEncoding));
+
+        result = name.getString(encoder); 
+        assertThat(result, is(encodedNameForEncoding));
+    }
+
 }



From dna-commits at lists.jboss.org  Mon Mar 23 22:16:35 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 22:16:35 -0400
Subject: [dna-commits] DNA SVN: r788 - in trunk/dna-jcr/src:
	main/java/org/jboss/dna/jcr/cache and 2 other directories.
Message-ID: <E1LlwBv-0002wk-5d@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 22:16:34 -0400 (Mon, 23 Mar 2009)
New Revision: 788

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.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/JcrNodeTypeManager.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.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/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/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
Log:
DNA-335 DNA's NodeTypeManager implementation does not allow multiple definitions with the same name

A large set of changes to handle multiple property definitions with the same name, and multiple child node definitions with the same name. Also corrects and centralizes the logic of selecting the "best" property definition (given the name, values, etc.) and "best" child node definition (given the names, primary type, number of children with the same name, etc.). The JcrNodeType now maintains a cache of various definitions keyed by their name, since searching by name is by far the most frequently used mechanism to access definitions.

This is slightly changed over the "dna-335-definitionsWithSameName" patch (see attached), including a correction to JcrNodeDefinition.allowsChildWithType(...), and a change to RepositoryNodeTypeManager.findPropertyDefinition(...) to not skip over property definitions that have an undefined property type. 

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -28,7 +28,6 @@
 import java.util.Collections;
 import java.util.List;
 import javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.property.Name;
 
@@ -40,7 +39,7 @@
     // Convenience constants to help improve readability
     protected static final Value[] NO_DEFAULT_VALUES = new Value[0];
     protected static final String[] NO_CONSTRAINTS = new String[0];
-    protected static final List<NodeType> NO_SUPERTYPES = Collections.<NodeType>emptyList();
+    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();
 

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -103,8 +103,7 @@
     public final PropertyDefinition getDefinition() throws RepositoryException {
         PropertyInfo info = propertyInfo();
         PropertyDefinitionId definitionId = info.getDefinitionId();
-        boolean multiValued = info.isMultiValued();
-        return cache.session().nodeTypeManager().getPropertyDefinition(definitionId, multiValued);
+        return cache.session().nodeTypeManager().getPropertyDefinition(definitionId);
     }
 
     /**

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -0,0 +1,179 @@
+/*
+ * 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.Collection;
+import javax.jcr.nodetype.NodeType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * A utility class that maintains a quick-lookup cache of all the child node definitions and property definitions for a series of
+ * node types. This class is used with {@link JcrNodeType} to represent these definitions for defined on the node type as well as
+ * those inherited from supertypes. However, it also can be used as a quick-lookup cache for a node's primary type and mixin
+ * types.
+ */
+ at Immutable
+final class DefinitionCache {
+
+    /**
+     * A local cache of all defined and inherited child node definitions, keyed by their name, that allow same-name-sibilings.
+     * This includes residual child node definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of
+     * this map is used frequently to find the most appropriate node definition for children, and so it is computed once at
+     * construction time (recall that all definitions, including the supertypes, are immutable).
+     * <p>
+     * Note that a node type may have multiple child node definitions with the same name,
+     * "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
+     * specification). The order of the node definitions stored for each name is such that this node's definitions are stored
+     * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
+     * </p>
+     */
+    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowSns = new LinkedListMultimap<Name, JcrNodeDefinition>();
+    /**
+     * A local cache of all defined and inherited child node definitions, keyed by their name, that do <i>not</i> allow
+     * same-name-sibilings. This includes residual child node definitions, which are keyed by the
+     * {@link JcrNodeType#RESIDUAL_NAME}. The content of this map is used requently to find the most appropriate node definition
+     * for children, and so it is computed once at construction time (recall that all definitions, including the supertypes, are
+     * immutable).
+     * <p>
+     * Note that a node type may have multiple child node definitions with the same name,
+     * "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
+     * specification). The order of the node definitions stored for each name is such that this node's definitions are stored
+     * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
+     * </p>
+     */
+    private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowNoSns = new LinkedListMultimap<Name, JcrNodeDefinition>();
+
+    /**
+     * A local cache of all defined and inherited property definitions, keyed by their name, that allow multiple values. This
+     * includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
+     * is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
+     * it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
+     * <p>
+     * Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
+     * otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
+     * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
+     * </p>
+     */
+    private final Multimap<Name, JcrPropertyDefinition> multiValuedPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+    /**
+     * A local cache of all defined and inherited property definitions, keyed by their name, that allow single values. This
+     * includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
+     * is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
+     * it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
+     * <p>
+     * Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
+     * otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
+     * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
+     * </p>
+     */
+    private final Multimap<Name, JcrPropertyDefinition> singleValuedPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+
+    private final Multimap<Name, JcrNodeDefinition> allChildNodeDefinitions = new LinkedListMultimap<Name, JcrNodeDefinition>();
+    private final Multimap<Name, JcrPropertyDefinition> allPropertyDefinitions = new LinkedListMultimap<Name, JcrPropertyDefinition>();
+
+    DefinitionCache( JcrNodeType nodeType ) {
+        addDefinitionsForTypeAndAllSupertypes(nodeType);
+    }
+
+    DefinitionCache( JcrNodeType primaryType,
+                     Iterable<JcrNodeType> mixinTypes ) {
+        addDefinitionsForTypeAndAllSupertypes(primaryType);
+        if (mixinTypes != null) {
+            for (JcrNodeType mixinType : mixinTypes) {
+                addDefinitionsForTypeAndAllSupertypes(mixinType);
+            }
+        }
+    }
+
+    private final void addDefinitionsForTypeAndAllSupertypes( JcrNodeType nodeType ) {
+        // And the definitions from the type and all supertypes ...
+        for (NodeType superSuperType : nodeType.getTypeAndSupertypes()) {
+            addDefinitions((JcrNodeType)superSuperType);
+        }
+    }
+
+    private final void addDefinitions( JcrNodeType nodeType ) {
+        for (JcrNodeDefinition definition : nodeType.childNodeDefinitions()) {
+            Name name = definition.getInternalName();
+            if (definition.allowsSameNameSiblings()) {
+                childNodeDefinitionsThatAllowSns.put(name, definition);
+            } else {
+                childNodeDefinitionsThatAllowNoSns.put(name, definition);
+            }
+            allChildNodeDefinitions.put(name, definition);
+        }
+        for (JcrPropertyDefinition definition : nodeType.propertyDefinitions()) {
+            Name name = definition.getInternalName();
+            if (definition.isMultiple()) {
+                multiValuedPropertyDefinitions.put(name, definition);
+            } else {
+                singleValuedPropertyDefinitions.put(name, definition);
+            }
+            allPropertyDefinitions.put(name, definition);
+        }
+    }
+
+    public Collection<JcrPropertyDefinition> allSingleValuePropertyDefinitions( Name propertyName ) {
+        return this.singleValuedPropertyDefinitions.get(propertyName);
+    }
+
+    public Collection<JcrPropertyDefinition> allMultiValuePropertyDefinitions( Name propertyName ) {
+        return this.multiValuedPropertyDefinitions.get(propertyName);
+    }
+
+    public Collection<JcrPropertyDefinition> allPropertyDefinitions( Name propertyName ) {
+        return this.allPropertyDefinitions.get(propertyName);
+    }
+
+    public Collection<JcrPropertyDefinition> allPropertyDefinitions() {
+        return this.allPropertyDefinitions.values();
+    }
+
+    public Collection<JcrNodeDefinition> allChildNodeDefinitionsWithNoSns( Name childName ) {
+        return this.childNodeDefinitionsThatAllowNoSns.get(childName);
+    }
+
+    public Collection<JcrNodeDefinition> allChildNodeDefinitionsWithSns( Name childName ) {
+        return this.childNodeDefinitionsThatAllowSns.get(childName);
+    }
+
+    public Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName ) {
+        return this.allChildNodeDefinitions.get(childName);
+    }
+
+    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
+                                                           boolean requireSns ) {
+        if (requireSns) {
+            return childNodeDefinitionsThatAllowSns.get(childName);
+        }
+        return allChildNodeDefinitions.get(childName);
+    }
+
+    public Collection<JcrNodeDefinition> allChildNodeDefinitions() {
+        return this.allChildNodeDefinitions.values();
+    }
+}

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -28,7 +28,6 @@
 import java.util.Collection;
 import java.util.List;
 import javax.jcr.PropertyType;
-import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.JcrMixLexicon;
@@ -72,7 +71,7 @@
                                                 context,
                                                 NO_NODE_TYPE_MANAGER,
                                                 DnaLexicon.NAMESPACE,
-                                                Arrays.asList(new NodeType[] {base}),
+                                                Arrays.asList(new JcrNodeType[] {base}),
                                                 DnaLexicon.URI,
                                                 NO_CHILD_NODES,
                                                 Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
@@ -93,7 +92,7 @@
                                                  context,
                                                  NO_NODE_TYPE_MANAGER,
                                                  DnaLexicon.NAMESPACES,
-                                                 Arrays.asList(new NodeType[] {base}),
+                                                 Arrays.asList(new JcrNodeType[] {base}),
                                                  NO_PRIMARY_ITEM_NAME,
                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                               context,
@@ -105,14 +104,14 @@
                                                                                                               true,
                                                                                                               false,
                                                                                                               DnaLexicon.NAMESPACE,
-                                                                                                              new NodeType[] {namespace})}),
+                                                                                                              new JcrNodeType[] {namespace})}),
                                                  NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType system = new JcrNodeType(
                                              context,
                                              NO_NODE_TYPE_MANAGER,
                                              DnaLexicon.SYSTEM,
-                                             Arrays.asList(new NodeType[] {base}),
+                                             Arrays.asList(new JcrNodeType[] {base}),
                                              NO_PRIMARY_ITEM_NAME,
                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                           context,
@@ -124,15 +123,15 @@
                                                                                                           true,
                                                                                                           false,
                                                                                                           DnaLexicon.NAMESPACES,
-                                                                                                          new NodeType[] {namespaces})}),
+                                                                                                          new JcrNodeType[] {namespaces})}),
                                              NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        JcrNodeType root = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base,
+        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,
-                                  true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
+                                  true, false, DnaLexicon.SYSTEM, new JcrNodeType[] {system}),
             new JcrNodeDefinition(context, null, ALL_NODES, OnParentVersionBehavior.VERSION.getJcrValue(), false, false, false,
-                                  true, JcrNtLexicon.UNSTRUCTURED, new NodeType[] {base}),
+                                  true, JcrNtLexicon.UNSTRUCTURED, new JcrNodeType[] {base}),
 
         }), Arrays.asList(new JcrPropertyDefinition[] {
             new JcrPropertyDefinition(context, null, ALL_NODES, OnParentVersionBehavior.COPY.getJcrValue(), false, false, false,

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -29,7 +29,6 @@
 import java.util.List;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
@@ -101,7 +100,7 @@
                                                           context,
                                                           NO_NODE_TYPE_MANAGER,
                                                           JcrNtLexicon.CHILD_NODE_DEFINITION,
-                                                          Arrays.asList(new NodeType[] {base}),
+                                                          Arrays.asList(new JcrNodeType[] {base}),
                                                           NO_PRIMARY_ITEM_NAME,
                                                           NO_CHILD_NODES,
                                                           Arrays.asList(new JcrPropertyDefinition[] {
@@ -173,7 +172,7 @@
                                                     context,
                                                     NO_NODE_TYPE_MANAGER,
                                                     JcrNtLexicon.HIERARCHY_NODE,
-                                                    Arrays.asList(new NodeType[] {base}),
+                                                    Arrays.asList(new JcrNodeType[] {base}),
                                                     NO_PRIMARY_ITEM_NAME,
                                                     NO_CHILD_NODES,
                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
@@ -194,7 +193,7 @@
                                            context,
                                            NO_NODE_TYPE_MANAGER,
                                            JcrNtLexicon.FILE,
-                                           Arrays.asList(new NodeType[] {hierarchyNode}),
+                                           Arrays.asList(new JcrNodeType[] {hierarchyNode}),
                                            JcrLexicon.CONTENT,
                                            Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                         context,
@@ -203,14 +202,14 @@
                                                                                                         OnParentVersionBehavior.COPY.getJcrValue(),
                                                                                                         false, true, false,
                                                                                                         false, null,
-                                                                                                        new NodeType[] {base})}),
+                                                                                                        new JcrNodeType[] {base})}),
                                            NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType folder = new JcrNodeType(
                                              context,
                                              NO_NODE_TYPE_MANAGER,
                                              JcrNtLexicon.FOLDER,
-                                             Arrays.asList(new NodeType[] {hierarchyNode}),
+                                             Arrays.asList(new JcrNodeType[] {hierarchyNode}),
                                              NO_PRIMARY_ITEM_NAME,
                                              Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                           context,
@@ -222,14 +221,14 @@
                                                                                                           false,
                                                                                                           false,
                                                                                                           null,
-                                                                                                          new NodeType[] {hierarchyNode})}),
+                                                                                                          new JcrNodeType[] {hierarchyNode})}),
                                              NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType frozenNode = new JcrNodeType(
                                                  context,
                                                  NO_NODE_TYPE_MANAGER,
                                                  JcrNtLexicon.FROZEN_NODE,
-                                                 Arrays.asList(new NodeType[] {base, referenceable}),
+                                                 Arrays.asList(new JcrNodeType[] {base, referenceable}),
                                                  NO_PRIMARY_ITEM_NAME,
                                                  Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                               context,
@@ -241,7 +240,7 @@
                                                                                                               true,
                                                                                                               true,
                                                                                                               null,
-                                                                                                              new NodeType[] {base})}),
+                                                                                                              new JcrNodeType[] {base})}),
                                                  Arrays.asList(new JcrPropertyDefinition[] {
                                                      new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_MIXIN_TYPES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -269,7 +268,7 @@
                                                  context,
                                                  NO_NODE_TYPE_MANAGER,
                                                  JcrNtLexicon.LINKED_FILE,
-                                                 Arrays.asList(new NodeType[] {hierarchyNode}),
+                                                 Arrays.asList(new JcrNodeType[] {hierarchyNode}),
                                                  JcrLexicon.CONTENT,
                                                  NO_CHILD_NODES,
                                                  Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
@@ -291,7 +290,7 @@
                                                          context,
                                                          NO_NODE_TYPE_MANAGER,
                                                          JcrNtLexicon.PROPERTY_DEFINITION,
-                                                         Arrays.asList(new NodeType[] {base}),
+                                                         Arrays.asList(new JcrNodeType[] {base}),
                                                          NO_PRIMARY_ITEM_NAME,
                                                          NO_CHILD_NODES,
                                                          Arrays.asList(new JcrPropertyDefinition[] {
@@ -366,16 +365,16 @@
                                                          NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
         JcrNodeType nodeType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.NODE_TYPE,
-                                               Arrays.asList(new NodeType[] {base}), NO_PRIMARY_ITEM_NAME,
+                                               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,
-                                                                         new NodeType[] {childNodeDefinition}),
+                                                                         new JcrNodeType[] {childNodeDefinition}),
                                                    new JcrNodeDefinition(context, null, JcrLexicon.PROPERTY_DEFINITION,
                                                                          OnParentVersionBehavior.VERSION.getJcrValue(), false,
                                                                          false, false, true, JcrNtLexicon.PROPERTY_DEFINITION,
-                                                                         new NodeType[] {propertyDefinition})}),
+                                                                         new JcrNodeType[] {propertyDefinition})}),
                                                Arrays.asList(new JcrPropertyDefinition[] {
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
@@ -400,7 +399,7 @@
                                                UNORDERABLE_CHILD_NODES);
 
         JcrNodeType query = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.QUERY,
-                                            Arrays.asList(new NodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                            Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
                                             Arrays.asList(new JcrPropertyDefinition[] {
                                                 new JcrPropertyDefinition(context, null, JcrLexicon.LANGUAGE,
                                                                           OnParentVersionBehavior.COPY.getJcrValue(), false,
@@ -413,7 +412,7 @@
                                             UNORDERABLE_CHILD_NODES);
 
         JcrNodeType resource = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.RESOURCE,
-                                               Arrays.asList(new NodeType[] {base, referenceable}), JcrLexicon.DATA,
+                                               Arrays.asList(new JcrNodeType[] {base, referenceable}), JcrLexicon.DATA,
                                                NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
                                                    new JcrPropertyDefinition(context, null, JcrLexicon.DATA,
                                                                              OnParentVersionBehavior.COPY.getJcrValue(), false,
@@ -437,7 +436,7 @@
                                                    context,
                                                    NO_NODE_TYPE_MANAGER,
                                                    JcrNtLexicon.UNSTRUCTURED,
-                                                   Arrays.asList(new NodeType[] {base}),
+                                                   Arrays.asList(new JcrNodeType[] {base}),
                                                    NO_PRIMARY_ITEM_NAME,
                                                    Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                                 context,
@@ -449,7 +448,7 @@
                                                                                                                 false,
                                                                                                                 true,
                                                                                                                 JcrNtLexicon.UNSTRUCTURED,
-                                                                                                                new NodeType[] {base}),}),
+                                                                                                                new JcrNodeType[] {base}),}),
                                                    Arrays.asList(new JcrPropertyDefinition[] {
                                                        new JcrPropertyDefinition(context, null, ALL_NODES,
                                                                                  OnParentVersionBehavior.COPY.getJcrValue(),
@@ -465,7 +464,7 @@
                                               context,
                                               NO_NODE_TYPE_MANAGER,
                                               JcrNtLexicon.VERSION,
-                                              Arrays.asList(new NodeType[] {base, referenceable}),
+                                              Arrays.asList(new JcrNodeType[] {base, referenceable}),
                                               NO_PRIMARY_ITEM_NAME,
                                               Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
                                                                                                            context,
@@ -477,7 +476,7 @@
                                                                                                            true,
                                                                                                            false,
                                                                                                            null,
-                                                                                                           new NodeType[] {frozenNode}),}),
+                                                                                                           new JcrNodeType[] {frozenNode}),}),
                                               Arrays.asList(new JcrPropertyDefinition[] {
                                                   new JcrPropertyDefinition(context, null, JcrLexicon.CREATED,
                                                                             OnParentVersionBehavior.ABORT.getJcrValue(), true,
@@ -497,7 +496,7 @@
                                                     context,
                                                     NO_NODE_TYPE_MANAGER,
                                                     JcrNtLexicon.VERSION_LABELS,
-                                                    Arrays.asList(new NodeType[] {base}),
+                                                    Arrays.asList(new JcrNodeType[] {base}),
                                                     NO_PRIMARY_ITEM_NAME,
                                                     NO_CHILD_NODES,
                                                     Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
@@ -518,21 +517,21 @@
                                                      context,
                                                      NO_NODE_TYPE_MANAGER,
                                                      JcrNtLexicon.VERSION_HISTORY,
-                                                     Arrays.asList(new NodeType[] {base, referenceable}),
+                                                     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 NodeType[] {version}),
+                                                                               new JcrNodeType[] {version}),
                                                          new JcrNodeDefinition(context, null, JcrLexicon.VERSION_LABELS,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(), true,
                                                                                true, true, false, JcrNtLexicon.VERSION_LABELS,
-                                                                               new NodeType[] {versionLabels}),
+                                                                               new JcrNodeType[] {versionLabels}),
                                                          new JcrNodeDefinition(context, null, ALL_NODES,
                                                                                OnParentVersionBehavior.ABORT.getJcrValue(),
                                                                                false, false, true, false, JcrNtLexicon.VERSION,
-                                                                               new NodeType[] {version}),}),
+                                                                               new JcrNodeType[] {version}),}),
                                                      Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
                                                                                                                           context,
                                                                                                                           null,
@@ -552,7 +551,7 @@
                                                      context,
                                                      NO_NODE_TYPE_MANAGER,
                                                      JcrNtLexicon.VERSIONED_CHILD,
-                                                     Arrays.asList(new NodeType[] {base}),
+                                                     Arrays.asList(new JcrNodeType[] {base}),
                                                      NO_PRIMARY_ITEM_NAME,
                                                      NO_CHILD_NODES,
                                                      Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
@@ -591,7 +590,7 @@
                                                   context,
                                                   NO_NODE_TYPE_MANAGER,
                                                   JcrMixLexicon.VERSIONABLE,
-                                                  Arrays.asList(new NodeType[] {referenceable}),
+                                                  Arrays.asList(new JcrNodeType[] {referenceable}),
                                                   NO_PRIMARY_ITEM_NAME,
                                                   NO_CHILD_NODES,
                                                   Arrays.asList(new JcrPropertyDefinition[] {

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -23,6 +23,10 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
@@ -45,8 +49,12 @@
     private final Name defaultPrimaryTypeName;
 
     /** @see NodeDefinition#getRequiredPrimaryTypes() */
-    private final NodeType[] requiredPrimaryTypes;
+    private final Map<Name, JcrNodeType> requiredPrimaryTypesByName;
 
+    private JcrNodeType[] requiredPrimaryTypes;
+
+    private Name[] requiredPrimaryTypeNames;
+
     /** A durable identifier for this node definition. */
     private NodeDefinitionId id;
 
@@ -62,12 +70,9 @@
                        boolean protectedItem,
                        boolean allowsSameNameSiblings,
                        Name defaultPrimaryTypeName,
-                       NodeType[] requiredPrimaryTypes ) {
-        super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
-        this.nodeTypeManager = null;
-        this.allowsSameNameSiblings = allowsSameNameSiblings;
-        this.defaultPrimaryTypeName = defaultPrimaryTypeName;
-        this.requiredPrimaryTypes = requiredPrimaryTypes;
+                       JcrNodeType[] requiredPrimaryTypes ) {
+        this(context, null, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem,
+             allowsSameNameSiblings, defaultPrimaryTypeName, requiredPrimaryTypes);
     }
 
     JcrNodeDefinition( ExecutionContext context,
@@ -80,12 +85,22 @@
                        boolean protectedItem,
                        boolean allowsSameNameSiblings,
                        Name defaultPrimaryTypeName,
-                       NodeType[] requiredPrimaryTypes ) {
+                       JcrNodeType[] requiredPrimaryTypes ) {
         super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
         this.nodeTypeManager = nodeTypeManager;
         this.allowsSameNameSiblings = allowsSameNameSiblings;
         this.defaultPrimaryTypeName = defaultPrimaryTypeName;
-        this.requiredPrimaryTypes = requiredPrimaryTypes;
+        this.requiredPrimaryTypes = new JcrNodeType[requiredPrimaryTypes.length];
+        this.requiredPrimaryTypeNames = new Name[requiredPrimaryTypes.length];
+        for (int i = 0; i != requiredPrimaryTypes.length; ++i) {
+            this.requiredPrimaryTypes[i] = requiredPrimaryTypes[i];
+            this.requiredPrimaryTypeNames[i] = requiredPrimaryTypes[i].getInternalName();
+        }
+        Map<Name, JcrNodeType> requiredPrimaryTypesByName = new HashMap<Name, JcrNodeType>();
+        for (JcrNodeType requiredPrimaryType : requiredPrimaryTypes) {
+            requiredPrimaryTypesByName.put(requiredPrimaryType.getInternalName(), requiredPrimaryType);
+        }
+        this.requiredPrimaryTypesByName = Collections.unmodifiableMap(requiredPrimaryTypesByName);
     }
 
     /**
@@ -95,7 +110,8 @@
      */
     public NodeDefinitionId getId() {
         if (id == null) {
-            id = new NodeDefinitionId(declaringNodeType.getInternalName(), name);
+            // This is idempotent, so no need to lock
+            id = new NodeDefinitionId(this.declaringNodeType.getInternalName(), this.name, this.requiredPrimaryTypeNames);
         }
         return id;
     }
@@ -129,10 +145,48 @@
      * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes()
      */
     public NodeType[] getRequiredPrimaryTypes() {
-        return requiredPrimaryTypes;
+        // Make a copy so that the caller can't modify our content ...
+        NodeType[] result = new NodeType[requiredPrimaryTypes.length];
+        for (int i = 0; i != requiredPrimaryTypes.length; ++i) {
+            result[i] = requiredPrimaryTypes[i];
+        }
+        return result;
     }
 
     /**
+     * Get the set of names of the primary types.
+     * 
+     * @return the required primary type names
+     */
+    Set<Name> getRequiredPrimaryTypeNames() {
+        return requiredPrimaryTypesByName.keySet();
+    }
+
+    /**
+     * Determine if this node definition will allow a child with the supplied primary type. This method checks this definition's
+     * {@link #getRequiredPrimaryTypes()} against the supplied primary type and its supertypes. The supplied primary type for the
+     * child must be or extend all of the types defined by the {@link #getRequiredPrimaryTypes() required primary types}.
+     * 
+     * @param childPrimaryType the primary type of the child
+     * @return true if the primary type of the child (or one of its supertypes) is one of the types required by this definition,
+     *         or false otherwise
+     */
+    final boolean allowsChildWithType( JcrNodeType childPrimaryType ) {
+        if (childPrimaryType == null) {
+            // The definition must have a default primary type ...
+            if (defaultPrimaryTypeName != null) {
+                return true;
+            }
+            return false;
+        }
+        // The supplied primary type must be or extend all of the required primary types ...
+        for (Name requiredPrimaryTypeName : requiredPrimaryTypesByName.keySet()) {
+            if (!childPrimaryType.isNodeType(requiredPrimaryTypeName)) return false;
+        }
+        return true;
+    }
+
+    /**
      * Creates a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
      * <code>declaringNodeType</code>. Provided to support immutable pattern for this class.
      * 
@@ -141,15 +195,27 @@
      *         <code>declaringNodeType</code>.
      */
     JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
+        JcrNodeType[] required = requiredPrimaryTypesByName.values().toArray(new JcrNodeType[requiredPrimaryTypesByName.size()]);
         return new JcrNodeDefinition(this.context, declaringNodeType.nodeTypeManager(), declaringNodeType, name,
                                      getOnParentVersion(), isAutoCreated(), isMandatory(), isProtected(),
-                                     allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
+                                     allowsSameNameSiblings(), defaultPrimaryTypeName, required);
     }
 
     JcrNodeDefinition with( ExecutionContext context ) {
+        JcrNodeType[] required = requiredPrimaryTypesByName.values().toArray(new JcrNodeType[requiredPrimaryTypesByName.size()]);
         return new JcrNodeDefinition(context, this.nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
                                      isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
-                                     defaultPrimaryTypeName, requiredPrimaryTypes);
+                                     defaultPrimaryTypeName, required);
     }
 
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getId().toString();
+    }
+
 }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -23,12 +23,13 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import java.util.Stack;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeDefinition;
@@ -38,6 +39,7 @@
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
 
 /**
  * DNA implementation of JCR {@link NodeType}s.
@@ -46,25 +48,52 @@
 class JcrNodeType implements NodeType {
 
     public static final String RESIDUAL_ITEM_NAME = "*";
+    public static final Name RESIDUAL_NAME = new BasicName("", RESIDUAL_ITEM_NAME);
 
     /** The name of the node type (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code>) */
     private final Name name;
     /** The name of the node's primary item */
     private final Name primaryItemName;
 
-    /** The set of child node definitions for nodes of this type (possibly empty). */
-    private final Set<JcrNodeDefinition> childNodeDefinitions;
-    /** The set of property definitions for nodes of this type (possibly empty). */
-    private final Set<JcrPropertyDefinition> propertyDefinitions;
     /** The supertypes for this node. */
-    private final List<NodeType> declaredSupertypes;
+    private final List<JcrNodeType> declaredSupertypes;
 
+    /**
+     * The list of all supertypes for this node, beginning with the immediate supertypes, followed by the supertypes of those
+     * supertypes, etc.
+     */
+    private final List<JcrNodeType> allSupertypes;
+
+    /**
+     * The list of this type and all supertypes for this node, beginning with this type, continuing with the immediate supertypes,
+     * followed by the supertypes of those supertypes, etc.
+     */
+    private final List<JcrNodeType> thisAndAllSupertypes;
+    private final Set<Name> thisAndAllSupertypesNames;
+
     /** Indicates whether this node type is a mixin type (as opposed to a primary type). */
     private boolean mixin;
     /** Indicates whether the child nodes of nodes of this type can be ordered. */
     private boolean orderableChildNodes;
 
     /**
+     * The child node definitions that are defined on this node type.
+     */
+    private final List<JcrNodeDefinition> childNodeDefinitions;
+
+    /**
+     * The property definitions that are defined on this node type.
+     */
+    private final List<JcrPropertyDefinition> propertyDefinitions;
+
+    /**
+     * A local cache of all defined and inherited child node definitions and property definitions. Residual definitions are
+     * included. This class's methods to find a property definition and find child node definitions, and since they're frequently
+     * used by SessionCache this cache provides very quick access.
+     */
+    private final DefinitionCache allDefinitions;
+
+    /**
      * A reference to the execution context in which this node type exists, used to remap the internal names to their appropriate
      * prefixed version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>&quot;nt:base&quot;</code>.).
      */
@@ -76,7 +105,7 @@
     JcrNodeType( ExecutionContext context,
                  RepositoryNodeTypeManager nodeTypeManager,
                  Name name,
-                 List<NodeType> declaredSupertypes,
+                 List<JcrNodeType> declaredSupertypes,
                  Name primaryItemName,
                  Collection<JcrNodeDefinition> childNodeDefinitions,
                  Collection<JcrPropertyDefinition> propertyDefinitions,
@@ -86,214 +115,97 @@
         this.nodeTypeManager = nodeTypeManager;
         this.name = name;
         this.primaryItemName = primaryItemName;
-        this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<NodeType>emptyList();
+        this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<JcrNodeType>emptyList();
         this.mixin = mixin;
         this.orderableChildNodes = orderableChildNodes;
-        this.propertyDefinitions = new HashSet<JcrPropertyDefinition>(propertyDefinitions.size());
+        this.propertyDefinitions = new ArrayList<JcrPropertyDefinition>(propertyDefinitions.size());
         for (JcrPropertyDefinition property : propertyDefinitions) {
             this.propertyDefinitions.add(property.with(this));
         }
 
-        this.childNodeDefinitions = new HashSet<JcrNodeDefinition>(childNodeDefinitions.size());
+        this.childNodeDefinitions = new ArrayList<JcrNodeDefinition>(childNodeDefinitions.size());
         for (JcrNodeDefinition childNode : childNodeDefinitions) {
             this.childNodeDefinitions.add(childNode.with(this));
         }
-    }
 
-    /**
-     * Returns the property definition with the given name. This method first checks the property definitions declared within this
-     * type to see if any property definitions have the given name. If no matches are found, this method initiates a recursive
-     * depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the supertypes of
-     * the supertypes).
-     * 
-     * @param propertyName the name of the property for which the definition should be retrieved. Use
-     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
-     * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
-     *        is preferred
-     * @return the property definition for the given name or <code>null</code> if no such definition exists.
-     * @see JcrNodeType#RESIDUAL_ITEM_NAME
-     */
-    JcrPropertyDefinition getPropertyDefinition( String propertyName,
-                                                 boolean preferMultiValued ) {
-        JcrPropertyDefinition result = null;
-        for (JcrPropertyDefinition property : propertyDefinitions) {
-            if (propertyName.equals(property.getName())) {
-                result = property;
-                if (property.isMultiple() == preferMultiValued) return result;
-                // Otherwise, keep looking for a better match ...
+        // Build the list of all types, including supertypes ...
+        List<JcrNodeType> thisAndAllSupertypes = new LinkedList<JcrNodeType>();
+        Set<Name> typeNames = new HashSet<Name>();
+        thisAndAllSupertypes.add(this);
+        typeNames.add(this.name);
+        for (int i = 0; i != thisAndAllSupertypes.size(); ++i) {
+            JcrNodeType superType = thisAndAllSupertypes.get(i);
+            for (NodeType superSuperType : superType.getDeclaredSupertypes()) {
+                JcrNodeType jcrSuperSuperType = (JcrNodeType)superSuperType;
+                if (typeNames.add(jcrSuperSuperType.getInternalName())) {
+                    thisAndAllSupertypes.add(jcrSuperSuperType);
+                }
             }
         }
+        this.thisAndAllSupertypes = Collections.unmodifiableList(thisAndAllSupertypes);
+        // Make the list of all supertypes to be a sublist of the first ...
+        this.allSupertypes = thisAndAllSupertypes.size() > 1 ? thisAndAllSupertypes.subList(1, thisAndAllSupertypes.size()) : Collections.<JcrNodeType>emptyList();
 
-        for (NodeType nodeType : declaredSupertypes) {
-            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
-            if (definition != null) {
-                if (definition.isMultiple() == preferMultiValued) return definition;
-                if (result == null) result = definition;
-            }
-        }
-        return result; // may be null
+        // Set up the set of all supertype names ...
+        this.thisAndAllSupertypesNames = Collections.unmodifiableSet(typeNames);
+
+        this.allDefinitions = new DefinitionCache(this);
     }
 
-    /**
-     * Returns the property definition with the given name. This method first checks the property definitions declared within this
-     * type to see if any property definitions have the given name. If no matches are found, this method initiates a recursive
-     * depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the supertypes of
-     * the supertypes).
-     * 
-     * @param propertyName the name of the property for which the definition should be retrieved. Use
-     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
-     * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
-     *        is preferred
-     * @return the property definition for the given name or <code>null</code> if no such definition exists.
-     * @see JcrNodeType#RESIDUAL_ITEM_NAME
-     */
-    JcrPropertyDefinition getPropertyDefinition( Name propertyName,
-                                                 boolean preferMultiValued ) {
-        JcrPropertyDefinition result = null;
-        for (JcrPropertyDefinition property : propertyDefinitions) {
-            if (propertyName.equals(property.getInternalName())) {
-                result = property;
-                if (property.isMultiple() == preferMultiValued) return result;
-                // Otherwise, keep looking for a better match ...
-            }
-        }
-
-        for (NodeType nodeType : declaredSupertypes) {
-            JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
-            if (definition != null) {
-                if (definition.isMultiple() == preferMultiValued) return definition;
-                if (result == null) result = definition;
-            }
-        }
-        return result; // may be null
+    List<JcrNodeType> getTypeAndSupertypes() {
+        return thisAndAllSupertypes;
     }
 
     /**
-     * Returns the child node definition with the given name. This method first checks the child node definitions declared within
-     * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
-     * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
-     * supertypes of the supertypes).
+     * Get the child definitions defined on this node type (excluding inherited definitions).
      * 
-     * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
-     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
-     * @return the child node definition with the given name or <code>null</code> if no such definition exists.
-     * @see JcrNodeType#RESIDUAL_ITEM_NAME
-     * @see #getChildNodeDefinition(Name)
+     * @return this node's child node definitions; never null
      */
-    JcrNodeDefinition getChildNodeDefinition( String childDefinitionName ) {
-        for (JcrNodeDefinition childNode : childNodeDefinitions) {
-            if (childDefinitionName.equals(childNode.getName())) {
-                return childNode;
-            }
-        }
-
-        for (NodeType nodeType : declaredSupertypes) {
-            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
-            if (definition != null) return definition;
-        }
-        return null;
+    List<JcrNodeDefinition> childNodeDefinitions() {
+        return childNodeDefinitions;
     }
 
     /**
-     * Returns the child node definition with the given name. This method first checks the child node definitions declared within
-     * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
-     * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
-     * supertypes of the supertypes).
+     * Get the property definitions defined on this node type (excluding inherited definitions).
      * 
-     * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
-     *        {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
-     * @return the child node definition with the given name or <code>null</code> if no such definition exists.
-     * @see JcrNodeType#RESIDUAL_ITEM_NAME
-     * @see #getChildNodeDefinition(String)
+     * @return this node's property definitions; never null
      */
-    JcrNodeDefinition getChildNodeDefinition( Name childDefinitionName ) {
-        for (JcrNodeDefinition childNode : childNodeDefinitions) {
-            if (childDefinitionName.equals(childNode.name)) {
-                return childNode;
-            }
-        }
+    List<JcrPropertyDefinition> propertyDefinitions() {
+        return propertyDefinitions;
+    }
 
-        for (NodeType nodeType : declaredSupertypes) {
-            JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
-            if (definition != null) return definition;
-        }
-        return null;
+    Collection<JcrPropertyDefinition> allSingleValuePropertyDefinitions( Name propertyName ) {
+        return allDefinitions.allSingleValuePropertyDefinitions(propertyName);
     }
 
-    /**
-     * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
-     * primary type is not supplied, then only the name is considered when finding a best match.
-     * 
-     * @param childName the name of the child
-     * @param primaryNodeTypeName the name of the primary node type for the child
-     * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
-     *         are not allowed given this node type
-     */
-    JcrNodeDefinition findBestNodeDefinitionForChild( Name childName,
-                                                      Name primaryNodeTypeName ) {
-        // First, try to find a child node definition with the given name
-        JcrNodeDefinition childNode = getChildNodeDefinition(childName);
+    Collection<JcrPropertyDefinition> allMultiValuePropertyDefinitions( Name propertyName ) {
+        return allDefinitions.allMultiValuePropertyDefinitions(propertyName);
+    }
 
-        // If there are no named definitions in the type hierarchy, try to find a residual node definition
-        boolean checkResidual = true;
-        if (childNode == null) {
-            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
-            checkResidual = false;
-        }
+    Collection<JcrPropertyDefinition> allPropertyDefinitions( Name propertyName ) {
+        return allDefinitions.allPropertyDefinitions(propertyName);
+    }
 
-        // Check if the node can be added with the named child node definition
-        if (childNode != null && primaryNodeTypeName != null) {
-            NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
-            if (primaryNodeType == null) return null;
-            if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) {
-                if (checkResidual) {
-                    // Find a residual child definition ...
-                    childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
-                    if (childNode != null) {
-                        // Check the residual child definition ...
-                        if (checkTypeAgainstDefinition(primaryNodeType, childNode)) return childNode;
-                    }
-                }
-                return null;
-            }
-        }
-        return childNode;
+    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name propertyName,
+                                                           boolean requireSns ) {
+        return allDefinitions.allChildNodeDefinitions(propertyName, requireSns);
     }
 
+    Collection<JcrNodeDefinition> allChildNodeDefinitions( Name propertyName ) {
+        return allDefinitions.allChildNodeDefinitions(propertyName);
+    }
+
     /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
      */
     public boolean canAddChildNode( String childNodeName ) {
-
         CheckArg.isNotNull(childNodeName, "childNodeName");
-
-        // First, try to find a child node definition with the given name
-        JcrNodeDefinition childNode = getChildNodeDefinition(childNodeName);
-
-        // If there are no named definitions in the type hierarchy, try to find a residual node definition
-        if (childNode == null) {
-            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
-        }
-
-        if (childNode != null) {
-            NodeType defaultType = childNode.getDefaultPrimaryType();
-            // If there's no default type, the child node can't be created
-            if (defaultType == null) {
-                return false;
-            }
-
-            // Check if the node can be added with the named child node definition
-            return checkTypeAgainstDefinition(defaultType, childNode);
-        }
-        return false;
+        Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
+        return nodeTypeManager().findChildNodeDefinition(this.name, null, childName, null, 0, true) != null;
     }
 
-    protected final NodeType getPrimaryNodeType( Name primaryNodeTypeName ) {
-        return nodeTypeManager.getNodeType(primaryNodeTypeName);
-    }
-
     /**
      * {@inheritDoc}
      * 
@@ -304,81 +216,28 @@
 
         CheckArg.isNotNull(childNodeName, "childNodeName");
         CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
-
-        Name nodeTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
-        NodeType primaryNodeType = getPrimaryNodeType(nodeTypeName);
-        if (primaryNodeType == null) {
-            // The node type doesn't exist, so you can't add a child node with that type
-            return false;
-        }
-
-        // First, try to find a child node definition with the given name
-        JcrNodeDefinition childNode = getChildNodeDefinition(childNodeName);
-
-        // If there are no named definitions in the type hierarchy, try to find a residual node definition
-        if (childNode == null) {
-            childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
-        }
-
-        // Check if the node can be added with the named child node definition
-        if (childNode != null) {
-            return checkTypeAgainstDefinition(primaryNodeType, childNode);
-        }
-
-        return false;
+        Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
+        Name childPrimaryTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
+        return nodeTypeManager().findChildNodeDefinition(this.name, null, childName, childPrimaryTypeName, 0, true) != null;
     }
 
-    /**
-     * Checks whether the given type is the same type or a subtype of each of the required primary types for the given node
-     * definition.
-     * 
-     * @param typeToCheck the type to check
-     * @param definition the node definition to check against
-     * @return <code>true</code> if and only if the given type is the same type or extends each of the required primary types in
-     *         the given definition
-     */
-    private boolean checkTypeAgainstDefinition( NodeType typeToCheck,
-                                                NodeDefinition definition ) {
-        NodeType[] requiredPrimaryTypes = definition.getRequiredPrimaryTypes();
-        for (int i = 0; i < requiredPrimaryTypes.length; i++) {
-            // See if the given type for the node matches all of the required primary types
-            if (!typeToCheck.isNodeType(requiredPrimaryTypes[i].getName())) {
-                return false;
-            }
-        }
-        // The node can be added with the given type based on the given child node definition
-        return true;
+    public boolean canRemoveNode( String itemName ) {
+        CheckArg.isNotNull(itemName, "itemName");
+        Name childName = context.getValueFactories().getNameFactory().create(itemName);
+        return nodeTypeManager().canRemoveAllChildren(this.name, null, childName, true);
     }
 
     /**
      * {@inheritDoc}
+     * <p>
+     * According to the JCR 1.0 JavaDoc, this method applies to all children. However, this appears to be changed in the JSR-283
+     * draft to apply only to nodes, and it is also deprecated.
+     * </p>
      * 
      * @see javax.jcr.nodetype.NodeType#canRemoveItem(java.lang.String)
      */
     public boolean canRemoveItem( String itemName ) {
-        CheckArg.isNotNull(itemName, "itemName");
-
-        // Don't know if item is a property or a node, so check both locally before moving up the type hierarchy
-        for (PropertyDefinition item : propertyDefinitions) {
-            if (itemName.equals(item.getName())) {
-                return !item.isMandatory() && !item.isProtected();
-            }
-        }
-
-        for (NodeDefinition item : childNodeDefinitions) {
-            if (itemName.equals(item.getName())) {
-                return !item.isMandatory() && !item.isProtected();
-            }
-        }
-
-        // Check if any supertypes prevent the removal of this item
-        for (NodeType type : declaredSupertypes) {
-            if (!type.canRemoveItem(itemName)) {
-                return false;
-            }
-        }
-
-        return true;
+        return canRemoveNode(itemName) || canRemoveProperty(itemName);
     }
 
     /**
@@ -395,19 +254,38 @@
      * @see PropertyDefinition#getValueConstraints()
      * @see JcrPropertyDefinition#satisfiesConstraints(Value)
      */
-    private boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
-                                                        Value value ) {
+    boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
+                                                Value value ) {
         try {
             assert value instanceof JcrValue : "Illegal implementation of Value interface";
-            ((JcrValue)value).asType(propertyDefinition.getRequiredType());
-
+            ((JcrValue)value).asType(propertyDefinition.getRequiredType()); // throws ValueFormatException if there's a problem
             return propertyDefinition.satisfiesConstraints(value);
-
         } catch (javax.jcr.ValueFormatException vfe) {
             // Cast failed
             return false;
         }
+    }
 
+    /**
+     * Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
+     * conversion rules in section 6.2.6 of the JCR 1.0 specification AND <code>value</code> satisfies the constraints (if any)
+     * for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
+     * will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
+     * type specified in <code>value.getType()</code>.
+     * 
+     * @param propertyDefinition the property definition to validate against
+     * @param values the values to be validated
+     * @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
+     *         satisfies the constraints for the property (if any exist).
+     * @see PropertyDefinition#getValueConstraints()
+     * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+     */
+    boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
+                                                Value[] values ) {
+        for (Value value : values) {
+            if (!canCastToTypeAndMatchesConstraints(propertyDefinition, value)) return false;
+        }
+        return true;
     }
 
     /**
@@ -418,27 +296,10 @@
     public boolean canSetProperty( String propertyName,
                                    Value value ) {
         CheckArg.isNotNull(propertyName, "propertyName");
+        Name name = context.getValueFactories().getNameFactory().create(propertyName);
 
-        JcrPropertyDefinition property = getPropertyDefinition(propertyName, false);
-        if (property == null) {
-            property = getPropertyDefinition(RESIDUAL_ITEM_NAME, false);
-        }
-
-        if (property == null) {
-            return false;
-        }
-
-        // Can't modify a multi-property with a single value. Can't modify a protected property at all.
-        if (property.isMultiple() || property.isProtected()) {
-            return false;
-        }
-
-        // Null values indicates an attempt to unset property
-        if (value == null) {
-            return !property.isMandatory();
-        }
-
-        return canCastToTypeAndMatchesConstraints(property, value);
+        // Reuse the logic in RepositoryNodeTypeManager ...
+        return nodeTypeManager().findPropertyDefinition(this.name, null, name, value, false, true) != null;
     }
 
     /**
@@ -449,34 +310,21 @@
     public boolean canSetProperty( String propertyName,
                                    Value[] values ) {
         CheckArg.isNotNull(propertyName, "propertyName");
-
-        JcrPropertyDefinition property = getPropertyDefinition(propertyName, true);
-        if (property == null) {
-            property = getPropertyDefinition(RESIDUAL_ITEM_NAME, true);
+        if (values == null || values.length == 0) {
+            return canRemoveProperty(propertyName);
         }
 
-        if (property == null) {
-            return false;
-        }
+        Name name = context.getValueFactories().getNameFactory().create(propertyName);
+        // Reuse the logic in RepositoryNodeTypeManager ...
+        return nodeTypeManager().findPropertyDefinition(this.name, null, name, values, true) != null;
+    }
 
-        // Can't modify a single valued property with a multiple values. Can't modify a protected property at all.
-        if (!property.isMultiple() || property.isProtected()) {
-            return false;
-        }
+    public boolean canRemoveProperty( String propertyName ) {
+        CheckArg.isNotNull(propertyName, "propertyName");
+        Name name = context.getValueFactories().getNameFactory().create(propertyName);
 
-        // Null values indicates an attempt to unset property
-        if (values == null) {
-            return !property.isMandatory();
-        }
-
-        for (int i = 0; i < values.length; i++) {
-            if (values[i] != null) {
-                if (!canCastToTypeAndMatchesConstraints(property, values[i])) {
-                    return false;
-                }
-            }
-        }
-        return true;
+        // Reuse the logic in RepositoryNodeTypeManager ...
+        return nodeTypeManager().canRemoveProperty(this.name, null, name, true);
     }
 
     /**
@@ -485,6 +333,7 @@
      * @see javax.jcr.nodetype.NodeType#getDeclaredChildNodeDefinitions()
      */
     public NodeDefinition[] getDeclaredChildNodeDefinitions() {
+        // Always have to make a copy to prevent changes ...
         return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
     }
 
@@ -494,23 +343,9 @@
      * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions()
      */
     public NodeDefinition[] getChildNodeDefinitions() {
-        Set<NodeDefinition> nodeDefs = new HashSet<NodeDefinition>();
-        NodeType[] supertypes = getSupertypes();
-
-        // TODO: This could be cached after being calculated once
-        for (int i = 0; i < supertypes.length; i++) {
-            NodeDefinition[] childNodeDefinitions = supertypes[i].getChildNodeDefinitions();
-            for (int j = 0; j < childNodeDefinitions.length; i++) {
-
-                // TODO: Could add sanity check here (assertion?) that definitions of the same child node in multiple supertypes
-                // are consistent
-                nodeDefs.add(childNodeDefinitions[j]);
-            }
-        }
-
-        nodeDefs.addAll(childNodeDefinitions);
-
-        return nodeDefs.toArray(new JcrNodeDefinition[nodeDefs.size()]);
+        // Always have to make a copy to prevent changes ...
+        Collection<JcrNodeDefinition> definitions = this.allDefinitions.allChildNodeDefinitions();
+        return definitions.toArray(new NodeDefinition[definitions.size()]);
     }
 
     /**
@@ -519,22 +354,9 @@
      * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions()
      */
     public PropertyDefinition[] getPropertyDefinitions() {
-        Set<PropertyDefinition> propDefs = new HashSet<PropertyDefinition>();
-        NodeType[] supertypes = getSupertypes();
-
-        // TODO: This could be cached after being calculated once
-        for (int i = 0; i < supertypes.length; i++) {
-            PropertyDefinition[] childPropertyDefinitions = supertypes[i].getPropertyDefinitions();
-            for (int j = 0; j < childPropertyDefinitions.length; j++) {
-
-                // TODO: Could add sanity check here (assertion?) that definitions of the same child node in multiple supertypes
-                // are consistent
-                propDefs.add(childPropertyDefinitions[j]);
-            }
-        }
-        propDefs.addAll(propertyDefinitions);
-
-        return propDefs.toArray(new JcrPropertyDefinition[propDefs.size()]);
+        // Always have to make a copy to prevent changes ...
+        Collection<JcrPropertyDefinition> definitions = this.allDefinitions.allPropertyDefinitions();
+        return definitions.toArray(new PropertyDefinition[definitions.size()]);
     }
 
     /**
@@ -543,6 +365,7 @@
      * @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes()
      */
     public NodeType[] getDeclaredSupertypes() {
+        // Always have to make a copy to prevent changes ...
         return declaredSupertypes.toArray(new NodeType[declaredSupertypes.size()]);
     }
 
@@ -595,31 +418,7 @@
      * @see javax.jcr.nodetype.NodeType#getSupertypes()
      */
     public NodeType[] getSupertypes() {
-        Set<NodeType> supertypes = new HashSet<NodeType>();
-        Stack<NodeType> unvisitedSupertypes = new Stack<NodeType>();
-
-        assert declaredSupertypes != null;
-        unvisitedSupertypes.addAll(declaredSupertypes);
-
-        // TODO: If this ends up getting called frequently, it should probably be executed once in the constructor and have the
-        // results cached.
-        while (!unvisitedSupertypes.isEmpty()) {
-            NodeType nodeType = unvisitedSupertypes.pop();
-
-            /*
-             * If we haven't already visited this nodeType (which we can
-             * infer by whether or not it was already added to the return set),
-             * then add the supertypes of this new node to the unvisited set for 
-             * further inspection.
-             */
-            if (!supertypes.contains(nodeType)) {
-                supertypes.add(nodeType);
-                // Violating encapsulation to avoid going from List to array back to List
-                unvisitedSupertypes.addAll(((JcrNodeType)nodeType).declaredSupertypes);
-            }
-        }
-
-        return supertypes.toArray(new NodeType[0]);
+        return allSupertypes.toArray(new NodeType[allSupertypes.size()]);
     }
 
     /**
@@ -646,17 +445,14 @@
      * @see javax.jcr.nodetype.NodeType#isNodeType(java.lang.String)
      */
     public boolean isNodeType( String nodeTypeName ) {
-        if (this.getName().equals(nodeTypeName)) return true;
+        if (nodeTypeName == null) return false;
+        Name name = context.getValueFactories().getNameFactory().create(nodeTypeName);
+        return this.thisAndAllSupertypesNames.contains(name);
+    }
 
-        // TODO: This could be optimized
-        NodeType[] supertypes = getSupertypes();
-        for (int i = 0; i < supertypes.length; i++) {
-            if (supertypes[i].isNodeType(nodeTypeName)) {
-                return true;
-            }
-        }
-
-        return false;
+    boolean isNodeType( Name nodeTypeName ) {
+        if (nodeTypeName == null) return false;
+        return this.thisAndAllSupertypesNames.contains(nodeTypeName);
     }
 
     @Override

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -26,12 +26,15 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
@@ -178,24 +181,206 @@
      */
     JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
         if (definitionId == null) return null;
-        Name nodeTypeName = definitionId.getNodeTypeName();
-        JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
-        return nodeType.getChildNodeDefinition(definitionId.getChildDefinitionName());
+        return repositoryTypeManager.getChildNodeDefinition(definitionId);
     }
 
     /**
      * Get the property definition given the supplied identifier.
      * 
      * @param definitionId the identifier of the node definition
-     * @param prefersMultiValued true if the property should be a multi-valued, or false if it should be single-valued
-     * @return the node definition, or null if there is no such definition (or if the ID was null)
+     * @return the property definition, or null if there is no such definition (or if the ID was null)
      */
-    JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId,
-                                                 boolean prefersMultiValued ) {
+    JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId ) {
         if (definitionId == null) return null;
-        Name nodeTypeName = definitionId.getNodeTypeName();
-        JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
-        return nodeType.getPropertyDefinition(definitionId.getPropertyDefinitionName(), prefersMultiValued);
+        return repositoryTypeManager.getPropertyDefinition(definitionId);
     }
 
+    /**
+     * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
+     * given property name, property type, and value.
+     * <p>
+     * This method first attempts to find a single-valued property definition with the supplied property name and
+     * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
+     * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
+     * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints}. Otherwise,
+     * the process continues with each of the mixin types, in the order they are named.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
+     * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
+     * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
+     * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
+     * definition's property type and still satisfy the definition's constraints.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
+     * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
+     * name}).
+     * </p>
+     * <p>
+     * Finally, if no satisfactory property definition could be found, this method returns null.
+     * </p>
+     * 
+     * @param primaryTypeName the name of the primary type; may not be null
+     * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
+     * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
+     *        look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
+     *        residual property definition (if any).
+     * @param value the value, or null if the property is being removed
+     * @param checkMultiValuedDefinitions true if the type's multi-valued property definitions should be considered, or false if
+     *        only single-value property definitions should be considered
+     * @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 best property definition, or <code>null</code> if no property definition allows the property with the supplied
+     *         name, type and number of values
+     */
+    final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
+                                                        List<Name> mixinTypeNames,
+                                                        Name propertyName,
+                                                        Value value,
+                                                        boolean checkMultiValuedDefinitions,
+                                                        boolean skipProtected ) {
+        return repositoryTypeManager.findPropertyDefinition(primaryTypeName,
+                                                            mixinTypeNames,
+                                                            propertyName,
+                                                            value,
+                                                            checkMultiValuedDefinitions,
+                                                            skipProtected);
+    }
+
+    /**
+     * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
+     * given property name, property type, and value. with the given name and property type that allows the supplied values.
+     * <p>
+     * This method first attempts to find a single-valued property definition with the supplied property name and
+     * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
+     * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
+     * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints}. Otherwise,
+     * the process continues with each of the mixin types, in the order they are named.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
+     * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
+     * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
+     * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
+     * definition's property type and still satisfy the definition's constraints.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
+     * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
+     * name}).
+     * </p>
+     * <p>
+     * Finally, if no satisfactory property definition could be found, this method returns null.
+     * </p>
+     * 
+     * @param primaryTypeName the name of the primary type; may not be null
+     * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
+     * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
+     *        look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
+     *        residual property definition (if any).
+     * @param values the 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 best property definition, or <code>null</code> if no property definition allows the property with the supplied
+     *         name, type and number of values
+     */
+    final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
+                                                        List<Name> mixinTypeNames,
+                                                        Name propertyName,
+                                                        Value[] values,
+                                                        boolean skipProtected ) {
+        return repositoryTypeManager.findPropertyDefinition(primaryTypeName, mixinTypeNames, propertyName, values, skipProtected);
+    }
+
+    /**
+     * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied
+     * name to be removed.
+     * 
+     * @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
+     *        to include in the search
+     * @param propertyName the name of the property to be removed; may not be null
+     * @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 true if at least one child node definition does not require children with the supplied name to exist, or false
+     *         otherwise
+     */
+    boolean canRemoveProperty( Name primaryTypeNameOfParent,
+                               List<Name> mixinTypeNamesOfParent,
+                               Name propertyName,
+                               boolean skipProtected ) {
+        return repositoryTypeManager.canRemoveProperty(primaryTypeNameOfParent,
+                                                       mixinTypeNamesOfParent,
+                                                       propertyName,
+                                                       skipProtected);
+    }
+
+    /**
+     * Searches the supplied primary node type and the mixin node types of a parent node for a child node definition that is the
+     * best match for a new child with the given name, primary node type name, and whether there are existing children with the
+     * same name.
+     * 
+     * @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
+     *        to include in the search
+     * @param childName the name of the child to be added to the parent; may not be null
+     * @param childPrimaryNodeType the name of the primary node type for the child node, or null if the primary type is not known
+     *        and the {@link NodeDefinition#getDefaultPrimaryType() definition's default primary type} will be used
+     * @param numberOfExistingChildrenWithSameName the number of existing children with the same name as the child to be added, or
+     *        0 if this new child will be the first child with this name (or if the number of children is not known)
+     * @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 best child node definition, or <code>null</code> if no node definition allows a new child with the supplied
+     *         name, primary type, and whether there are already children with the same name
+     */
+    final JcrNodeDefinition findChildNodeDefinition( Name primaryTypeNameOfParent,
+                                                     List<Name> mixinTypeNamesOfParent,
+                                                     Name childName,
+                                                     Name childPrimaryNodeType,
+                                                     int numberOfExistingChildrenWithSameName,
+                                                     boolean skipProtected ) {
+        return repositoryTypeManager.findChildNodeDefinition(primaryTypeNameOfParent,
+                                                             mixinTypeNamesOfParent,
+                                                             childName,
+                                                             childPrimaryNodeType,
+                                                             numberOfExistingChildrenWithSameName,
+                                                             skipProtected);
+    }
+
+    /**
+     * 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.
+     * 
+     * @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
+     *        to include in the search
+     * @param childName the name of the child to be added to the parent; may not be null
+     * @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 true if at least one child node definition does not require children with the supplied name to exist, or false
+     *         otherwise
+     */
+    final boolean canRemoveAllChildren( Name primaryTypeNameOfParent,
+                                        List<Name> mixinTypeNamesOfParent,
+                                        Name childName,
+                                        boolean skipProtected ) {
+        return repositoryTypeManager.canRemoveAllChildren(primaryTypeNameOfParent,
+                                                          mixinTypeNamesOfParent,
+                                                          childName,
+                                                          skipProtected);
+    }
+
 }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -82,7 +82,8 @@
      */
     public PropertyDefinitionId getId() {
         if (id == null) {
-            id = new PropertyDefinitionId(declaringNodeType.getInternalName(), name);
+            // This is idempotent, so no need to lock
+            id = new PropertyDefinitionId(this.declaringNodeType.getInternalName(), this.name, this.requiredType, this.multiple);
         }
         return id;
     }
@@ -137,9 +138,18 @@
                                          this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
     }
 
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getId().toString();
+    }
+
     boolean satisfiesConstraints( Value value ) {
-        assert value instanceof JcrValue;
-
+        if (value == null) return false;
         if (valueConstraints == null || valueConstraints.length == 0) {
             return true;
         }
@@ -166,7 +176,91 @@
         }
     }
 
+    boolean satisfiesConstraints( Value[] values ) {
+        if (valueConstraints == null || valueConstraints.length == 0) {
+            if (requiredType != PropertyType.UNDEFINED) {
+                for (Value value : values) {
+                    if (value.getType() != requiredType) return false;
+                }
+            }
+            return true;
+        }
+        if (values == null || values.length == 0) {
+            // There are no values, so see if the definition allows multiple values ...
+            return isMultiple();
+        }
+
+        // Neither the 1.0 or 2.0 specification formally prohibit constraints on properties with no required type.
+        int type = requiredType == PropertyType.UNDEFINED ? values[0].getType() : requiredType;
+
+        /*
+         * Keep a method-local reference to the constraint checker in case another thread attempts to concurrently
+         * check the constraints with a different required type.
+         */
+        ConstraintChecker checker = this.checker;
+
+        if (checker == null || checker.getType() != type) {
+            checker = createChecker(context, type, valueConstraints);
+            this.checker = checker;
+        }
+
+        try {
+            for (Value value : values) {
+                if (requiredType != PropertyType.UNDEFINED && value.getType() != requiredType) return false;
+                if (!checker.matches(value)) return false;
+            }
+            return true;
+        } catch (ValueFormatException vfe) {
+            // The value was so wonky that we couldn't even convert it to an appropriate type
+            return false;
+        }
+    }
+
     /**
+     * Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
+     * conversion rules in section 6.2.6 of the JCR 1.0 specification AND <code>value</code> satisfies the constraints (if any)
+     * for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
+     * will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
+     * type specified in <code>value.getType()</code>.
+     * 
+     * @param value the value to be validated
+     * @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
+     *         satisfies the constraints for the property (if any exist).
+     * @see PropertyDefinition#getValueConstraints()
+     * @see #satisfiesConstraints(Value)
+     */
+    boolean canCastToTypeAndSatisfyConstraints( Value value ) {
+        try {
+            assert value instanceof JcrValue : "Illegal implementation of Value interface";
+            ((JcrValue)value).asType(getRequiredType()); // throws ValueFormatException if there's a problem
+            return satisfiesConstraints(value);
+        } catch (javax.jcr.ValueFormatException vfe) {
+            // Cast failed
+            return false;
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
+     * conversion rules in section 6.2.6 of the JCR 1.0 specification AND <code>value</code> satisfies the constraints (if any)
+     * for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
+     * will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
+     * type specified in <code>value.getType()</code>.
+     * 
+     * @param values the values to be validated
+     * @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
+     *         satisfies the constraints for the property (if any exist).
+     * @see PropertyDefinition#getValueConstraints()
+     * @see #satisfiesConstraints(Value)
+     */
+    boolean canCastToTypeAndSatisfyConstraints( Value[] values ) {
+        for (Value value : values) {
+            if (!canCastToTypeAndSatisfyConstraints(value)) return false;
+        }
+        return true;
+    }
+
+    /**
      * Returns a {@link ConstraintChecker} that will interpret the constraints described by <code>valueConstraints</code> using
      * the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type indicated by <code>type</code> (where
      * <code>type</code> is a value from {@link PropertyType}) for the given <code>context</code>. The {@link ExecutionContext} is
@@ -481,7 +575,12 @@
 
             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)) {

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -25,7 +25,6 @@
 
 import java.io.Serializable;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
@@ -51,7 +50,8 @@
 
     private final Name nodeTypeName;
     private final Name childDefinitionName;
-    private final int hc;
+    private final Name[] requiredPrimaryTypes;
+    private final String stringVersion;
 
     /**
      * Create an identifier for a node definition.
@@ -59,14 +59,23 @@
      * @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 childDefinitionName,
+                             Name[] requiredPrimaryTypes ) {
         assert nodeTypeName != null;
         assert childDefinitionName != null;
         this.nodeTypeName = nodeTypeName;
         this.childDefinitionName = childDefinitionName;
-        this.hc = HashCode.compute(this.nodeTypeName, this.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.stringVersion = sb.toString();
     }
 
     /**
@@ -88,6 +97,15 @@
     }
 
     /**
+     * @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
@@ -103,7 +121,7 @@
      * @return the string form
      */
     public String getString() {
-        return this.nodeTypeName.getString() + '/' + this.childDefinitionName.getString();
+        return this.stringVersion;
     }
 
     /**
@@ -116,12 +134,16 @@
      */
     public static NodeDefinitionId fromString( String definition,
                                                NameFactory factory ) {
-        int index = definition.indexOf('/');
-        String nodeTypeNameString = definition.substring(0, index);
-        String childDefinitionNameString = definition.substring(index + 1);
+        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);
+        return new NodeDefinitionId(nodeTypeName, childDefinitionName, requiredPrimaryTypes);
     }
 
     /**
@@ -131,7 +153,7 @@
      */
     @Override
     public int hashCode() {
-        return hc;
+        return stringVersion.hashCode();
     }
 
     /**
@@ -144,9 +166,7 @@
         if (obj == this) return true;
         if (obj instanceof NodeDefinitionId) {
             NodeDefinitionId that = (NodeDefinitionId)obj;
-            if (this.hc != that.hc) return false;
-            if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
-            return this.childDefinitionName.equals(that.childDefinitionName);
+            return this.stringVersion.equals(that.stringVersion);
         }
         return false;
     }
@@ -158,7 +178,7 @@
      */
     @Override
     public String toString() {
-        return this.nodeTypeName.toString() + '/' + this.childDefinitionName.toString();
+        return this.stringVersion;
     }
 
 }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -24,8 +24,8 @@
 package org.jboss.dna.jcr;
 
 import java.io.Serializable;
+import javax.jcr.PropertyType;
 import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
@@ -51,7 +51,9 @@
 
     private final Name nodeTypeName;
     private final Name propertyDefinitionName;
-    private final int hc;
+    private final int propertyType;
+    private final boolean allowsMultiple;
+    private final String stringVersion;
 
     /**
      * Create a new identifier for a propety definition.
@@ -59,12 +61,20 @@
      * @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 ) {
+                                 Name propertyDefinitionName,
+                                 int propertyType,
+                                 boolean allowsMultiple ) {
         this.nodeTypeName = nodeTypeName;
         this.propertyDefinitionName = propertyDefinitionName;
-        this.hc = HashCode.compute(this.nodeTypeName, this.propertyDefinitionName);
+        this.propertyType = propertyType;
+        this.allowsMultiple = allowsMultiple;
+        this.stringVersion = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
+                             + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
     }
 
     /**
@@ -86,6 +96,24 @@
     }
 
     /**
+     * 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
@@ -101,7 +129,7 @@
      * @return the string form
      */
     public String getString() {
-        return this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString();
+        return this.stringVersion;
     }
 
     /**
@@ -114,14 +142,24 @@
      */
     public static PropertyDefinitionId fromString( String definition,
                                                    NameFactory factory ) {
-        int index = definition.indexOf('/');
-        String nodeTypeNameString = definition.substring(0, index);
-        String propertyDefinitionNameString = definition.substring(index + 1);
+        String[] parts = definition.split("/");
+        String nodeTypeNameString = parts[0];
+        String propertyDefinitionNameString = parts[1];
         Name nodeTypeName = factory.create(nodeTypeNameString);
         Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
-        return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName);
+        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}
      * 
@@ -129,7 +167,7 @@
      */
     @Override
     public int hashCode() {
-        return hc;
+        return this.stringVersion.hashCode();
     }
 
     /**
@@ -142,9 +180,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyDefinitionId) {
             PropertyDefinitionId that = (PropertyDefinitionId)obj;
-            if (this.hc != that.hc) return false;
-            if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
-            return this.propertyDefinitionName.equals(that.propertyDefinitionName);
+            return this.stringVersion.equals(that.stringVersion);
         }
         return false;
     }
@@ -156,7 +192,7 @@
      */
     @Override
     public String toString() {
-        return this.nodeTypeName.toString() + '/' + this.propertyDefinitionName.toString();
+        return this.stringVersion;
     }
 
 }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -25,7 +25,13 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
@@ -34,9 +40,9 @@
  * The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
  * repository manager maintains a list of all node types and the ability to retrieve node types by {@link Name}. </p> The JCR 1.0
  * and 2.0 specifications both require that node type information be shared across all sessions within a repository and that the
- * {@link javax.jcr.nodetype.NodeTypeManager} perform operations based on the string versions of {@link Name}s based on the permanent
- * (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by maintaining a single master
- * repository of all node type information (the {@link RepositoryNodeTypeManager}) and per-session wrappers (
+ * {@link javax.jcr.nodetype.NodeTypeManager} perform operations based on the string versions of {@link Name}s based on the
+ * permanent (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by maintaining a single
+ * master repository of all node type information (the {@link RepositoryNodeTypeManager}) and per-session wrappers (
  * {@link JcrNodeTypeManager}) for this master repository that perform {@link String} to {@link Name} translation based on the
  * {@link javax.jcr.Session}'s transient mappings and then delegating node type lookups to the repository manager.
  */
@@ -45,20 +51,36 @@
 
     private final Map<Name, JcrNodeType> primaryNodeTypes;
     private final Map<Name, JcrNodeType> mixinNodeTypes;
+    private final Map<PropertyDefinitionId, JcrPropertyDefinition> propertyDefinitions;
+    private final Map<NodeDefinitionId, JcrNodeDefinition> childNodeDefinitions;
 
     RepositoryNodeTypeManager( ExecutionContext context,
                                JcrNodeTypeSource source ) {
         Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
         Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+        propertyDefinitions = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
+        childNodeDefinitions = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
 
         primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
         for (JcrNodeType nodeType : primary) {
             primaryNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+            for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
+                childNodeDefinitions.put(childDefinition.getId(), childDefinition);
+            }
+            for (JcrPropertyDefinition propertyDefinition : nodeType.propertyDefinitions()) {
+                propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
+            }
         }
 
         mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
         for (JcrNodeType nodeType : mixins) {
             mixinNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+            for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
+                childNodeDefinitions.put(childDefinition.getId(), childDefinition);
+            }
+            for (JcrPropertyDefinition propertyDefinition : nodeType.propertyDefinitions()) {
+                propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
+            }
         }
     }
 
@@ -70,6 +92,14 @@
         return primaryNodeTypes.values();
     }
 
+    public JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId id ) {
+        return propertyDefinitions.get(id);
+    }
+
+    public JcrNodeDefinition getChildNodeDefinition( NodeDefinitionId id ) {
+        return childNodeDefinitions.get(id);
+    }
+
     JcrNodeType getNodeType( Name nodeTypeName ) {
 
         JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
@@ -78,4 +108,491 @@
         }
         return nodeType;
     }
+
+    /**
+     * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
+     * given property name, property type, and value.
+     * <p>
+     * This method first attempts to find a single-valued property definition with the supplied property name and
+     * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
+     * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
+     * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints}. Otherwise,
+     * the process continues with each of the mixin types, in the order they are named.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
+     * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
+     * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
+     * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
+     * definition's property type and still satisfy the definition's constraints.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
+     * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
+     * name}).
+     * </p>
+     * <p>
+     * Finally, if no satisfactory property definition could be found, this method returns null.
+     * </p>
+     * 
+     * @param primaryTypeName the name of the primary type; may not be null
+     * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
+     * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
+     *        look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
+     *        residual property definition (if any).
+     * @param value the value, or null if the property is being removed
+     * @param checkMultiValuedDefinitions true if the type's multi-valued property definitions should be considered, or false if
+     *        only single-value property definitions should be considered
+     * @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 best property definition, or <code>null</code> if no property definition allows the property with the supplied
+     *         name, type and number of values
+     */
+    JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
+                                                  List<Name> mixinTypeNames,
+                                                  Name propertyName,
+                                                  Value value,
+                                                  boolean checkMultiValuedDefinitions,
+                                                  boolean skipProtected ) {
+        boolean setToEmpty = value == null;
+
+        // Look for a single-value property definition on the primary type that matches by name and type ...
+        JcrNodeType primaryType = getNodeType(primaryTypeName);
+        if (primaryType != null) {
+            for (JcrPropertyDefinition definition : primaryType.allSingleValuePropertyDefinitions(propertyName)) {
+                // See if the definition allows the value ...
+                if (skipProtected && definition.isProtected()) continue;
+                if (setToEmpty) {
+                    if (!definition.isMandatory()) return definition;
+                    // Otherwise this definition doesn't work, so continue with the next ...
+                    continue;
+                }
+                assert value != null;
+                // We can use the definition if it matches the type and satisfies the constraints ...
+                int type = definition.getRequiredType();
+                if ((type == PropertyType.UNDEFINED || type == value.getType()) && definition.satisfiesConstraints(value)) return definition;
+            }
+        }
+
+        // Look for a single-value property definition on the mixin types that matches by name and type ...
+        List<JcrNodeType> mixinTypes = null;
+        if (mixinTypeNames != null && !mixinTypeNames.isEmpty()) {
+            mixinTypes = new LinkedList<JcrNodeType>();
+            for (Name mixinTypeName : mixinTypeNames) {
+                JcrNodeType mixinType = getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                mixinTypes.add(mixinType);
+                for (JcrPropertyDefinition definition : mixinType.allSingleValuePropertyDefinitions(propertyName)) {
+                    // See if the definition allows the value ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    if (setToEmpty) {
+                        if (!definition.isMandatory()) return definition;
+                        // Otherwise this definition doesn't work, so continue with the next ...
+                        continue;
+                    }
+                    assert value != null;
+                    // We can use the definition if it matches the type and satisfies the constraints ...
+                    int type = definition.getRequiredType();
+                    if ((type == PropertyType.UNDEFINED || type == value.getType()) && definition.satisfiesConstraints(value)) return definition;
+                }
+            }
+        }
+
+        if (checkMultiValuedDefinitions) {
+            // Look for a multi-value property definition on the primary type that matches by name and type ...
+            if (primaryType != null) {
+                for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
+                    // See if the definition allows the value ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    if (setToEmpty) {
+                        if (!definition.isMandatory()) return definition;
+                        // Otherwise this definition doesn't work, so continue with the next ...
+                        continue;
+                    }
+                    assert value != null;
+                    // We can use the definition if it matches the type and satisfies the constraints ...
+                    int type = definition.getRequiredType();
+                    if ((type == PropertyType.UNDEFINED || type == value.getType()) && definition.satisfiesConstraints(value)) return definition;
+                }
+            }
+
+            // Look for a multi-value property definition on the mixin types that matches by name and type ...
+            if (mixinTypes != null) {
+                for (JcrNodeType mixinType : mixinTypes) {
+                    for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
+                        // See if the definition allows the value ...
+                        if (skipProtected && definition.isProtected()) continue;
+                        if (setToEmpty) {
+                            if (!definition.isMandatory()) return definition;
+                            // Otherwise this definition doesn't work, so continue with the next ...
+                            continue;
+                        }
+                        assert value != null;
+                        // We can use the definition if it matches the type and satisfies the constraints ...
+                        int type = definition.getRequiredType();
+                        if ((type == PropertyType.UNDEFINED || type == value.getType()) && definition.satisfiesConstraints(value)) return definition;
+                    }
+                }
+            }
+        }
+
+        if (value != null) {
+            // Nothing was found with matching name and type, so look for definitions with
+            // matching name and an undefined or castable type ...
+
+            // Look for a single-value property definition on the primary type that matches by name ...
+            if (primaryType != null) {
+                for (JcrPropertyDefinition definition : primaryType.allSingleValuePropertyDefinitions(propertyName)) {
+                    // See if the definition allows the value ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                    if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
+                }
+            }
+
+            // Look for a single-value property definition on the mixin types that matches by name ...
+            if (mixinTypes != null) {
+                for (JcrNodeType mixinType : mixinTypes) {
+                    for (JcrPropertyDefinition definition : mixinType.allSingleValuePropertyDefinitions(propertyName)) {
+                        // See if the definition allows the value ...
+                        if (skipProtected && definition.isProtected()) continue;
+                        assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                        if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
+                    }
+                }
+            }
+
+            if (checkMultiValuedDefinitions) {
+                // Look for a multi-value property definition on the primary type that matches by name ...
+                if (primaryType != null) {
+                    for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
+                        // See if the definition allows the value ...
+                        if (skipProtected && definition.isProtected()) continue;
+                        assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                        if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
+                    }
+                }
+
+                // Look for a multi-value property definition on the mixin types that matches by name ...
+                if (mixinTypes != null) {
+                    for (JcrNodeType mixinType : mixinTypes) {
+                        for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
+                            // See if the definition allows the value ...
+                            if (skipProtected && definition.isProtected()) continue;
+                            assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                            if (definition.canCastToTypeAndSatisfyConstraints(value)) return definition;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Nothing was found, so look for residual property definitions ...
+        if (!propertyName.equals(JcrNodeType.RESIDUAL_NAME)) return findPropertyDefinition(primaryTypeName,
+                                                                                           mixinTypeNames,
+                                                                                           JcrNodeType.RESIDUAL_NAME,
+                                                                                           value,
+                                                                                           checkMultiValuedDefinitions,
+                                                                                           skipProtected);
+        return null;
+    }
+
+    /**
+     * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
+     * given property name, property type, and value.
+     * <p>
+     * This method first attempts to find a single-valued property definition with the supplied property name and
+     * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
+     * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
+     * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints}. Otherwise,
+     * the process continues with each of the mixin types, in the order they are named.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
+     * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
+     * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
+     * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
+     * definition's property type and still satisfy the definition's constraints.
+     * </p>
+     * <p>
+     * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
+     * </p>
+     * <p>
+     * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
+     * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
+     * name}).
+     * </p>
+     * <p>
+     * Finally, if no satisfactory property definition could be found, this method returns null.
+     * </p>
+     * 
+     * @param primaryTypeName the name of the primary type; may not be null
+     * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
+     * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
+     *        look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
+     *        residual property definition (if any).
+     * @param values the 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 best property definition, or <code>null</code> if no property definition allows the property with the supplied
+     *         name, type and number of values
+     */
+    JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
+                                                  List<Name> mixinTypeNames,
+                                                  Name propertyName,
+                                                  Value[] values,
+                                                  boolean skipProtected ) {
+        boolean setToEmpty = values == null || values.length == 0;
+        int propertyType = values == null || values.length == 0 ? PropertyType.STRING : values[0].getType();
+
+        // Look for a multi-value property definition on the primary type that matches by name and type ...
+        JcrNodeType primaryType = getNodeType(primaryTypeName);
+        if (primaryType != null) {
+            for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
+                // See if the definition allows the value ...
+                if (skipProtected && definition.isProtected()) continue;
+                if (setToEmpty) {
+                    if (!definition.isMandatory()) return definition;
+                    // Otherwise this definition doesn't work, so continue with the next ...
+                    continue;
+                }
+                assert values != null;
+                assert values.length != 0;
+                // We can use the definition if it matches the type and satisfies the constraints ...
+                int type = definition.getRequiredType();
+                if ((type == PropertyType.UNDEFINED || type == propertyType) && definition.satisfiesConstraints(values)) return definition;
+            }
+        }
+
+        // Look for a multi-value property definition on the mixin types that matches by name and type ...
+        List<JcrNodeType> mixinTypes = null;
+        if (mixinTypeNames != null && !mixinTypeNames.isEmpty()) {
+            mixinTypes = new LinkedList<JcrNodeType>();
+            for (Name mixinTypeName : mixinTypeNames) {
+                JcrNodeType mixinType = getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                mixinTypes.add(mixinType);
+                for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
+                    // See if the definition allows the value ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    if (setToEmpty) {
+                        if (!definition.isMandatory()) return definition;
+                        // Otherwise this definition doesn't work, so continue with the next ...
+                        continue;
+                    }
+                    assert values != null;
+                    assert values.length != 0;
+                    // We can use the definition if it matches the type and satisfies the constraints ...
+                    int type = definition.getRequiredType();
+                    if ((type == PropertyType.UNDEFINED || type == propertyType) && definition.satisfiesConstraints(values)) return definition;
+                }
+            }
+        }
+
+        if (values != null && values.length != 0) {
+            // Nothing was found with matching name and type, so look for definitions with
+            // matching name and an undefined or castable type ...
+
+            // Look for a multi-value property definition on the primary type that matches by name and type ...
+            if (primaryType != null) {
+                for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
+                    // See if the definition allows the value ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                    if (definition.canCastToTypeAndSatisfyConstraints(values)) return definition;
+                }
+            }
+
+            // Look for a multi-value property definition on the mixin types that matches by name and type ...
+            if (mixinTypes != null) {
+                for (JcrNodeType mixinType : mixinTypes) {
+                    for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
+                        // See if the definition allows the value ...
+                        if (skipProtected && definition.isProtected()) continue;
+                        assert definition.getRequiredType() != PropertyType.UNDEFINED;
+                        if (definition.canCastToTypeAndSatisfyConstraints(values)) return definition;
+                    }
+                }
+            }
+        }
+
+        // Nothing was found, so look for residual property definitions ...
+        if (!propertyName.equals(JcrNodeType.RESIDUAL_NAME)) return findPropertyDefinition(primaryTypeName,
+                                                                                           mixinTypeNames,
+                                                                                           JcrNodeType.RESIDUAL_NAME,
+                                                                                           values,
+                                                                                           skipProtected);
+        return null;
+    }
+
+    /**
+     * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied
+     * name to be removed.
+     * 
+     * @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
+     *        to include in the search
+     * @param propertyName the name of the property to be removed; may not be null
+     * @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 true if at least one child node definition does not require children with the supplied name to exist, or false
+     *         otherwise
+     */
+    boolean canRemoveProperty( Name primaryTypeNameOfParent,
+                               List<Name> mixinTypeNamesOfParent,
+                               Name propertyName,
+                               boolean skipProtected ) {
+        // First look in the primary type ...
+        JcrNodeType primaryType = getNodeType(primaryTypeNameOfParent);
+        if (primaryType != null) {
+            for (JcrPropertyDefinition definition : primaryType.allPropertyDefinitions(propertyName)) {
+                // Skip protected definitions ...
+                if (skipProtected && definition.isProtected()) continue;
+                // If this definition is not mandatory, then we have found that we CAN remove the property ...
+                if (!definition.isMandatory()) return true;
+            }
+        }
+
+        // Then, look in the mixin types ...
+        if (mixinTypeNamesOfParent != null && !mixinTypeNamesOfParent.isEmpty()) {
+            for (Name mixinTypeName : mixinTypeNamesOfParent) {
+                JcrNodeType mixinType = getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                for (JcrPropertyDefinition definition : mixinType.allPropertyDefinitions(propertyName)) {
+                    // Skip protected definitions ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    // If this definition is not mandatory, then we have found that we CAN remove the property ...
+                    if (!definition.isMandatory()) return true;
+                }
+            }
+        }
+
+        // Nothing was found, so look for residual node definitions ...
+        if (!propertyName.equals(JcrNodeType.RESIDUAL_NAME)) return canRemoveProperty(primaryTypeNameOfParent,
+                                                                                      mixinTypeNamesOfParent,
+                                                                                      JcrNodeType.RESIDUAL_NAME,
+                                                                                      skipProtected);
+        return false;
+    }
+
+    /**
+     * Searches the supplied primary node type and the mixin node types of a parent node for a child node definition that is the
+     * best match for a new child with the given name, primary node type name, and whether there are existing children with the
+     * same name.
+     * 
+     * @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
+     *        to include in the search
+     * @param childName the name of the child to be added to the parent; may not be null
+     * @param childPrimaryNodeType the name of the primary node type for the child node, or null if the primary type is not known
+     *        and the {@link NodeDefinition#getDefaultPrimaryType() definition's default primary type} will be used
+     * @param numberOfExistingChildrenWithSameName the number of existing children with the same name as the child to be added, or
+     *        0 if this new child will be the first child with this name (or if the number of children is not known)
+     * @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 best child node definition, or <code>null</code> if no node definition allows a new child with the supplied
+     *         name, primary type, and whether there are already children with the same name
+     */
+    JcrNodeDefinition findChildNodeDefinition( Name primaryTypeNameOfParent,
+                                               List<Name> mixinTypeNamesOfParent,
+                                               Name childName,
+                                               Name childPrimaryNodeType,
+                                               int numberOfExistingChildrenWithSameName,
+                                               boolean skipProtected ) {
+        JcrNodeType childType = childPrimaryNodeType != null ? getNodeType(childPrimaryNodeType) : null;
+        boolean requireSns = numberOfExistingChildrenWithSameName != 0;
+
+        // First look in the primary type ...
+        JcrNodeType primaryType = getNodeType(primaryTypeNameOfParent);
+        if (primaryType != null) {
+            for (JcrNodeDefinition definition : primaryType.allChildNodeDefinitions(childName, requireSns)) {
+                // Skip protected definitions ...
+                if (skipProtected && definition.isProtected()) continue;
+                // See if the definition allows a child with the supplied primary type ...
+                if (definition.allowsChildWithType(childType)) return definition;
+            }
+        }
+
+        // Then, look in the mixin types ...
+        if (mixinTypeNamesOfParent != null && !mixinTypeNamesOfParent.isEmpty()) {
+            for (Name mixinTypeName : mixinTypeNamesOfParent) {
+                JcrNodeType mixinType = getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                for (JcrNodeDefinition definition : mixinType.allChildNodeDefinitions(childName, requireSns)) {
+                    // Skip protected definitions ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    // See if the definition allows a child with the supplied primary type ...
+                    if (definition.allowsChildWithType(childType)) return definition;
+                }
+            }
+        }
+
+        // Nothing was found, so look for residual node definitions ...
+        if (!childName.equals(JcrNodeType.RESIDUAL_NAME)) return findChildNodeDefinition(primaryTypeNameOfParent,
+                                                                                         mixinTypeNamesOfParent,
+                                                                                         JcrNodeType.RESIDUAL_NAME,
+                                                                                         childPrimaryNodeType,
+                                                                                         numberOfExistingChildrenWithSameName,
+                                                                                         skipProtected);
+        return null;
+    }
+
+    /**
+     * 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.
+     * 
+     * @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
+     *        to include in the search
+     * @param childName the name of the child to be added to the parent; may not be null
+     * @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 true if at least one child node definition does not require children with the supplied name to exist, or false
+     *         otherwise
+     */
+    boolean canRemoveAllChildren( Name primaryTypeNameOfParent,
+                                  List<Name> mixinTypeNamesOfParent,
+                                  Name childName,
+                                  boolean skipProtected ) {
+        // First look in the primary type ...
+        JcrNodeType primaryType = getNodeType(primaryTypeNameOfParent);
+        if (primaryType != null) {
+            for (JcrNodeDefinition definition : primaryType.allChildNodeDefinitions(childName)) {
+                // Skip protected definitions ...
+                if (skipProtected && definition.isProtected()) continue;
+                // If this definition is not mandatory, then we have found that we CAN remove all children ...
+                if (!definition.isMandatory()) return true;
+            }
+        }
+
+        // Then, look in the mixin types ...
+        if (mixinTypeNamesOfParent != null && !mixinTypeNamesOfParent.isEmpty()) {
+            for (Name mixinTypeName : mixinTypeNamesOfParent) {
+                JcrNodeType mixinType = getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                for (JcrNodeDefinition definition : mixinType.allChildNodeDefinitions(childName)) {
+                    // Skip protected definitions ...
+                    if (skipProtected && definition.isProtected()) continue;
+                    // If this definition is not mandatory, then we have found that we CAN remove all children ...
+                    if (!definition.isMandatory()) return true;
+                }
+            }
+        }
+
+        // Nothing was found, so look for residual node definitions ...
+        if (!childName.equals(JcrNodeType.RESIDUAL_NAME)) return canRemoveAllChildren(primaryTypeNameOfParent,
+                                                                                      mixinTypeNamesOfParent,
+                                                                                      JcrNodeType.RESIDUAL_NAME,
+                                                                                      skipProtected);
+        return 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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -27,7 +27,6 @@
 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;
@@ -43,10 +42,10 @@
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.Value;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NodeDefinition;
 import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.graph.ExecutionContext;
@@ -432,14 +431,16 @@
          * existing values will be replaced with the supplied value.
          * 
          * @param name the property name; may not be null
+         * @param propertyType the property type; must be a valid {@link PropertyType} value
          * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
          */
         public void setProperty( Name name,
+                                 int propertyType,
                                  Object value ) throws ConstraintViolationException {
             Property dnaProp = propertyFactory.create(name, value);
-            setProperty(name, dnaProp);
+            setProperty(name, dnaProp, propertyType);
         }
 
         /**
@@ -447,46 +448,59 @@
          * existing values will be replaced with those that are supplied.
          * 
          * @param name the property name; may not be null
+         * @param propertyType the property type; must be a valid {@link PropertyType} value
          * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
          */
         public void setProperty( Name name,
+                                 int propertyType,
                                  Object[] value ) throws ConstraintViolationException {
             Property dnaProp = propertyFactory.create(name, value);
-            setProperty(name, dnaProp);
+            setProperty(name, dnaProp, propertyType);
         }
 
         protected final void setProperty( Name name,
-                                          Property dnaProp ) throws ConstraintViolationException {
+                                          Property dnaProp,
+                                          int propertyType ) throws ConstraintViolationException {
+            assert propertyType != PropertyType.UNDEFINED;
             PropertyInfo existing = node.getProperty(name);
             PropertyInfo newProperty = null;
             if (existing != null) {
                 // We're replacing an existing property, but we still need to check that the property definition
                 // defines a type. So, find the property definition for the existing property ...
-                JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId(),
-                                                                                     existing.isMultiValued());
-
-                // Look at the required property type ...
-                int propertyType = definition.getRequiredType();
-                if (propertyType == PropertyType.UNDEFINED) {
-                    // We need set the new type to that defined by the values ...
-                    propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+                JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId());
+                if (definition == null && existing.getDefinitionId().allowsMultiple() && dnaProp.isSingle()) {
+                    // Look for a single-valued definition ...
+                    PropertyDefinitionId id = existing.getDefinitionId().asSingleValued();
+                    definition = nodeTypes().getPropertyDefinition(id);
                 }
+                if (definition == null) {
+                    // Try to find a different (but existing) property definition ...
+                    definition = findBestPropertyDefintion(node.getPrimaryTypeName(),
+                                                           node.getMixinTypeNames(),
+                                                           dnaProp,
+                                                           propertyType,
+                                                           true);
+                }
+                if (definition == null) {
+                    throw new ConstraintViolationException();
+                }
 
-                // Csreate the property info ...
+                // Create the property info ...
                 newProperty = new PropertyInfo(existing.getPropertyId(), existing.getDefinitionId(), propertyType, dnaProp,
                                                existing.isMultiValued());
             } else {
                 // It's a new property ...
                 PropertyId id = new PropertyId(node.getUuid(), name);
                 // Look find the property definition to use ...
-                JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProp);
-
-                // Figure out the property type ...
-                int propertyType = definition.getRequiredType();
-                if (propertyType == PropertyType.UNDEFINED) {
-                    propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+                JcrPropertyDefinition definition = findBestPropertyDefintion(node.getPrimaryTypeName(),
+                                                                             node.getMixinTypeNames(),
+                                                                             dnaProp,
+                                                                             propertyType,
+                                                                             true);
+                if (definition == null) {
+                    throw new ConstraintViolationException();
                 }
                 // Create the property info ...
                 newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
@@ -545,11 +559,19 @@
             // 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 = findBestChildNodeDefinition(childName, numSns);
+            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 {
-                    setProperty(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
+                    setProperty(DnaLexicon.NODE_DEFINITON, PropertyType.STRING, definition.getId().getString());
                 } 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.
@@ -597,7 +619,15 @@
 
             // Verify that this node accepts a child of the supplied name (given any existing SNS nodes) ...
             int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(name);
-            JcrNodeDefinition definition = findBestChildNodeDefinition(name, numSns);
+            JcrNodeDefinition definition = nodeTypes().findChildNodeDefinition(node.getPrimaryTypeName(),
+                                                                               node.getMixinTypeNames(),
+                                                                               name,
+                                                                               primaryTypeName,
+                                                                               numSns,
+                                                                               true);
+            if (definition == null) {
+                throw new ConstraintViolationException();
+            }
 
             ChildNode result = node.addChild(name, desiredUuid, pathFactory);
 
@@ -613,14 +643,22 @@
             Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, nodeDefinitionId.getString());
 
             // Create the property info for the "jcr:primaryType" child property ...
-            JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(primaryTypeProp, primaryTypeName);
+            JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(node.getPrimaryTypeName(),
+                                                                              node.getMixinTypeNames(),
+                                                                              primaryTypeProp,
+                                                                              PropertyType.NAME,
+                                                                              false);
             PropertyDefinitionId primaryTypeDefinitionId = primaryTypeDefn.getId();
             PropertyInfo primaryTypeInfo = new PropertyInfo(new PropertyId(desiredUuid, primaryTypeProp.getName()),
                                                             primaryTypeDefinitionId, PropertyType.NAME, primaryTypeProp, false);
             properties.put(primaryTypeProp.getName(), primaryTypeInfo);
 
             // Create the property info for the "dna:nodeDefinition" child property ...
-            JcrPropertyDefinition nodeDefnDefn = findBestPropertyDefintion(nodeDefinitionProp, primaryTypeName);
+            JcrPropertyDefinition nodeDefnDefn = findBestPropertyDefintion(node.getPrimaryTypeName(),
+                                                                           node.getMixinTypeNames(),
+                                                                           nodeDefinitionProp,
+                                                                           PropertyType.STRING,
+                                                                           false);
             if (nodeDefnDefn != null) {
                 PropertyDefinitionId nodeDefnDefinitionId = nodeDefnDefn.getId();
                 PropertyInfo nodeDefinitionInfo = new PropertyInfo(new PropertyId(desiredUuid, nodeDefinitionProp.getName()),
@@ -672,111 +710,6 @@
             return false;
         }
 
-        /**
-         * Find the best property definition in this node's
-         * 
-         * @param dnaProperty the new property that is to be set on this node
-         * @return the property definition that allows setting this property; never null
-         * @throws ConstraintViolationException if setting the property would violates this node's definition or the property's
-         *         definition
-         */
-        protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty ) throws ConstraintViolationException {
-            // First check the primary type ...
-            JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProperty, node.getPrimaryTypeName());
-            if (definition != null) {
-                // TODO: Does this definition allow this value? ...
-                return definition;
-            }
-            // Check the mixin types ...
-            for (Name mixinTypeName : node.getMixinTypeNames()) {
-                definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
-                if (definition != null) {
-                    // TODO: Does this definition allow this value? ...
-                    return definition;
-                }
-            }
-
-            // Nothing was found yet, so check the residual property definitions, starting with the primary type ...
-            definition = findBestPropertyDefintion(dnaProperty, residualName);
-            if (definition != null) {
-                // TODO: Does this definition allow this value? ...
-                return definition;
-            }
-            // Check the mixin types ...
-            for (Name mixinTypeName : node.getMixinTypeNames()) {
-                definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
-                if (definition != null) {
-                    // TODO: Does this definition allow this value? ...
-                    return definition;
-                }
-            }
-
-            // No definition that allowed the values ...
-            throw new ConstraintViolationException();
-        }
-
-        /**
-         * Find the best property definition in the named node type.
-         * 
-         * @param dnaProperty the new property that is to be set on this node
-         * @param nodeTypeName the name of the node type that should be checked; may not be null
-         * @return the property definition that allows setting this property; or null if no valid property definition could be
-         *         found in the given node type
-         * @see #findBestPropertyDefintion(Property)
-         */
-        protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty,
-                                                                   Name nodeTypeName ) {
-            JcrNodeType nodeType = nodeTypes().getNodeType(nodeTypeName);
-            Name name = dnaProperty.getName();
-            JcrPropertyDefinition definition = null;
-            if (dnaProperty.isSingle()) {
-                // First look for a single-valued property definition with a matching name ...
-                definition = nodeType.getPropertyDefinition(name, false);
-                if (definition != null) return definition;
-
-                // Then look for a residual definition ...
-                definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, false);
-                if (definition != null) return definition;
-            }
-
-            // Either the DNA property has 0 or 2+ values (and we couldn't use a single-valued definition)
-            // OR there was 1 value and we couldn't find a single-valued definition.
-            // So, we need to look for a multi-valued property definition ...
-
-            // First look for a definition that matches by name ...
-            definition = nodeType.getPropertyDefinition(name, true);
-            if (definition != null) return definition;
-
-            // Then look for a residual definition ...
-            definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
-            if (definition != null) return definition;
-
-            // Nothing found yet ...
-            if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
-                // We can use the "nt:unstructured" property definitions for any property ...
-                JcrNodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
-                definition = unstructured.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
-                if (definition != null) return definition;
-            }
-
-            // No property definition could be found ...
-            return null;
-        }
-
-        /**
-         * @param childName
-         * @param numberOfExistingChildrenWithName
-         * @return the most specific node definition for the child; never null
-         * @throws ConstraintViolationException if the new child would violates this node's definition
-         */
-        protected JcrNodeDefinition findBestChildNodeDefinition( Name childName,
-                                                                 int numberOfExistingChildrenWithName )
-            throws ConstraintViolationException {
-
-            // No definition that allowed the values ...
-            throw new ConstraintViolationException();
-        }
-
         protected boolean isAncestor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
             UUID ancestor = node.getParent();
             while (ancestor != null) {
@@ -789,6 +722,56 @@
     }
 
     /**
+     * Find the best property definition in this node's
+     * 
+     * @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
+     *        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 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
+     */
+    protected JcrPropertyDefinition findBestPropertyDefintion( Name primaryTypeNameOfParent,
+                                                               List<Name> mixinTypeNamesOfParent,
+                                                               Property dnaProperty,
+                                                               int propertyType,
+                                                               boolean skipProtected ) {
+        JcrPropertyDefinition definition = null;
+
+        // 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);
+            definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
+                                                            mixinTypeNamesOfParent,
+                                                            dnaProperty.getName(),
+                                                            jcrValue,
+                                                            true,
+                                                            skipProtected);
+        } else {
+            // Create values for the DNA property value ...
+            Value[] jcrValues = new Value[dnaProperty.size()];
+            int index = 0;
+            for (Object value : dnaProperty) {
+                jcrValues[index++] = new JcrValue(context().getValueFactories(), SessionCache.this, propertyType, value);
+            }
+            definition = nodeTypes().findPropertyDefinition(primaryTypeNameOfParent,
+                                                            mixinTypeNamesOfParent,
+                                                            dnaProperty.getName(),
+                                                            jcrValues,
+                                                            skipProtected);
+        }
+
+        if (definition != null) return definition;
+
+        // No definition that allowed the values ...
+        return null;
+    }
+
+    /**
      * Utility method that creates and caches the appropriate kind of AbstractJcrNode implementation for node given by the
      * supplied information.
      * 
@@ -828,7 +811,7 @@
      */
     private AbstractJcrProperty createAndCacheJcrPropertyFor( PropertyInfo info ) {
         boolean multiValued = info.isMultiValued();
-        JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId(), multiValued);
+        JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId());
         assert definition != null;
         if (multiValued) {
             return new JcrMultiValueProperty(this, info.getPropertyId());
@@ -1271,7 +1254,13 @@
                     parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
                 }
                 Name childName = path.getLastSegment().getName();
-                definition = findNodeDefinitionForChild(parentInfo, childName, primaryTypeName);
+                int numExistingChildrenWithSameName = parentInfo.getChildren().getCountOfSameNameSiblingsWithName(childName);
+                definition = nodeTypes().findChildNodeDefinition(parentInfo.getPrimaryTypeName(),
+                                                                 parentInfo.getMixinTypeNames(),
+                                                                 childName,
+                                                                 primaryTypeName,
+                                                                 numExistingChildrenWithSameName,
+                                                                 false);
                 if (definition == null) {
                     String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
                     throw new RepositorySourceException(msg);
@@ -1282,59 +1271,23 @@
         // ------------------------------------------------------
         // Set the node's properties ...
         // ------------------------------------------------------
-        // First get the property type for each property, based upon the primary type and mixins ...
-        // The map with single-valued properties...
-        Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
-        // ... and the map with multi-valued properties
-        Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
-
         boolean referenceable = false;
 
-        List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
         // Start with the primary type ...
-        NodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
-        for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
-            String nameString = propertyDefn.getName();
-            if ("*".equals(nameString)) {
-                anyPropertyDefinitions.add(propertyDefn);
-                continue;
-            }
-            Name name = nameFactory.create(nameString);
+        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+        if (primaryType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
 
-            if (propertyDefn.isMultiple()) {
-                PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-            } else {
-                PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-            }
-        }
         // The process the mixin types ...
         org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
-        Set<Name> mixinTypeNames = null;
+        List<Name> mixinTypeNames = null;
         if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
             for (Object mixinTypeValue : mixinTypesProperty) {
                 Name mixinTypeName = nameFactory.create(mixinTypeValue);
-                if (mixinTypeNames == null) mixinTypeNames = new HashSet<Name>();
+                if (mixinTypeNames == null) mixinTypeNames = new LinkedList<Name>();
                 mixinTypeNames.add(mixinTypeName);
-                if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
-                String mixinTypeNameString = mixinTypeName.getString(namespaces);
-                NodeType mixinType = nodeTypes().getNodeType(mixinTypeNameString);
-                for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
-                    String nameString = propertyDefn.getName();
-                    if ("*".equals(nameString)) {
-                        anyPropertyDefinitions.add(propertyDefn);
-                        continue;
-                    }
-                    Name name = nameFactory.create(nameString);
-                    if (propertyDefn.isMultiple()) {
-                        PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                        if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-                    } else {
-                        PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
-                        if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
-                    }
-                }
+                JcrNodeType mixinType = nodeTypes().getNodeType(mixinTypeName);
+                if (mixinType == null) continue;
+                if (!referenceable && mixinType.isNodeType(JcrMixLexicon.REFERENCEABLE)) referenceable = true;
             }
         }
 
@@ -1344,47 +1297,25 @@
             Name name = dnaProp.getName();
 
             // Figure out the JCR property type for this property ...
-            PropertyDefinition propertyDefinition;
-            if (dnaProp.isMultiple()) {
-                propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
-            } else {
-                propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);
+            int propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+            PropertyDefinition propertyDefinition = findBestPropertyDefintion(primaryTypeName,
+                                                                              mixinTypeNames,
+                                                                              dnaProp,
+                                                                              propertyType,
+                                                                              false);
 
-                // If the property has only one value, dnaProp.isMultiple() will return false, but the
-                // property may actually be a multi-valued property that happens to have one property set.
-                if (propertyDefinition == null) {
-                    propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
-                }
-            }
-
-            // If no property type was found for this property, see if there is a wildcard property ...
-            if (propertyDefinition == null) {
-                for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext();) {
-                    PropertyDefinition nextDef = iter.next();
-
-                    // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
-                    if ((nextDef.isMultiple() && dnaProp.isMultiple()) || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
-                        propertyDefinition = nextDef;
+            // If there still is no property type defined ...
+            if (propertyDefinition == null && INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+                // We can use the "nt:unstructured" property definitions for any property ...
+                NodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
+                for (PropertyDefinition anyDefinition : unstructured.getDeclaredPropertyDefinitions()) {
+                    if (anyDefinition.isMultiple()) {
+                        propertyDefinition = anyDefinition;
                         break;
                     }
                 }
             }
-
-            // If there still is no property type defined ...
             if (propertyDefinition == null) {
-                assert anyPropertyDefinitions.isEmpty();
-                if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
-                    // We can use the "nt:unstructured" property definitions for any property ...
-                    NodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
-                    for (PropertyDefinition anyDefinition : unstructured.getDeclaredPropertyDefinitions()) {
-                        if (anyDefinition.isMultiple()) {
-                            propertyDefinition = anyDefinition;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (propertyDefinition == null) {
                 // We're supposed to skip this property (since we don't have a definition for it) ...
                 continue;
             }
@@ -1397,9 +1328,9 @@
             }
 
             // Figure out the property type ...
-            int propertyType = propertyDefinition.getRequiredType();
-            if (propertyType == PropertyType.UNDEFINED) {
-                propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+            int definitionType = propertyDefinition.getRequiredType();
+            if (definitionType != PropertyType.UNDEFINED) {
+                propertyType = definitionType;
             }
 
             // Record the property in the node information ...
@@ -1412,7 +1343,13 @@
         // Now add the "jcr:uuid" property if and only if referenceable ...
         if (referenceable) {
             // We know that this property is single-valued
-            PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
+            JcrValue value = new JcrValue(context.getValueFactories(), this, PropertyType.STRING, uuid);
+            PropertyDefinition propertyDefinition = nodeTypes().findPropertyDefinition(primaryTypeName,
+                                                                                       mixinTypeNames,
+                                                                                       JcrLexicon.UUID,
+                                                                                       value,
+                                                                                       false,
+                                                                                       false);
             PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
             JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
             PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
@@ -1433,31 +1370,6 @@
     }
 
     /**
-     * Utility method to find the {@link NodeDefinition} for a child node with the supplied {@link Name name}, parent information,
-     * and the primary node type of the child.
-     * 
-     * @param parentInfo the parent information; may not be null
-     * @param childName the name of the child node (without any same-name-sibling index); may not be null
-     * @param primaryTypeOfChild the name of the child's primary type
-     * @return the node definition for this child, as best as can be determined, or null if the node definition could not be
-     *         determined
-     * @throws RepositoryException if the parent's primary node type cannot be found in the {@link NodeTypeManager}
-     */
-    protected JcrNodeDefinition findNodeDefinitionForChild( NodeInfo parentInfo,
-                                                            Name childName,
-                                                            Name primaryTypeOfChild ) throws RepositoryException {
-        // Get the primary type of the parent, and look at it's child definitions ...
-        Name primaryTypeName = parentInfo.getPrimaryTypeName();
-        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
-        if (primaryType == null) {
-            String msg = JcrI18n.missingNodeTypeForExistingNode.text(primaryTypeName, parentInfo.getUuid(), workspaceName);
-            throw new RepositoryException(msg);
-        }
-        // TODO: should this also check the mixins?
-        return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
-    }
-
-    /**
      * 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.
      * <p>
@@ -1498,31 +1410,4 @@
         }
         return numDeleted;
     }
-
-    /**
-     * Find the best {@link JcrNodeDefinition child definition} for a child with the specified name given the named primary type
-     * and mixin types.
-     * 
-     * @param childName the name of the child that is to be added
-     * @param childPrimaryTypeName the name of the child's primary type, or null if the child's primary type is not known
-     * @param requiresMultipleSns true if there is at least one existing child with the same name, requiring the child node type
-     *        to allow same-name-siblings, or false if the child will be the first child with the supplied name
-     * @param primaryTypeName the name of the primary type for the parent; may not be null
-     * @param mixinTypeNames the names of the mixin types for the parent; may be null or empty if the parent has no mixins
-     * @return the child node definition that can be used (which may be a residual definition), or null if there is no such node
-     *         type
-     */
-    protected JcrNodeDefinition findBestChildDefinition( Name childName,
-                                                         Name childPrimaryTypeName,
-                                                         boolean requiresMultipleSns,
-                                                         Name primaryTypeName,
-                                                         Collection<Name> mixinTypeNames ) {
-        // First check the primary type for a child definition with the supplied name ...
-        JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
-        JcrNodeDefinition definition = primaryType.findBestNodeDefinitionForChild(childName, childPrimaryTypeName);
-        if (definition != null) {
-            // definition.
-        }
-        return null;
-    }
 }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -25,6 +25,8 @@
 
 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;
@@ -74,7 +76,7 @@
      * The updated list of mixin node type names. This is merely a cached version of what's already in the
      * {@link JcrLexicon#MIXIN_TYPES "jcr:mixinTypes"} property.
      */
-    private Set<Name> changedMixinTypeNames;
+    private List<Name> changedMixinTypeNames;
 
     /**
      * The updated node definition, which may be changed when this node is moved to a different parent (with a different node
@@ -152,7 +154,7 @@
      * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
      */
-    public Set<Name> getMixinTypeNames() {
+    public List<Name> getMixinTypeNames() {
         if (changedMixinTypeNames != null) return changedMixinTypeNames;
         return original.getMixinTypeNames();
     }
@@ -335,7 +337,7 @@
         // If this property was the "jcr:mixinTypes" property, update the cached values ...
         if (name.equals(JcrLexicon.MIXIN_TYPES)) {
             if (changedMixinTypeNames == null) {
-                changedMixinTypeNames = new HashSet<Name>();
+                changedMixinTypeNames = new LinkedList<Name>();
             } else {
                 changedMixinTypeNames.clear();
             }

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -24,6 +24,7 @@
 package org.jboss.dna.jcr.cache;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -44,7 +45,7 @@
     private final NodeDefinitionId definition;
     private final Children children;
     private final Map<Name, PropertyInfo> properties;
-    private final Set<Name> mixinTypeNames;
+    private final List<Name> mixinTypeNames;
 
     /**
      * Create an immutable NodeInfo instance.
@@ -59,7 +60,7 @@
      */
     public ImmutableNodeInfo( Location originalLocation,
                               Name primaryTypeName,
-                              Set<Name> mixinTypeNames,
+                              List<Name> mixinTypeNames,
                               NodeDefinitionId definition,
                               UUID parent,
                               Children children,
@@ -70,10 +71,8 @@
         this.parent = parent;
         this.uuid = this.originalLocation.getUuid();
         this.children = children != null ? children : new EmptyChildren(this.uuid);
-        if (properties == null) properties = Collections.emptyMap();
-        this.properties = properties;
-        if (mixinTypeNames == null) mixinTypeNames = Collections.emptySet();
-        this.mixinTypeNames = mixinTypeNames;
+        this.properties = properties != null ? properties : Collections.<Name, PropertyInfo>emptyMap();
+        this.mixinTypeNames = mixinTypeNames != null ? mixinTypeNames : Collections.<Name>emptyList();
         assert this.uuid != null;
         assert this.definition != null;
         assert this.primaryTypeName != null;
@@ -123,7 +122,7 @@
      * 
      * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
      */
-    public Set<Name> getMixinTypeNames() {
+    public List<Name> getMixinTypeNames() {
         return mixinTypeNames;
     }
 

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr.cache;
 
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import org.jboss.dna.graph.Location;
@@ -57,9 +58,9 @@
     /**
      * Get the names of the mixin types for this node.
      * 
-     * @return the unmodifiable set of mixin type names; never null but possibly empty
+     * @return the unmodifiable list of mixin type names; never null but possibly empty
      */
-    public Set<Name> getMixinTypeNames();
+    public List<Name> getMixinTypeNames();
 
     /**
      * @return definition

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -71,8 +71,9 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
         stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
         stub(definition.isMultiple()).toReturn(true);
-        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
-        stub(nodeTypes.getPropertyDefinition(definitionId, true)).toReturn(definition);
+        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"),
+                                                                     PropertyType.BOOLEAN, true);
+        stub(nodeTypes.getPropertyDefinition(definitionId)).toReturn(definition);
 
         UUID uuid = UUID.randomUUID();
         propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -232,11 +232,10 @@
         return true;
     }
 
-    @Test( expected = AssertionError.class )
-    public void shouldNotAllowNullValue() throws Exception {
+    @Test
+    public void shouldAllowNullValue() throws Exception {
         NodeType constrainedType = validateTypeDefinition();
         JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
-
         assertThat(prop.satisfiesConstraints((Value)null), is(false));
     }
 

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -131,13 +131,13 @@
         nodeTypes = new JcrBuiltinNodeTypeSource(context, nodeTypes);
         nodeTypes = new DnaBuiltinNodeTypeSource(context, nodeTypes);
         repoTypeManager = new RepositoryNodeTypeManager(context, nodeTypes);
-        
+
         // Stub out the repository, since we only need a few methods ...
         MockitoAnnotations.initMocks(this);
         stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
         stub(repository.getConnectionFactory()).toReturn(connectionFactory);
         stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
-        
+
         // Set up the session attributes ...
         sessionAttributes = new HashMap<String, Object>();
         sessionAttributes.put("attribute1", "value1");
@@ -308,7 +308,7 @@
     }
 
     @Test
-    public void shouldProvideItemsByPath() throws Exception {
+    public void shouldProvideChildrenByPath() throws Exception {
         Item item = session.getItem("/a");
         assertThat(item, instanceOf(Node.class));
         item = session.getItem("/a/b");
@@ -318,6 +318,12 @@
     }
 
     @Test
+    public void shouldProvidePropertiesByPath() throws Exception {
+        Item item = session.getItem("/a/b/booleanProperty");
+        assertThat(item, instanceOf(Property.class));
+    }
+
+    @Test
     public void shouldProvideValueFactory() throws Exception {
         ValueFactory factory = session.getValueFactory();
         assertThat(factory, notNullValue());
@@ -417,7 +423,7 @@
 
         NodeType rootNodePrimaryType = rootNode.getPrimaryNodeType();
         NodeType dnaRootType = session.nodeTypeManager().getNodeType(DnaLexicon.ROOT);
-        
+
         assertThat(rootNodePrimaryType.getName(), is(dnaRootType.getName()));
 
     }
@@ -431,7 +437,8 @@
     public void rootNodeShouldBeReferenceable() throws RepositoryException {
         Node rootNode = session.getRootNode();
 
-        assertTrue(rootNode.getPrimaryNodeType().isNodeType(JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry())));
+        assertTrue(rootNode.getPrimaryNodeType()
+                           .isNodeType(JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry())));
     }
 
     @Test

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -77,8 +77,9 @@
         dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
         stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
         stub(definition.isMultiple()).toReturn(false);
-        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
-        stub(nodeTypes.getPropertyDefinition(definitionId, false)).toReturn(definition);
+        PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"),
+                                                                     PropertyType.STRING, false);
+        stub(nodeTypes.getPropertyDefinition(definitionId)).toReturn(definition);
 
         UUID uuid = UUID.randomUUID();
         propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -43,7 +43,6 @@
 import java.util.Set;
 import java.util.UUID;
 import javax.jcr.InvalidItemStateException;
-import javax.jcr.nodetype.NodeType;
 import org.jboss.dna.common.statistic.Stopwatch;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
@@ -127,12 +126,12 @@
 
             // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
             JcrNodeType car = new JcrNodeType(context, (RepositoryNodeTypeManager)null, carName,
-                                              Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
-                                              NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
+                                              Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
+                                              NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
             // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
             JcrNodeType aircraft = new JcrNodeType(context, (RepositoryNodeTypeManager)null, aircraftName,
-                                                   Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
+                                                   Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
                                                    NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
             primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
@@ -225,7 +224,20 @@
                 assertThat(info.getProperty().size(), is(actual.size()));
                 assertThat(info.getProperty().getValuesAsArray(), is(actual.getValuesAsArray()));
             } else {
-                assertThat(propertyName, is(JcrLexicon.PRIMARY_TYPE));
+                if (propertyName.equals(JcrLexicon.UUID)) {
+                    // check for a DNA UUID property ...
+                    actual = dnaNode.getProperty(DnaLexicon.UUID);
+                    if (actual != null) {
+                        assertThat(info.getProperty().size(), is(actual.size()));
+                        assertThat(info.getProperty().getValuesAsArray(), is(actual.getValuesAsArray()));
+                    } else {
+                        fail("missing property \"" + propertyName + "\" on " + dnaNode);
+                    }
+                } else if (propertyName.equals(JcrLexicon.PRIMARY_TYPE)) {
+                    // This is okay
+                } else {
+                    fail("missing property \"" + propertyName + "\" on " + dnaNode);
+                }
             }
         }
     }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -28,7 +28,6 @@
 import java.util.Collection;
 import java.util.List;
 import javax.jcr.PropertyType;
-import javax.jcr.nodetype.NodeType;
 import org.jboss.dna.graph.ExecutionContext;
 
 /**
@@ -56,17 +55,10 @@
             throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
         }
 
-        
-        
         // Stubbing in child node and property definitions for now
-        JcrNodeType constrainedType = new JcrNodeType(
-                                                      context,
-                                                      NO_NODE_TYPE_MANAGER,
-                                                      TestLexicon.CONSTRAINED_TYPE,
-                                                      Arrays.asList(new NodeType[] {base}),
-                                                      NO_PRIMARY_ITEM_NAME,
-                                                      NO_CHILD_NODES,
-                                                      Arrays.asList(new JcrPropertyDefinition[] {
+        JcrNodeType constrainedType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, TestLexicon.CONSTRAINED_TYPE,
+                                                      Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME,
+                                                      NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
                                                           new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_BINARY,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
@@ -104,8 +96,9 @@
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
                                                                                     false, false, false, NO_DEFAULT_VALUES,
                                                                                     PropertyType.PATH, new String[] {
-                                                              // "/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"}, false),
-                                                              "/jcr:system/*", "b", "/a/b/c"}, false),
+                                                                                    // "/" + JcrLexicon.Namespace.URI +
+                                                                                    // ":system/*", "b", "/a/b/c"}, false),
+                                                                                        "/jcr:system/*", "b", "/a/b/c"}, false),
                                                           new JcrPropertyDefinition(context, null,
                                                                                     TestLexicon.CONSTRAINED_REFERENCE,
                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),

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-03-23 20:41:31 UTC (rev 787)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java	2009-03-24 02:16:34 UTC (rev 788)
@@ -37,7 +37,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
 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;
@@ -62,11 +63,12 @@
     private NodeInfo original;
     private UUID uuid;
     private Name primaryTypeName;
+    private Name[] requiredPrimaryTypes;
     private Location location;
     private NodeDefinitionId definitionId;
     private ChangedChildren children;
     private Map<Name, PropertyInfo> properties;
-    private Set<Name> mixinTypeNames;
+    private List<Name> mixinTypeNames;
     private ChangedNodeInfo changes;
 
     @Before
@@ -79,10 +81,11 @@
         uuid = UUID.randomUUID();
         location = Location.create(uuid);
         primaryTypeName = name("acme:geniusType");
-        definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"));
+        requiredPrimaryTypes = new Name[] {name("acme:requiredTypeA"), name("acme:requiredTypeB")};
+        definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"), requiredPrimaryTypes);
         children = new ChangedChildren(uuid);
         properties = new HashMap<Name, PropertyInfo>();
-        mixinTypeNames = new HashSet<Name>();
+        mixinTypeNames = new LinkedList<Name>();
         original = new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definitionId, uuid, children, properties);
 
         // Create the changed node representation ...



From dna-commits at lists.jboss.org  Mon Mar 23 22:29:20 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 22:29:20 -0400
Subject: [dna-commits] DNA SVN: r789 - in trunk/dna-jcr/src:
	main/resources/org/jboss/dna/jcr and 1 other directories.
Message-ID: <E1LlwOG-0006H2-U8@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 22:29:20 -0400 (Mon, 23 Mar 2009)
New Revision: 789

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.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
Log:
DNA-285 Pass all Level 1 TCK unit tests

Applied the patch that adds an implementation of the simplest query methods for an L1-compliant repository, and that uncommented the related TCK tests that now pass.

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-03-24 02:16:34 UTC (rev 788)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-03-24 02:29:20 UTC (rev 789)
@@ -97,6 +97,10 @@
     public static I18n workspaceNameIsInvalid;
     public static I18n errorVerifyingWorkspaceName;
 
+    // Query-related messages
+    public static I18n notStoredQuery;
+    public static I18n invalidQueryLanguage;
+    
     static {
         try {
             I18n.initialize(JcrI18n.class);

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java	2009-03-24 02:29:20 UTC (rev 789)
@@ -0,0 +1,208 @@
+/*
+ * 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.util.Arrays;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Place-holder implementation of {@link QueryManager} interface.
+ */
+ at Immutable
+class JcrQueryManager implements QueryManager {
+
+    private final JcrSession session;
+
+    JcrQueryManager( JcrSession session ) {
+        this.session = session;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.query.QueryManager#createQuery(java.lang.String, java.lang.String)
+     */
+    public Query createQuery( String statement,
+                              String language ) throws InvalidQueryException {
+        return this.createQuery(statement, language, null);
+    }
+
+    /**
+     * Creates a new query by specifying the query statement itself, the language in which the query is stated, and, optionally,
+     * the node from which the query was loaded. If the query statement is syntactically invalid, given the language specified, an
+     * {@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 language
+     * @param storedNode
+     * @return A {@code Query} object
+     * @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,
+                               String language,
+                               AbstractJcrNode storedNode ) throws InvalidQueryException {
+        if (Query.XPATH.equals(language)) {
+            return new XPathQuery(this.session, statement, storedNode);
+        }
+        throw new InvalidQueryException(JcrI18n.invalidQueryLanguage.text(language, Arrays.asList(getSupportedQueryLanguages())));
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.query.QueryManager#getQuery(javax.jcr.Node)
+     */
+    public Query getQuery( Node node ) throws InvalidQueryException, RepositoryException {
+        assert node instanceof AbstractJcrNode;
+
+        JcrNodeType nodeType = (JcrNodeType)node.getPrimaryNodeType();
+        if (!nodeType.getInternalName().equals(JcrNtLexicon.QUERY)) {
+            throw new InvalidQueryException(JcrI18n.notStoredQuery.text());
+        }
+
+        // These are both mandatory properties for nodes of nt:query
+        NamespaceRegistry registry = session.getExecutionContext().getNamespaceRegistry();
+        String statement = node.getProperty(JcrLexicon.STATEMENT.getString(registry)).getString();
+        String language = node.getProperty(JcrLexicon.LANGUAGE.getString(registry)).getString();
+
+        return createQuery(statement, language, (AbstractJcrNode)node);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.query.QueryManager#getSupportedQueryLanguages()
+     */
+    public String[] getSupportedQueryLanguages() {
+        return new String[] {Query.XPATH};
+    }
+
+    @Immutable
+    protected abstract class AbstractJcrQuery implements Query {
+        private final JcrSession session;
+        private final String language;
+        private final String statement;
+        private final Path storedPath;
+
+        protected AbstractJcrQuery( JcrSession session,
+                                    String statement,
+                                    String language,
+                                    AbstractJcrNode storedNode ) {
+            assert session != null;
+            assert statement != null;
+            assert language != null;
+
+            this.session = session;
+            this.language = language;
+            this.statement = statement;
+
+            try {
+                this.storedPath = storedNode != null ? storedNode.path() : null;
+            } catch (RepositoryException re) {
+                throw new IllegalStateException(re);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.query.Query#execute()
+         */
+        public abstract QueryResult execute();
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.query.Query#getLanguage()
+         */
+        public String getLanguage() {
+            return language;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.query.Query#getStatement()
+         */
+        public String getStatement() {
+            return statement;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.query.Query#getStoredQueryPath()
+         */
+        public String getStoredQueryPath() throws ItemNotFoundException {
+            if (storedPath == null) {
+                throw new ItemNotFoundException(JcrI18n.notStoredQuery.text());
+            }
+            return storedPath.getString(session.getExecutionContext().getNamespaceRegistry());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.query.Query#storeAsNode(java.lang.String)
+         */
+        public Node storeAsNode( java.lang.String absPath ) throws UnsupportedRepositoryOperationException {
+            throw new UnsupportedRepositoryOperationException();
+        }
+
+    }
+
+    @Immutable
+    protected class XPathQuery extends AbstractJcrQuery {
+
+        XPathQuery( JcrSession session,
+                    String statement,
+                    AbstractJcrNode storedNode ) {
+            super(session, statement, Query.XPATH, storedNode);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.JcrQueryManager.AbstractJcrQuery#execute()
+         */
+        @Override
+        public QueryResult execute() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+}


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

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-03-24 02:16:34 UTC (rev 788)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-03-24 02:29:20 UTC (rev 789)
@@ -107,6 +107,11 @@
     private final JcrNodeTypeManager nodeTypeManager;
 
     /**
+     * Reference to the JCR query manager for this workspace.
+     */
+    private final JcrQueryManager queryManager;
+    
+    /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
     private final JcrSession session;
@@ -149,6 +154,7 @@
 
         // This must be initialized after the session
         this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repository.getRepositoryTypeManager());
+        this.queryManager = new JcrQueryManager(this.session);
     }
 
     final String getSourceName() {
@@ -212,7 +218,7 @@
      * {@inheritDoc}
      */
     public final QueryManager getQueryManager() {
-        throw new UnsupportedOperationException();
+        return queryManager;
     }
 
     /**

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-03-24 02:16:34 UTC (rev 788)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-03-24 02:29:20 UTC (rev 789)
@@ -83,3 +83,6 @@
 
 noNegativeDepth=Depth parameter ({0}) cannot be negative
 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

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-03-24 02:16:34 UTC (rev 788)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-03-24 02:29:20 UTC (rev 789)
@@ -107,7 +107,7 @@
             addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
             addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
@@ -118,13 +118,13 @@
             // addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
-            // addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
             // addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
 
             // The tests in this suite are level one

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-03-24 02:16:34 UTC (rev 788)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrWorkspaceTest.java	2009-03-24 02:29:20 UTC (rev 789)
@@ -160,9 +160,9 @@
         workspace.getObservationManager();
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowGetQueryManager() throws Exception {
-        workspace.getQueryManager();
+    @Test
+    public void shouldProvideQueryManager() throws Exception {
+        assertThat(workspace.getQueryManager(), notNullValue());
     }
 
     @Test



From dna-commits at lists.jboss.org  Mon Mar 23 22:34:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 22:34:43 -0400
Subject: [dna-commits] DNA SVN: r790 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LlwTT-0007sp-Bd@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 22:34:43 -0400 (Mon, 23 Mar 2009)
New Revision: 790

Removed:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
Log:
DNA-305 JcrNodeType.canSetProperty Does Not Enforce Constraints

Applied the "DNA-305_path_namespace" patch that makes path constraints support transient namespace remapping, modifies the test cases to verify this, and moves the test node type into class to clean up the package and make JcrPropertyDefinitionTest more consistent with the approach in SessionCacheTest. This fix should allow the defect to be resolved.

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-03-24 02:29:20 UTC (rev 789)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java	2009-03-24 02:34:43 UTC (rev 790)
@@ -642,15 +642,22 @@
         }
 
         public boolean matches( Value valueToMatch ) {
-            assert valueToMatch != null;
+            assert valueToMatch instanceof JcrValue;
+            
+            JcrValue jcrValue = (JcrValue) valueToMatch;
 
-            PathFactory pathFactory = context.getValueFactories().getPathFactory();
-            Path value = pathFactory.create(((JcrValue)valueToMatch).value());
+            /*
+             * 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
+             */
+            PathFactory repoPathFactory = context.getValueFactories().getPathFactory();
+            PathFactory sessionPathFactory = jcrValue.sessionCache().context().getValueFactories().getPathFactory();
+            Path value = sessionPathFactory.create(((JcrValue)valueToMatch).value());
             value = value.getNormalizedPath();
 
             for (int i = 0; i < constraints.length; i++) {
                 boolean matchesDescendants = constraints[i].endsWith("*");
-                Path constraintPath = pathFactory.create(matchesDescendants ? constraints[i].substring(0,
+                Path constraintPath = repoPathFactory.create(matchesDescendants ? constraints[i].substring(0,
                                                                                                        constraints[i].length() - 2) : constraints[i]);
 
                 if (matchesDescendants && value.isDecendantOf(constraintPath)) {

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-03-24 02:29:20 UTC (rev 789)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-03-24 02:34:43 UTC (rev 790)
@@ -27,8 +27,11 @@
 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.HashMap;
+import java.util.List;
 import java.util.Map;
 import javax.jcr.PropertyType;
 import javax.jcr.Value;
@@ -55,17 +58,15 @@
  */
 public class JcrPropertyDefinitionTest {
 
-    private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
-    private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] {"[,+1945-08-01T01:30:00.000Z]",
+    protected final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+    protected final String[] EXPECTED_DATE_CONSTRAINTS = new String[] {"[,+1945-08-01T01:30:00.000Z]",
         "[+1975-08-01T01:30:00.000Z,)"};
-    private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] {"[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]"};
-    private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
-    private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] {"jcr:system", "dnatest:constrainedType"};
-    // private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b",
-    // "/a/b/c" };
-    private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c"};
-    private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] {"dna:root"};
-    private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] {"foo", "bar*", ".*baz"};
+    protected final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] {"[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]"};
+    protected final String[] EXPECTED_LONG_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+    protected final String[] EXPECTED_NAME_CONSTRAINTS = new String[] {"jcr:system", "dnatest:constrainedType"};
+    protected final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c"};
+    protected final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] {"dna:root"};
+    protected final String[] EXPECTED_STRING_CONSTRAINTS = new String[] {"foo", "bar*", ".*baz"};
 
     private String workspaceName;
     private ExecutionContext context;
@@ -210,7 +211,7 @@
 
     private Value valueFor( Object value,
                             int jcrType ) {
-        return new JcrValue(context.getValueFactories(), session.cache(), jcrType, value);
+        return new JcrValue(session.getExecutionContext().getValueFactories(), session.cache(), jcrType, value);
     }
 
     private String stringOfLength( int length ) {
@@ -552,7 +553,11 @@
         assertThat(prop.satisfiesConstraints(valueFor("/a/b/c", PropertyType.PATH)), is(true));
         assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:namespace", PropertyType.PATH)), is(true));
         assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/", PropertyType.PATH)), is(true));
-        assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:foo", PropertyType.PATH)), is(true));
+
+        // Test that constraints work after session rename
+        session.setNamespacePrefix("jcr2", JcrLexicon.Namespace.URI);
+
+        assertThat(prop.satisfiesConstraints(valueFor("/jcr2:system/dna:foo", PropertyType.PATH)), is(true));
     }
 
     @Test
@@ -575,6 +580,7 @@
         assertThat(prop.satisfiesConstraints(valueFor("/a/b", PropertyType.PATH)), is(false));
         assertThat(prop.satisfiesConstraints(valueFor("/jcr:system", PropertyType.PATH)), is(false));
         assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/d", PropertyType.PATH)), is(false));
+
     }
 
     @Test
@@ -628,4 +634,132 @@
         assertThat(satisfiesConstraints(prop, new Value[] {value}), is(false));
     }
 
+    class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+        /** The list of primary node types. */
+        private final List<JcrNodeType> primaryNodeTypes;
+        /** The list of mixin node types. */
+        private final List<JcrNodeType> mixinNodeTypes;
+
+        TestNodeTypeSource( ExecutionContext context,
+                            JcrNodeTypeSource predecessor ) {
+            super(predecessor);
+
+            primaryNodeTypes = new ArrayList<JcrNodeType>();
+            mixinNodeTypes = 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));
+            }
+
+            // Stubbing in child node and property definitions for now
+            JcrNodeType constrainedType = new JcrNodeType(
+                                                          context,
+                                                          NO_NODE_TYPE_MANAGER,
+                                                          TestLexicon.CONSTRAINED_TYPE,
+                                                          Arrays.asList(new JcrNodeType[] {base}),
+                                                          NO_PRIMARY_ITEM_NAME,
+                                                          NO_CHILD_NODES,
+                                                          Arrays.asList(new JcrPropertyDefinition[] {
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_BINARY,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.BINARY,
+                                                                                        EXPECTED_BINARY_CONSTRAINTS, false),
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_DATE,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.DATE,
+                                                                                        EXPECTED_DATE_CONSTRAINTS, false),
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_DOUBLE,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.DOUBLE,
+                                                                                        EXPECTED_DOUBLE_CONSTRAINTS, false),
+
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_LONG,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.LONG,
+                                                                                        EXPECTED_LONG_CONSTRAINTS, false),
+
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_NAME,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.NAME,
+                                                                                        EXPECTED_NAME_CONSTRAINTS, false),
+
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_PATH,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.PATH,
+                                                                                        EXPECTED_PATH_CONSTRAINTS, false),
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_REFERENCE,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.REFERENCE,
+                                                                                        new String[] {"dna:root",}, false),
+
+                                                              new JcrPropertyDefinition(
+                                                                                        context,
+                                                                                        null,
+                                                                                        TestLexicon.CONSTRAINED_STRING,
+                                                                                        OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                        false, false, false, NO_DEFAULT_VALUES,
+                                                                                        PropertyType.STRING,
+                                                                                        EXPECTED_STRING_CONSTRAINTS, false),
+
+                                                          }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
+            mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+            return primaryNodeTypes;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+            return mixinNodeTypes;
+        }
+
+    }
+
 }

Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-24 02:29:20 UTC (rev 789)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java	2009-03-24 02:34:43 UTC (rev 790)
@@ -1,142 +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.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import javax.jcr.PropertyType;
-import org.jboss.dna.graph.ExecutionContext;
-
-/**
- * Node type source with additional node types that can be used for testing. This class defines its own namespace for its types.
- */
-public class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
-
-    /** The list of primary node types. */
-    private final List<JcrNodeType> primaryNodeTypes;
-    /** The list of mixin node types. */
-    private final List<JcrNodeType> mixinNodeTypes;
-
-    TestNodeTypeSource( ExecutionContext context,
-                        JcrNodeTypeSource predecessor ) {
-        super(predecessor);
-
-        primaryNodeTypes = new ArrayList<JcrNodeType>();
-        mixinNodeTypes = 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));
-        }
-
-        // Stubbing in child node and property definitions for now
-        JcrNodeType constrainedType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, TestLexicon.CONSTRAINED_TYPE,
-                                                      Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME,
-                                                      NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
-                                                          new JcrPropertyDefinition(context, null,
-                                                                                    TestLexicon.CONSTRAINED_BINARY,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.BINARY, new String[] {"[,5)",
-                                                                                        "[10, 20)", "(30,40]", "[50,]"}, false),
-                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_DATE,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.DATE, new String[] {
-                                                                                        "[,+1945-08-01T01:30:00.000Z]",
-                                                                                        "[+1975-08-01T01:30:00.000Z,)"}, false),
-                                                          new JcrPropertyDefinition(context, null,
-                                                                                    TestLexicon.CONSTRAINED_DOUBLE,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.DOUBLE,
-                                                                                    new String[] {"[,5.0)", "[10.1, 20.2)",
-                                                                                        "(30.3,40.4]", "[50.5,]"}, false),
-
-                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_LONG,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.LONG, new String[] {"[,5)",
-                                                                                        "[10, 20)", "(30,40]", "[50,]"}, false),
-
-                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_NAME,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.NAME, new String[] {
-                                                                                        "jcr:system", "dnatest:constrainedType"},
-                                                                                    false),
-
-                                                          new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_PATH,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.PATH, new String[] {
-                                                                                    // "/" + JcrLexicon.Namespace.URI +
-                                                                                    // ":system/*", "b", "/a/b/c"}, false),
-                                                                                        "/jcr:system/*", "b", "/a/b/c"}, false),
-                                                          new JcrPropertyDefinition(context, null,
-                                                                                    TestLexicon.CONSTRAINED_REFERENCE,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.REFERENCE,
-                                                                                    new String[] {"dna:root",}, false),
-
-                                                          new JcrPropertyDefinition(context, null,
-                                                                                    TestLexicon.CONSTRAINED_STRING,
-                                                                                    OnParentVersionBehavior.IGNORE.getJcrValue(),
-                                                                                    false, false, false, NO_DEFAULT_VALUES,
-                                                                                    PropertyType.STRING, new String[] {"foo",
-                                                                                        "bar*", ".*baz",}, false),
-
-                                                      }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
-
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
-        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
-     */
-    @Override
-    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
-        return primaryNodeTypes;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
-     */
-    @Override
-    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
-        return mixinNodeTypes;
-    }
-
-}



From dna-commits at lists.jboss.org  Mon Mar 23 22:51:24 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 22:51:24 -0400
Subject: [dna-commits] DNA SVN: r791 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Llwjc-0005dn-83@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 22:51:24 -0400 (Mon, 23 Mar 2009)
New Revision: 791

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-335 DNA's NodeTypeManager implementation does not allow multiple definitions with the same name

Applied the patch ("DNA-335_clean_type_source") that simplifies RepositoryTypeManager's internal type handling by combining the primaryNodeTypes and mixinNodeTypes into a single map under the assumption that lookups of types by name (which must check primary and mixin types) will be the most common option and retrieval of all primary or mixin types will be relatively rare. 

This also allows for a simplification of the JcrNodeTypeSource hierarchy.

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -71,67 +71,38 @@
     }
 
     /**
-     * Returns the list of mixin node types declared in this type source.
+     * Returns the list of node types declared in this type source.
      * 
-     * @return the list of mixin node types declared in this type source.
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     * @return the list of node types declared in this type source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
      */
-    public abstract Collection<JcrNodeType> getDeclaredMixinNodeTypes();
+    public abstract Collection<JcrNodeType> getDeclaredNodeTypes();
 
     /**
-     * Returns the list of primary node types declared in this type source.
+     * Returns the list of node types returned by this and any predecessor source.
      * 
-     * @return the list of primary node types declared in this type source.
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     * @return the list of node types returned by this and any predecessor source.
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
      */
-    public abstract Collection<JcrNodeType> getDeclaredPrimaryNodeTypes();
-
-    /**
-     * Returns the list of mixin node types returned by this and any predecessor source.
-     * 
-     * @return the list of mixin node types returned by this and any predecessor source.
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
-     */
-    public Collection<JcrNodeType> getMixinNodeTypes() {
+    public Collection<JcrNodeType> getNodeTypes() {
         if (predecessor == null) {
-            return getDeclaredMixinNodeTypes();
+            return getDeclaredNodeTypes();
         }
 
-        Collection<JcrNodeType> declaredMixins = getDeclaredMixinNodeTypes();
-        Collection<JcrNodeType> predecessorMixins = predecessor.getMixinNodeTypes();
+        Collection<JcrNodeType> declaredTypes = getDeclaredNodeTypes();
+        Collection<JcrNodeType> predecessorTypes = predecessor.getNodeTypes();
 
-        List<JcrNodeType> mixins = new ArrayList<JcrNodeType>(declaredMixins.size() + predecessorMixins.size());
-        mixins.addAll(predecessorMixins);
-        mixins.addAll(declaredMixins);
+        List<JcrNodeType> primaries = new ArrayList<JcrNodeType>(declaredTypes.size() + predecessorTypes.size());
+        primaries.addAll(predecessorTypes);
+        primaries.addAll(declaredTypes);
 
-        return mixins;
-    }
-
-    /**
-     * Returns the list of primary node types returned by this and any predecessor source.
-     * 
-     * @return the list of primary node types returned by this and any predecessor source.
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
-     */
-    public Collection<JcrNodeType> getPrimaryNodeTypes() {
-        if (predecessor == null) {
-            return getDeclaredPrimaryNodeTypes();
-        }
-
-        Collection<JcrNodeType> declaredPrimaries = getDeclaredPrimaryNodeTypes();
-        Collection<JcrNodeType> predecessorPrimaries = predecessor.getPrimaryNodeTypes();
-
-        List<JcrNodeType> primaries = new ArrayList<JcrNodeType>(declaredPrimaries.size() + predecessorPrimaries.size());
-        primaries.addAll(predecessorPrimaries);
-        primaries.addAll(declaredPrimaries);
-
         return primaries;
     }
 
     /**
      * Finds the type with the given name and returns its definition.
      * <p>
-     * This implementation delegates to the <code>predecessor</code> (if it exists) if the type is not found within the declareded
+     * This implementation delegates to the <code>predecessor</code> (if it exists) if the type is not found within the declared
      * primary and mixin types.
      * </p>
      * 
@@ -142,18 +113,12 @@
      */
     public JcrNodeType findType( Name typeName ) {
         CheckArg.isNotNull(typeName, "typeName");
-        for (JcrNodeType type : getDeclaredPrimaryNodeTypes()) {
+        for (JcrNodeType type : getDeclaredNodeTypes()) {
             if (typeName.equals(type.getInternalName())) {
                 return type;
             }
         }
 
-        for (JcrNodeType type : getDeclaredMixinNodeTypes()) {
-            if (typeName.equals(type.getInternalName())) {
-                return type;
-            }
-        }
-
         if (predecessor != null) {
             return predecessor.findType(typeName);
         }

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -38,17 +38,14 @@
 @Immutable
 class DnaBuiltinNodeTypeSource extends AbstractJcrNodeTypeSource {
 
-    /** The list of primary node types. */
-    private final List<JcrNodeType> primaryNodeTypes;
-    /** The list of mixin node types. */
-    private final List<JcrNodeType> mixinNodeTypes;
+    /** The list of node types. */
+    private final List<JcrNodeType> nodeTypes;
 
     DnaBuiltinNodeTypeSource( ExecutionContext context,
                               JcrNodeTypeSource predecessor ) {
         super(predecessor);
 
-        primaryNodeTypes = new ArrayList<JcrNodeType>();
-        mixinNodeTypes = new ArrayList<JcrNodeType>();
+        nodeTypes = new ArrayList<JcrNodeType>();
 
         JcrNodeType base = findType(JcrNtLexicon.BASE);
 
@@ -140,29 +137,19 @@
                                       NO_DEFAULT_VALUES, PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}), NOT_MIXIN,
                                            ORDERABLE_CHILD_NODES);
 
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
-        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
 
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
      */
     @Override
-    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
-        return mixinNodeTypes;
+    public Collection<JcrNodeType> getDeclaredNodeTypes() {
+        return nodeTypes;
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
-     */
-    @Override
-    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
-        return primaryNodeTypes;
-    }
-
 }

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -41,9 +41,7 @@
 class JcrBuiltinNodeTypeSource extends AbstractJcrNodeTypeSource {
 
     /** The list of primary node types. */
-    private final List<JcrNodeType> primaryNodeTypes;
-    /** The list of mixin node types. */
-    private final List<JcrNodeType> mixinNodeTypes;
+    private final List<JcrNodeType> nodeTypes;
 
     JcrBuiltinNodeTypeSource( ExecutionContext context ) {
         this(context, null);
@@ -53,7 +51,7 @@
                               JcrNodeTypeSource predecessor ) {
         super(predecessor);
 
-        primaryNodeTypes = new ArrayList<JcrNodeType>();
+        nodeTypes = new ArrayList<JcrNodeType>();
 
         /*
          * These values get created without a session cache, as they aren't tied to any particular session.
@@ -568,12 +566,10 @@
                                                                                                                           false),}),
                                                      NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder,
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {base, unstructured, childNodeDefinition, file, folder,
             frozenNode, hierarchyNode, linkedFile, nodeType, propertyDefinition, query, resource, nodeType, version,
             versionHistory, versionLabels, versionedChild}));
 
-        mixinNodeTypes = new ArrayList<JcrNodeType>();
-
         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,
@@ -616,28 +612,17 @@
                                                                                 PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),
                                                   IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
 
-        mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {lockable, referenceable, versionable}));
+        nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {lockable, referenceable, versionable}));
 
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getMixinNodeTypes()
+     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getNodeTypes()
      */
     @Override
-    public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
-        return mixinNodeTypes;
+    public Collection<JcrNodeType> getDeclaredNodeTypes() {
+        return nodeTypes;
     }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes()
-     */
-    @Override
-    public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
-        return primaryNodeTypes;
-    }
-
 }

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -65,31 +65,7 @@
      * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes()
      */
     public NodeTypeIterator getAllNodeTypes() {
-
-        Collection<JcrNodeType> mixinNodeTypes = repositoryTypeManager.getMixinNodeTypes();
-        Collection<JcrNodeType> primaryNodeTypes = repositoryTypeManager.getPrimaryNodeTypes();
-
-        // TODO: Can revisit this approach later if it becomes a performance issue
-        /*
-         * Note also that this creates a subtle difference in behavior for concurrent modification
-         * between this method and the specific get*NodeTypes methods.  That is, if a type is added
-         * while an iterator from the corresponding specific get*NodeType method is being traversed,
-         * a ConcurrentModificationException will be thrown.  Because this iterator is based on a copy
-         * of the underlying maps, no exception would be thrown in the same case.
-         */
-
-        List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
-
-        // Need to return a version of the node type with the current context
-        for (JcrNodeType type : primaryNodeTypes) {
-            allTypes.add(type.with(context));
-        }
-
-        for (JcrNodeType type : mixinNodeTypes) {
-            allTypes.add(type.with(context));
-        }
-
-        return new JcrNodeTypeIterator(allTypes);
+        return new JcrNodeTypeIterator(repositoryTypeManager.getAllNodeTypes());
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	2009-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -13,20 +13,13 @@
 public interface JcrNodeTypeSource {
 
     /**
-     * Returns the list of primary node types provided by this source
+     * Returns the list of node types provided by this source
      * 
-     * @return the list of primary node types provided by this source
+     * @return the list of node types provided by this source
      */
-    public Collection<JcrNodeType> getPrimaryNodeTypes();
+    public Collection<JcrNodeType> getNodeTypes();
 
     /**
-     * Returns the list of mixin node types provided by this source
-     * 
-     * @return the list of mixin node types provided by this source
-     */
-    public Collection<JcrNodeType> getMixinNodeTypes();
-
-    /**
      * Finds the type with the given name and returns its definition.
      * @param typeName the name of the type to return
      * @return the type named <code>typeName</code> if it exists, otherwise <code>null</code>.

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.jcr;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -49,21 +50,19 @@
 @Immutable
 class RepositoryNodeTypeManager {
 
-    private final Map<Name, JcrNodeType> primaryNodeTypes;
-    private final Map<Name, JcrNodeType> mixinNodeTypes;
+    private final Map<Name, JcrNodeType> nodeTypes;
     private final Map<PropertyDefinitionId, JcrPropertyDefinition> propertyDefinitions;
     private final Map<NodeDefinitionId, JcrNodeDefinition> childNodeDefinitions;
 
     RepositoryNodeTypeManager( ExecutionContext context,
                                JcrNodeTypeSource source ) {
-        Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
-        Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+        Collection<JcrNodeType> types = source.getNodeTypes();
         propertyDefinitions = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
         childNodeDefinitions = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
 
-        primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
-        for (JcrNodeType nodeType : primary) {
-            primaryNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+        nodeTypes = new HashMap<Name, JcrNodeType>(types.size());
+        for (JcrNodeType nodeType : types) {
+            nodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
             for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
                 childNodeDefinitions.put(childDefinition.getId(), childDefinition);
             }
@@ -71,25 +70,30 @@
                 propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
             }
         }
+    }
 
-        mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
-        for (JcrNodeType nodeType : mixins) {
-            mixinNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
-            for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
-                childNodeDefinitions.put(childDefinition.getId(), childDefinition);
-            }
-            for (JcrPropertyDefinition propertyDefinition : nodeType.propertyDefinitions()) {
-                propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
-            }
-        }
+    public Collection<JcrNodeType> getAllNodeTypes() {
+        return nodeTypes.values();
     }
 
     public Collection<JcrNodeType> getMixinNodeTypes() {
-        return mixinNodeTypes.values();
+        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() {
-        return primaryNodeTypes.values();
+        List<JcrNodeType> types = new ArrayList<JcrNodeType>(nodeTypes.size());
+        
+        for (JcrNodeType nodeType : nodeTypes.values()) {
+            if (!nodeType.isMixin()) types.add(nodeType);
+        }
+        
+        return types;
     }
 
     public JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId id ) {
@@ -101,12 +105,7 @@
     }
 
     JcrNodeType getNodeType( Name nodeTypeName ) {
-
-        JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
-        if (nodeType == null) {
-            nodeType = mixinNodeTypes.get(nodeTypeName);
-        }
-        return nodeType;
+        return nodeTypes.get(nodeTypeName);
     }
 
     /**

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -149,7 +149,7 @@
             session.logout();
         }
     }
-
+    
     private JcrPropertyDefinition propertyDefinitionFor( NodeType nodeType,
                                                          Name propertyName ) {
         PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
@@ -637,16 +637,13 @@
     class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
 
         /** The list of primary node types. */
-        private final List<JcrNodeType> primaryNodeTypes;
-        /** The list of mixin node types. */
-        private final List<JcrNodeType> mixinNodeTypes;
+        private final List<JcrNodeType> nodeTypes;
 
         TestNodeTypeSource( ExecutionContext context,
                             JcrNodeTypeSource predecessor ) {
             super(predecessor);
 
-            primaryNodeTypes = new ArrayList<JcrNodeType>();
-            mixinNodeTypes = new ArrayList<JcrNodeType>();
+            nodeTypes = new ArrayList<JcrNodeType>();
 
             JcrNodeType base = findType(JcrNtLexicon.BASE);
 
@@ -736,30 +733,19 @@
 
                                                           }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
 
-            primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
-            mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredNodeTypes()
          */
         @Override
-        public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
-            return primaryNodeTypes;
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
         }
 
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
-         */
-        @Override
-        public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
-            return mixinNodeTypes;
-        }
-
     }
 
 }

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-03-24 02:34:43 UTC (rev 790)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-24 02:51:24 UTC (rev 791)
@@ -112,14 +112,13 @@
      * Define the node types for the "vehix" namespace.
      */
     public static class VehixNodeTypeSource extends AbstractJcrNodeTypeSource {
-        private final List<JcrNodeType> primaryNodeTypes;
-        private final List<JcrNodeType> mixinNodeTypes;
+        private final List<JcrNodeType> nodeTypes;
 
         public VehixNodeTypeSource( ExecutionContext context,
                                     JcrNodeTypeSource predecessor ) {
             super(predecessor);
-            this.primaryNodeTypes = new ArrayList<JcrNodeType>();
-            this.mixinNodeTypes = new ArrayList<JcrNodeType>();
+            this.nodeTypes = new ArrayList<JcrNodeType>();
+
             Name carName = context.getValueFactories().getNameFactory().create("vehix:car");
             Name aircraftName = context.getValueFactories().getNameFactory().create("vehix:aircraft");
             JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
@@ -134,29 +133,19 @@
                                                    Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
                                                    NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
 
-            primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
         }
 
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredNodeTypes()
          */
         @Override
-        public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
-            return mixinNodeTypes;
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
         }
 
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
-         */
-        @Override
-        public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
-            return primaryNodeTypes;
-        }
-
     }
 
     protected Graph createFrom( String repositoryName,



From dna-commits at lists.jboss.org  Mon Mar 23 23:18:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 23 Mar 2009 23:18:34 -0400
Subject: [dna-commits] DNA SVN: r792 - in trunk: dna-jcr-tck and 8 other
	directories.
Message-ID: <E1Llx9u-0005T8-VT@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-23 23:18:34 -0400 (Mon, 23 Mar 2009)
New Revision: 792

Added:
   trunk/dna-jcr-tck/
   trunk/dna-jcr-tck/.classpath
   trunk/dna-jcr-tck/.project
   trunk/dna-jcr-tck/pom.xml
   trunk/dna-jcr-tck/src/
   trunk/dna-jcr-tck/src/test/
   trunk/dna-jcr-tck/src/test/java/
   trunk/dna-jcr-tck/src/test/java/org/
   trunk/dna-jcr-tck/src/test/java/org/jboss/
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelOneTckTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelTwoTckTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalLockingTckTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalObservationTckTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalSqlQueryTckTest.java
   trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalVersionTckTest.java
   trunk/dna-jcr-tck/src/test/resources/
   trunk/dna-jcr-tck/src/test/resources/log4j.properties
   trunk/dna-jcr-tck/src/test/resources/repositoryForTckTests.xml
   trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties
Modified:
   trunk/pom.xml
Log:
DNA-306 Add ability to run all TCK unit tests within Hudson, showing success and failure rate

Applied the patch, which adds a 'dna-jcr-tck' project (which is no longer under 'extensions' but is at the top level) that shows how the project is doing relative to the JCR TCK unit tests.  These unit tests are NOT the official TCK, but are reportedly some of the tests that comprise the TCK and are available via the Jackrabbit project.  Also changed the parent POM to include this module only when using the 'jcr-tck' profile (e.g., 'mvn install -P jcr-tck').


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


Added: trunk/dna-jcr-tck/.classpath
===================================================================
--- trunk/dna-jcr-tck/.classpath	                        (rev 0)
+++ trunk/dna-jcr-tck/.classpath	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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>

Added: trunk/dna-jcr-tck/.project
===================================================================
--- trunk/dna-jcr-tck/.project	                        (rev 0)
+++ trunk/dna-jcr-tck/.project	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna-jcr-tck</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+	</natures>
+</projectDescription>

Added: trunk/dna-jcr-tck/pom.xml
===================================================================
--- trunk/dna-jcr-tck/pom.xml	                        (rev 0)
+++ trunk/dna-jcr-tck/pom.xml	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.jboss.dna</groupId>
+    <artifactId>dna</artifactId>
+    <version>0.4-SNAPSHOT</version>
+  </parent>
+  <!-- The groupId and version values are inherited from parent -->
+  <artifactId>dna-jcr-tck</artifactId>
+  <packaging>jar</packaging>
+  <name>JBoss DNA JCR Compatibility Unit Tests</name>
+  <description>JBoss DNA unit tests to check compatibility with the JCR 1.0 API. This does NOT use the official TCK.</description>
+  <url>http://labs.jboss.org/dna</url>
+  
+  <!--
+  Define the dependencies.  Note that all version and scopes default to those 
+  defined in the dependencyManagement section of the parent pom.
+  -->
+  <dependencies>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+    </dependency>
+    <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>
+    </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>org.jboss.dna</groupId>
+      <artifactId>dna-repository</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-jcr</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+    <!-- 
+    Testing (note the scope)
+    -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- 
+    Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
+    -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <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>
+    <!-- 
+      Java Content Repository API 
+    -->
+    <dependency>
+      <groupId>javax.jcr</groupId>
+      <artifactId>jcr</artifactId>
+    </dependency>
+    <!-- 
+      Apache JCR API unit tests (for any JCR implementation), which is a subset of the official TCK
+    -->
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-jcr-tests</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file

Added: 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	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,152 @@
+/*
+ * 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.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.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.property.Path;
+
+/**
+ * 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
+        connection = source.getConnection();
+        repository = new JcrRepository(Collections.<String, String>emptyMap(), executionContext.create(accessControlContext),
+                                       connectionFactory, source.getName());
+
+        // 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("sv", "http://www.jcp.org/jcr/sv/1.0");
+
+            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;
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelOneTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelOneTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelOneTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -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.
+ *
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for Level 1 (L1) compliance. Note that
+ * technically these are not the actual TCK, but these are unit tests that happen to be similar to (or provided the basis for) a
+ * subset of the TCK.
+ */
+public class Jsr170LevelOneTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170LevelOneTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        // Uncomment this to execute all tests
+        // return new JCRTestSuite();
+
+        // Or uncomment the following lines to execute the different sets/suites of tests ...
+        TestSuite suite = new TestSuite("JCR 1.0 Level 1 Compliance tests");
+
+        suite.addTest(new LevelOneFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite that includes the Level 1 JCR TCK API tests from the Jackrabbit project.
+     */
+    private static class LevelOneFeatureTests extends TestSuite {
+        protected LevelOneFeatureTests() {
+            super("JCR Level 1 API Tests");
+            // We currently don't pass the tests in those suites that are commented out
+            // See https://jira.jboss.org/jira/browse/DNA-285
+
+            addTestSuite(org.apache.jackrabbit.test.api.RootNodeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyTypeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeDiscoveringNodeTypesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.BinaryPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.BooleanPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.DatePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.DoublePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.LongPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PathPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ReferencePropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.StringPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.UndefinedPropertyTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRegistryReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NamespaceRemappingTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.NodeIteratorTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.PropertyReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.RepositoryDescriptorTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.SessionReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.WorkspaceReadMethodsTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ReferenceableRootNodesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ExportSysViewTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.ExportDocViewTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.RepositoryLoginTest.class);
+
+            // These might not all be level one tests
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
+
+            addTestSuite(org.apache.jackrabbit.test.api.query.ElementTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.OrderByDateTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.OrderByDoubleTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.OrderByLongTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.OrderByMultiTypeTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.OrderByStringTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.QueryResultNodeIteratorTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
+
+            addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetStatementTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetSupportedQueryLanguagesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.GetPropertyNamesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.PredicatesTest.class);
+            addTestSuite(org.apache.jackrabbit.test.api.query.SimpleSelectionTest.class);
+
+            // The tests in this suite are level one
+            addTest(org.apache.jackrabbit.test.api.nodetype.TestAll.suite());
+        }
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelTwoTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelTwoTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170LevelTwoTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,196 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.CheckPermissionTest;
+import org.apache.jackrabbit.test.api.DocumentViewImportTest;
+import org.apache.jackrabbit.test.api.ImpersonateTest;
+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.NodeOrderableChildNodesTest;
+import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.NodeTest;
+import org.apache.jackrabbit.test.api.NodeUUIDTest;
+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.ReferencesTest;
+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.SetPropertyAssumeTypeTest;
+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.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;
+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.SetValueStringTest;
+import org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest;
+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.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
+import org.apache.jackrabbit.test.api.query.GetPersistentQueryPathTest;
+import org.apache.jackrabbit.test.api.query.SaveTest;
+import org.apache.jackrabbit.test.api.query.XPathQueryLevel2Test;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for Level 2 (L2) compliance. Note that
+ * technically these are not the actual TCK, but these are unit tests that happen to be similar to (or provided the basis for) a
+ * subset of the TCK.
+ */
+public class Jsr170LevelTwoTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170LevelTwoTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Level 2 Compliance tests");
+
+        suite.addTest(new LevelTwoFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite that includes the Level 2 JCR TCK API tests from the Jackrabbit project.
+     */
+    private static class LevelTwoFeatureTests extends TestSuite {
+        protected LevelTwoFeatureTests() {
+            super("JCR Level 2 API Tests");
+            addTestSuite(AddNodeTest.class);
+            addTestSuite(NamespaceRegistryTest.class);
+            addTestSuite(ReferencesTest.class);
+            addTestSuite(SessionTest.class);
+            addTestSuite(SessionUUIDTest.class);
+            addTestSuite(NodeTest.class);
+            addTestSuite(NodeUUIDTest.class);
+            addTestSuite(NodeOrderableChildNodesTest.class);
+            addTestSuite(PropertyTest.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(SetValueValueFormatExceptionTest.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(SetPropertyAssumeTypeTest.class);
+
+            addTestSuite(NodeItemIsModifiedTest.class);
+            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);
+            addTestSuite(WorkspaceCloneVersionableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
+            addTestSuite(WorkspaceCopyReferenceableTest.class);
+            addTestSuite(WorkspaceCopySameNameSibsTest.class);
+            addTestSuite(WorkspaceCopyTest.class);
+            addTestSuite(WorkspaceCopyVersionableTest.class);
+            addTestSuite(WorkspaceMoveReferenceableTest.class);
+            addTestSuite(WorkspaceMoveSameNameSibsTest.class);
+            addTestSuite(WorkspaceMoveTest.class);
+            addTestSuite(WorkspaceMoveVersionableTest.class);
+
+            addTestSuite(RepositoryLoginTest.class);
+            addTestSuite(ImpersonateTest.class);
+            addTestSuite(CheckPermissionTest.class);
+
+            addTestSuite(DocumentViewImportTest.class);
+            addTestSuite(SerializationTest.class);
+
+            addTestSuite(ValueFactoryTest.class);
+
+            addTestSuite(GetPersistentQueryPathTest.class);
+            addTestSuite(SaveTest.class);
+            addTestSuite(XPathQueryLevel2Test.class);
+
+        }
+    }
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalLockingTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalLockingTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalLockingTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,67 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for the compliance of the optional
+ * locking feature. Note that technically these are not the actual TCK, but these are unit tests that happen to be similar to (or
+ * provided the basis for) a subset of the TCK.
+ */
+public class Jsr170OptionalLockingTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170OptionalLockingTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Optional Feature (Locking) Compliance tests");
+
+        suite.addTest(new OptionalLockingFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite for the locking tests
+     */
+    private static class OptionalLockingFeatureTests extends TestSuite {
+        protected OptionalLockingFeatureTests() {
+            super("JCR Optional Feature (Locking) Tests");
+
+            addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
+        }
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalObservationTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalObservationTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalObservationTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,67 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for the compliance of the optional
+ * observation feature. Note that technically these are not the actual TCK, but these are unit tests that happen to be similar to (or
+ * provided the basis for) a subset of the TCK.
+ */
+public class Jsr170OptionalObservationTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170OptionalObservationTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Optional Feature (Observation) Compliance tests");
+
+        suite.addTest(new OptionalObservationFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite for the observation tests
+     */
+    private static class OptionalObservationFeatureTests extends TestSuite {
+        protected OptionalObservationFeatureTests() {
+            super("JCR Optional Feature (Observation) Tests");
+
+            addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
+        }
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalSqlQueryTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalSqlQueryTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalSqlQueryTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,76 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+import org.apache.jackrabbit.test.api.query.SQLJcrPathTest;
+import org.apache.jackrabbit.test.api.query.SQLJoinTest;
+import org.apache.jackrabbit.test.api.query.SQLOrderByTest;
+import org.apache.jackrabbit.test.api.query.SQLPathTest;
+import org.apache.jackrabbit.test.api.query.SQLQueryLevel2Test;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for the compliance of the optional
+ * JCR-SQL query feature. Note that technically these are not the actual TCK, but these are unit tests that happen to be similar
+ * to (or provided the basis for) a subset of the TCK.
+ */
+public class Jsr170OptionalSqlQueryTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170OptionalSqlQueryTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Optional Feature (JCR-SQL Query) Compliance tests");
+
+        suite.addTest(new OptionalSqlQueryFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite for the JCR-SQL query tests
+     */
+    private static class OptionalSqlQueryFeatureTests extends TestSuite {
+        protected OptionalSqlQueryFeatureTests() {
+            super("JCR Optional Feature (JCR-SQL Query) Tests");
+
+            addTestSuite(SQLOrderByTest.class);
+            addTestSuite(SQLQueryLevel2Test.class);
+            addTestSuite(SQLJoinTest.class);
+            addTestSuite(SQLJcrPathTest.class);
+            addTestSuite(SQLPathTest.class);
+        }
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalVersionTckTest.java
===================================================================
--- trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalVersionTckTest.java	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/java/org/jboss/dna/jcr/Jsr170OptionalVersionTckTest.java	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,67 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.JCRTestSuite;
+
+/**
+ * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests for the compliance of the optional
+ * version feature. Note that technically these are not the actual TCK, but these are unit tests that happen to be similar to (or
+ * provided the basis for) a subset of the TCK.
+ */
+public class Jsr170OptionalVersionTckTest {
+
+    /**
+     * 
+     */
+    public Jsr170OptionalVersionTckTest() {
+    }
+
+    /**
+     * Wrapper so that the Jackrabbit TCK test suite gets picked up by the DNA Maven test target.
+     * 
+     * @return a new instance of {@link JCRTestSuite}.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("JCR 1.0 Optional Feature (Version) Compliance tests");
+
+        suite.addTest(new OptionalVersionFeatureTests());
+
+        return suite;
+    }
+
+    /**
+     * Test suite for the version tests.
+     */
+    private static class OptionalVersionFeatureTests extends TestSuite {
+        protected OptionalVersionFeatureTests() {
+            super("JCR Optional Feature (Version) Tests");
+
+            addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
+        }
+    }
+
+}

Added: trunk/dna-jcr-tck/src/test/resources/log4j.properties
===================================================================
--- trunk/dna-jcr-tck/src/test/resources/log4j.properties	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/resources/log4j.properties	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,12 @@
+# 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
+

Added: trunk/dna-jcr-tck/src/test/resources/repositoryForTckTests.xml
===================================================================
--- trunk/dna-jcr-tck/src/test/resources/repositoryForTckTests.xml	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/resources/repositoryForTckTests.xml	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss DNA (http://www.jboss.org/dna)
+  ~
+  ~ See the COPYRIGHT.txt file distributed with this work for information
+  ~ regarding copyright ownership.  Some portions may be licensed
+  ~ to Red Hat, Inc. under one or more contributor license agreements.
+  ~ See the AUTHORS.txt file in the distribution for a full listing of 
+  ~ individual contributors.
+  ~
+  ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+  ~ is licensed to you under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ JBoss DNA is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+  ~ for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public License
+  ~ along with this distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0" 
+            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;" >
+       <!--  
+           This stanza checks for the jcr:xmltext special case for export as per JCR 1.0.1 section 6.4.2.3
+         -->
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+    </nt:unstructured>
+    <nt:unstructured jcr:name="node2 has a multi-word name" multi-valued-property="bar baz blah">
+       <!--  
+           This stanza checks that consecutive jcr:xmltext elements will be merged in the export as 
+           per JCR 1.0.1 section 6.4.2.3
+         -->
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;XML&quot; text!" />
+       <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my &quot;other&quot; text!" />
+    </nt:unstructured>
+    <!--  
+       Test of unprintable character exports.
+       Cannot test \r character here on Windows as TCK XML Parser will replace it with \n and fail the comparison 
+      -->
+    <nt:unstructured jcr:name="node3" multi-line-property="Line&#009;1&#010;Line 2"/>
+	
+	<!-- Helps test date property conversion, mandatory properties (jcr:content), and primary items -->
+	<!-- 
+	<nt:folder jcr:name="hierarchyRoot" jcr:created="+2009-03-15T12:30:00.000Z">
+	   <nt:file jcr:name="testFile" jcr:created="+2009-03-15T13:30:00.000Z">
+	       <nt:unstructured jcr:name="jcr:content" />
+	   </nt:file> 
+	</nt:folder>
+	 -->
+</testroot>
\ No newline at end of file

Added: trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties	                        (rev 0)
+++ trunk/dna-jcr-tck/src/test/resources/repositoryStubImpl.properties	2009-03-24 03:18:34 UTC (rev 792)
@@ -0,0 +1,9 @@
+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=

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2009-03-24 02:51:24 UTC (rev 791)
+++ trunk/pom.xml	2009-03-24 03:18:34 UTC (rev 792)
@@ -142,10 +142,16 @@
 			<id>release</id>
 			<modules>
 				<module>docs</module>
-		        <module>docs/examples/gettingstarted</module>
+        <module>docs/examples/gettingstarted</module>
 			</modules>
 		</profile>
 		<profile>
+			<id>jcr-tck</id>
+			<modules>
+				<module>dna-jcr-tck</module>
+			</modules>
+		</profile>
+		<profile>
 		    <!-- 
 			This profile is activated when the "generate.docs" property is set,
 			as in "mvn ... -Dgenerate.docs=true ..."



From dna-commits at lists.jboss.org  Tue Mar 24 10:31:24 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Mar 2009 10:31:24 -0400
Subject: [dna-commits] DNA SVN: r793 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1Lm7f2-0003MU-Df@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-24 10:31:24 -0400 (Tue, 24 Mar 2009)
New Revision: 793

Added:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ItemDefinitionTest.java
Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java
Log:
DNA-335 DNA's NodeTypeManager implementation does not allow multiple definitions with the same name

Applied the "DNA-335_overriding_definitions" patch that changes the behavior to be more consistent with the implications of section 4.7.7 in the JCR 2.0 spec. Subtypes can override definitions of their supertypes (or their super-supertypes, etc.). Included some very rudimentary test cases as a sanity check. A more complete test suite is forthcoming. 

This patch retains the support for multiple inheritance but adds the assumption that any future type registration support will limit overriding to cases where the overriding property or child node definition is "more narrow" than the definition being overridden. In the case of properties, this implies that the required type of the property in the subtype's property definition can ALWAYS be cast to the required type of the property in the supertype's definition.

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-03-24 03:18:34 UTC (rev 792)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DefinitionCache.java	2009-03-24 14:31:24 UTC (rev 793)
@@ -24,6 +24,8 @@
 package org.jboss.dna.jcr;
 
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 import javax.jcr.nodetype.NodeType;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.property.Name;
@@ -102,6 +104,7 @@
     DefinitionCache( JcrNodeType primaryType,
                      Iterable<JcrNodeType> mixinTypes ) {
         addDefinitionsForTypeAndAllSupertypes(primaryType);
+        // Assumption is that addMixin won't allow mixin types that conflict with primary types
         if (mixinTypes != null) {
             for (JcrNodeType mixinType : mixinTypes) {
                 addDefinitionsForTypeAndAllSupertypes(mixinType);
@@ -117,22 +120,47 @@
     }
 
     private final void addDefinitions( JcrNodeType nodeType ) {
+        Set<Name> namesFromThisType = new HashSet<Name>();
+
         for (JcrNodeDefinition definition : nodeType.childNodeDefinitions()) {
             Name name = definition.getInternalName();
+
+            /*
+             * If the child node was already defined in the type hierarchy at some other level, ignore the definition 
+             * - it was overridden by the previous definition.  This relies on the fact that TypeA.getTypeAndSupertypes()
+             * always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is.
+             */
+            if (allChildNodeDefinitions.containsKey(name) && !namesFromThisType.contains(name)) {
+                continue;
+            }
+
             if (definition.allowsSameNameSiblings()) {
                 childNodeDefinitionsThatAllowSns.put(name, definition);
             } else {
                 childNodeDefinitionsThatAllowNoSns.put(name, definition);
             }
+            namesFromThisType.add(name);
             allChildNodeDefinitions.put(name, definition);
         }
+
+        namesFromThisType.clear();
         for (JcrPropertyDefinition definition : nodeType.propertyDefinitions()) {
             Name name = definition.getInternalName();
+
+            /*
+             * If the property was already defined in the type hierarchy at some other level, ignore the definition 
+             * - it was overridden by the previous definition.  This relies on the fact that TypeA.getTypeAndSupertypes()
+             * always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is.
+             */
+            if (allPropertyDefinitions.containsKey(name) && !namesFromThisType.contains(name)) {
+                continue;
+            }
             if (definition.isMultiple()) {
                 multiValuedPropertyDefinitions.put(name, definition);
             } else {
                 singleValuedPropertyDefinitions.put(name, definition);
             }
+            namesFromThisType.add(name);
             allPropertyDefinitions.put(name, definition);
         }
     }

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ItemDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ItemDefinitionTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ItemDefinitionTest.java	2009-03-24 14:31:24 UTC (rev 793)
@@ -0,0 +1,380 @@
+/*
+ * 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.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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+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.ValueFactory;
+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;
+
+/**
+ * BDD test cases for property and child node definition inheritance. Could be part of RepositoryNodeTypeManagerTest, but split
+ * off to isolate tests for this behavior vs. projection/inference and registration/unregistration behavior.
+ */
+public class ItemDefinitionTest {
+
+    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 ValueFactory<Name> nameFactory;
+    @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");
+
+        // 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);
+
+        // 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();
+
+        nameFactory = session.getExecutionContext().getValueFactories().getNameFactory();
+    }
+
+    @After
+    public void after() throws Exception {
+        if (session != null && session.isLive()) {
+            session.logout();
+        }
+    }
+
+    @Test
+    public void shouldNotFindInvalidPropertyDefinition() throws Exception {
+        // This property name is not defined for any of our test types
+        Name badName = nameFactory.create("undefinedName");
+        JcrPropertyDefinition propDef;
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_A, Collections.<Name>emptyList(), badName, null, true, true);
+        assertThat(propDef, is(nullValue()));
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_B, Collections.<Name>emptyList(), badName, null, true, true);
+        assertThat(propDef, is(nullValue()));
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_C, Collections.<Name>emptyList(), badName, null, true, true);
+        assertThat(propDef, is(nullValue()));
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_D, Collections.<Name>emptyList(), badName, null, true, true);
+        assertThat(propDef, is(nullValue()));
+    }
+
+    @Test
+    public void shouldUseNearestPropertyDefinition() {
+        // If a property is defined at multiple points in the type hierarchy, the property definition closest to the given type
+        // should be used.
+
+        JcrPropertyDefinition propDef;
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_A,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP1,
+                                                         null,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.STRING);
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_B,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP1,
+                                                         null,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.LONG);
+
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_C,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP1,
+                                                         null,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.BOOLEAN);
+    }
+
+    @Test
+    public void shouldPreferLeftmostSupertypeForDefinition() {
+        /*
+         * This specification doesn't mandate this, but since DNA supports multiple inheritance, we
+         * have imposed the rule that property or child-node definitions should be preferred based on
+         * distance from the given node type in the hierarchy with the order of the supertypes in the declaration
+         * being the tiebreaker.
+         */
+        JcrPropertyDefinition propDef;
+
+        // Should prefer the inherited definition from NODE_TYPE_A since it was declared before NODE_TYPE_C in NODE_TYPE_D's list
+        // of supertypes
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_D,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP1,
+                                                         null,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.STRING);
+
+    }
+    
+    @Test
+    public void shouldFindBestMatchDefinition() {
+        /*
+         * In cases where there is more than one valid definition for the same property,
+         * the best match should be returned.
+         */
+        Value doubleValue = session.getValueFactory().createValue(0.7);
+        Value longValue = session.getValueFactory().createValue(10);
+        Value stringValue = session.getValueFactory().createValue("Should not work");
+        
+        JcrPropertyDefinition propDef;
+
+        // Should prefer the double definition from NODE_TYPE_C since the value is of type double
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_D,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP2,
+                                                         doubleValue,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.DOUBLE);
+
+        // Should prefer the long definition from NODE_TYPE_C since the value is of type long
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_D,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP2,
+                                                         longValue,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(notNullValue()));
+        assertEquals(propDef.getRequiredType(), PropertyType.LONG);
+
+        // Should not allow a string though, since the NODE_TYPE_C definition narrows the acceptable types to double and long
+        propDef = repoTypeManager.findPropertyDefinition(NODE_TYPE_D,
+                                                         Collections.<Name>emptyList(),
+                                                         SINGLE_PROP2,
+                                                         stringValue,
+                                                         true,
+                                                         true);
+        assertThat(propDef, is(nullValue()));
+        
+    }
+
+    static final Name NODE_TYPE_A = new BasicName(TestLexicon.Namespace.URI, "nodeA");
+    static final Name NODE_TYPE_B = new BasicName(TestLexicon.Namespace.URI, "nodeB");
+    static final Name NODE_TYPE_C = new BasicName(TestLexicon.Namespace.URI, "nodeC");
+    static final Name NODE_TYPE_D = new BasicName(TestLexicon.Namespace.URI, "nodeD");
+
+    static final Name SINGLE_PROP1 = new BasicName(TestLexicon.Namespace.URI, "singleProp1");
+    static final Name SINGLE_PROP2 = new BasicName(TestLexicon.Namespace.URI, "singleProp2");
+
+    class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+        /** The list of primary node types. */
+        private final List<JcrNodeType> nodeTypes;
+
+        /*
+         * Build a hierarchy of node types with the following relationships:
+         *  
+         *   dnatest:nodeA extends nt:base
+         *   dnatest:nodeB extends nt:base
+         *   dnatest:nodeC extends dnatest:nodeB
+         *   dnatest:nodeD extends dnatest:nodeA and dnatest:nodeC
+         *   
+         * And the following single-valued property definitions
+         * 
+         *   dnatest:nodeA defines properties:
+         *      dnatest:singleProp1 of type STRING
+         *   dnatest:nodeB defines properties:
+         *      dnatest:singleProp1 of type LONG
+         *      dnatest:singleProp2 of type UNDEFINED
+         *   dnatest:nodeC defines properties:
+         *      dnatest:singleProp1 of type BOOLEAN
+         *      dnatest:singleProp2 of type DOUBLE     
+         *      dnatest:singleProp2 of type LONG (note the double-definition)
+         *   dnatest:nodeD defines properties:
+         *      < NO PROPERTIES DEFINED IN THIS TYPE >
+         */
+
+        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));
+            }
+
+            // Stubbing in child node and property definitions for now
+            JcrNodeType nodeA = new JcrNodeType(
+                                                context,
+                                                NO_NODE_TYPE_MANAGER,
+                                                NODE_TYPE_A,
+                                                Arrays.asList(new JcrNodeType[] {base}),
+                                                NO_PRIMARY_ITEM_NAME,
+                                                NO_CHILD_NODES,
+                                                Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
+                                                                                                                     context,
+                                                                                                                     null,
+                                                                                                                     SINGLE_PROP1,
+                                                                                                                     OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                                                                     false,
+                                                                                                                     false,
+                                                                                                                     false,
+                                                                                                                     NO_DEFAULT_VALUES,
+                                                                                                                     PropertyType.STRING,
+                                                                                                                     NO_CONSTRAINTS,
+                                                                                                                     false),}),
+                                                NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType nodeB = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, NODE_TYPE_B,
+                                                Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                                Arrays.asList(new JcrPropertyDefinition[] {
+                                                    new JcrPropertyDefinition(context, null, SINGLE_PROP1,
+                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                              false, false, false, NO_DEFAULT_VALUES,
+                                                                              PropertyType.LONG, NO_CONSTRAINTS, false),
+                                                    new JcrPropertyDefinition(context, null, SINGLE_PROP2,
+                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                              false, false, false, NO_DEFAULT_VALUES,
+                                                                              PropertyType.UNDEFINED, NO_CONSTRAINTS, false),}),
+                                                NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+            JcrNodeType nodeC = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, NODE_TYPE_C,
+                                                Arrays.asList(new JcrNodeType[] {nodeB}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                                Arrays.asList(new JcrPropertyDefinition[] {
+                                                    new JcrPropertyDefinition(context, null, SINGLE_PROP1,
+                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                              false, false, false, NO_DEFAULT_VALUES,
+                                                                              PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
+                                                    new JcrPropertyDefinition(context, null, SINGLE_PROP2,
+                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                              false, false, false, NO_DEFAULT_VALUES,
+                                                                              PropertyType.DOUBLE, NO_CONSTRAINTS, false),
+                                                    new JcrPropertyDefinition(context, null, SINGLE_PROP2,
+                                                                              OnParentVersionBehavior.IGNORE.getJcrValue(),
+                                                                              false, false, false, NO_DEFAULT_VALUES,
+                                                                              PropertyType.LONG, NO_CONSTRAINTS, false),
+
+                                                }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            JcrNodeType nodeD = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, NODE_TYPE_D, Arrays.asList(new JcrNodeType[] {
+                nodeA, nodeC}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {nodeA, nodeB, nodeC, nodeD}));
+        }
+
+        /**
+         * {@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/ItemDefinitionTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain



From dna-commits at lists.jboss.org  Tue Mar 24 14:16:53 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Mar 2009 14:16:53 -0400
Subject: [dna-commits] DNA SVN: r794 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LmBBF-0005g3-Oe@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-24 14:16:53 -0400 (Tue, 24 Mar 2009)
New Revision: 794

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
Log:
DNA-336 JcrValue.asType(int desiredPropertyType) skips a number of valid conversions and has duplicate logic

Changed the JcrValue.asType(int) logic to first ensure the value matches the current required type (in most cases this should just return the actual value if the type is correct), and to then convert to the desired property type.  Corrected a number of conversions to include several combinations that was excluded.

Also added a check to the NameValueFactory to ensure that the relative path (of size 1) has no SNS index.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-24 14:31:24 UTC (rev 793)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/NameValueFactory.java	2009-03-24 18:16:53 UTC (rev 794)
@@ -265,7 +265,9 @@
         if (value == null) return null;
         if (!value.isAbsolute() && value.size() == 1) {
             // A relative name of length 1 is converted to a name
-            return value.getSegment(0).getName();
+            Path.Segment segment = value.getLastSegment();
+            // Can only convert if the path has no SNS index ...
+            if (!segment.hasIndex()) return segment.getName();
         }
         throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(Path.class.getSimpleName(),
                                                                                                     Name.class.getSimpleName(),

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-03-24 14:31:24 UTC (rev 793)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java	2009-03-24 18:16:53 UTC (rev 794)
@@ -35,7 +35,6 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.Path.Segment;
 
 /**
  * @author jverhaeg
@@ -56,7 +55,7 @@
         assert type == PropertyType.BINARY || type == PropertyType.BOOLEAN || type == PropertyType.DATE
                || type == PropertyType.DOUBLE || type == PropertyType.LONG || type == PropertyType.NAME
                || type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING;
-        
+
         // Leaving this assertion out for now so that values can be created in node type sources, which are created outside
         // the context of any particular session.
         // assert sessionCache != null;
@@ -90,12 +89,13 @@
 
     /**
      * Returns the session cache for the session that created this value.
+     * 
      * @return the session cache for the session that created this value.
      */
     final SessionCache sessionCache() {
         return sessionCache;
     }
-    
+
     /**
      * {@inheritDoc}
      * 
@@ -226,91 +226,136 @@
      * @see PropertyType
      */
     JcrValue asType( int type ) throws ValueFormatException {
-
         if (type == this.type) {
             return this.withTypeAndValue(this.type, this.value);
         }
 
+        Object value = this.value;
         switch (type) {
             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 {
+                    throw createValueFormatException(boolean.class);
+                }
                 try {
-                    if (this.type == PropertyType.STRING || this.type == PropertyType.BINARY) {
-                        return this.withTypeAndValue(type, valueFactories.getBooleanFactory().create(value));
-                    }
+                    return this.withTypeAndValue(type, valueFactories.getBooleanFactory().create(value));
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
-                throw createValueFormatException(boolean.class);
 
             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 {
+                    throw createValueFormatException(Calendar.class);
+                }
                 try {
-                    if (this.type == PropertyType.DOUBLE || this.type == PropertyType.LONG || this.type == PropertyType.STRING
-                        || this.type == PropertyType.BINARY) {
-                        return this.withTypeAndValue(type, valueFactories.getDateFactory().create(value));
-                    }
+                    return this.withTypeAndValue(type, valueFactories.getDateFactory().create(value));
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
 
-                throw createValueFormatException(Calendar.class);
-
             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 {
+                    throw createValueFormatException(Name.class);
+                }
                 try {
-                    if (this.type == PropertyType.STRING) {
-                        return this.withTypeAndValue(type, valueFactories.getNameFactory().create(value));
-                    }
-
-                    String valueAsString = this.valueFactories.getStringFactory().create(value);
-                    if (this.type == PropertyType.BINARY) {
-                        return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
-
-                    }
-
-                    if (this.type == PropertyType.PATH) {
-                        Path path = valueFactories.getPathFactory().create(valueAsString);
-
-                        Segment[] segments = path.getSegmentsArray();
-                        if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
-                            return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
-                        }
-                    }
+                    return this.withTypeAndValue(type, valueFactories.getNameFactory().create(value));
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
 
-                throw createValueFormatException(Name.class);
-
             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 {
+                    throw createValueFormatException(Path.class);
+                }
                 try {
-                    if (this.type == PropertyType.STRING) {
-                        return this.withTypeAndValue(type, valueFactories.getPathFactory().create(value));
-
-                    }
+                    return this.withTypeAndValue(type, valueFactories.getPathFactory().create(value));
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
-                throw createValueFormatException(Path.class);
 
-                // Nothing can be converted to these types (except themselves)
             case PropertyType.REFERENCE:
-                throw createValueFormatException(Node.class);
+                if (this.type == PropertyType.STRING) {
+                    value = valueFactories.getStringFactory().create(this.value);
+                } else if (this.type == PropertyType.BINARY) {
+                    value = valueFactories.getBinaryFactory().create(this.value);
+                } else {
+                    throw createValueFormatException(Node.class);
+                }
+                try {
+                    return this.withTypeAndValue(type, valueFactories.getReferenceFactory().create(value));
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
             case PropertyType.DOUBLE:
-                throw createValueFormatException(double.class);
+                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 {
+                    throw createValueFormatException(double.class);
+                }
+                try {
+                    return this.withTypeAndValue(type, valueFactories.getDoubleFactory().create(value));
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
             case PropertyType.LONG:
-                throw createValueFormatException(long.class);
+                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 {
+                    throw createValueFormatException(long.class);
+                }
+                try {
+                    return this.withTypeAndValue(type, valueFactories.getLongFactory().create(value));
+                } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
+                    throw createValueFormatException(vfe);
+                }
 
                 // Anything can be converted to these types
             case PropertyType.BINARY:
                 try {
                     return this.withTypeAndValue(type, valueFactories.getBinaryFactory().create(value));
-
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }
             case PropertyType.STRING:
                 try {
                     return this.withTypeAndValue(type, valueFactories.getStringFactory().create(value));
-
                 } catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
                     throw createValueFormatException(vfe);
                 }



From dna-commits at lists.jboss.org  Tue Mar 24 18:33:26 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 24 Mar 2009 18:33:26 -0400
Subject: [dna-commits] DNA SVN: r795 - in trunk/dna-jcr/src:
	test/java/org/jboss/dna/jcr and 1 other directory.
Message-ID: <E1LmFBW-0002QC-LW@committer01.frg.pub.inap.atl.jboss.com>

Author: rhauch
Date: 2009-03-24 18:33:26 -0400 (Tue, 24 Mar 2009)
New Revision: 795

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/Vehicles.java
Modified:
   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/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-194 Implement update JCR capability

Added (and tested) functionality to set properties and remove properties.  However, none of the TCK unit tests could be run, however, since adding nodes appears to be a prerequisite of all of the Level 2 TCK unit tests.


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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -23,15 +23,12 @@
  */
 package org.jboss.dna.jcr;
 
-import java.io.InputStream;
-import java.util.Calendar;
 import javax.jcr.Item;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 import javax.jcr.nodetype.PropertyDefinition;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
@@ -45,7 +42,7 @@
 @NotThreadSafe
 abstract class AbstractJcrProperty extends AbstractJcrItem implements Property {
 
-    private final PropertyId propertyId;
+    protected final PropertyId propertyId;
 
     AbstractJcrProperty( SessionCache cache,
                          PropertyId propertyId ) {
@@ -81,6 +78,11 @@
         return new JcrValue(context().getValueFactories(), this.cache, propertyInfo().getPropertyType(), value);
     }
 
+    final JcrValue createValue( Object value,
+                                int propertyType ) {
+        return new JcrValue(context().getValueFactories(), this.cache, propertyType, value);
+    }
+
     @Override
     Path path() throws RepositoryException {
         return cache.getPathFor(propertyInfo());
@@ -168,106 +170,6 @@
      * {@inheritDoc}
      * 
      * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(javax.jcr.Value)
-     */
-    public final void setValue( Value value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(javax.jcr.Value[])
-     */
-    public final void setValue( Value[] values ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(java.lang.String)
-     */
-    public final void setValue( String value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(java.lang.String[])
-     */
-    public final void setValue( String[] values ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(java.io.InputStream)
-     */
-    public final void setValue( InputStream value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(long)
-     */
-    public final void setValue( long value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(double)
-     */
-    public final void setValue( double value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(java.util.Calendar)
-     */
-    public final void setValue( Calendar value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(boolean)
-     */
-    public final void setValue( boolean value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
-     * @see javax.jcr.Property#setValue(javax.jcr.Node)
-     */
-    public final void setValue( Node value ) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Item#refresh(boolean)
      */
     public void refresh( boolean keepChanges ) {

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -27,11 +27,16 @@
 import java.util.Calendar;
 import java.util.Iterator;
 import javax.jcr.Node;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactory;
 
 /**
  * @author jverhaeg
@@ -140,16 +145,6 @@
 
     /**
      * {@inheritDoc}
-     * 
-     * @throws ValueFormatException always
-     * @see javax.jcr.Property#getValue()
-     */
-    public Value getValue() throws ValueFormatException {
-        throw new ValueFormatException();
-    }
-
-    /**
-     * {@inheritDoc}
      * <p>
      * Per the JCR specification, these values need to be created each time this method is called, since the Value cannot be used
      * after {@link Value#getStream()} is called/processed. The spec says that the client simply needs to obtain a new Value (or
@@ -167,4 +162,176 @@
         }
         return values;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(javax.jcr.Value[])
+     */
+    public final void setValue( Value[] values )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        JcrValue[] jcrValues = null;
+        if (values != null && values.length != 0) {
+            int numValues = values.length;
+            jcrValues = new JcrValue[numValues];
+            ValueFactory<?> factory = null;
+            for (int i = 0; i != numValues; ++i) {
+                Value value = values[i];
+                if (value instanceof JcrValue) {
+                    // just use the value ...
+                    jcrValues[i] = (JcrValue)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();
+                    }
+                    jcrValues[i] = createValue(factory.create(data), type);
+                }
+            }
+        }
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(java.lang.String[])
+     */
+    public final void setValue( String[] values )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        JcrValue[] jcrValues = null;
+        if (values != null && values.length != 0) {
+            int numValues = values.length;
+            jcrValues = new JcrValue[numValues];
+            for (int i = 0; i != numValues; ++i) {
+                jcrValues[i] = createValue(values[i], PropertyType.STRING);
+            }
+        }
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValues);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#getValue()
+     */
+    public Value getValue() throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(javax.jcr.Value)
+     */
+    public final void setValue( Value value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(java.lang.String)
+     */
+    public final void setValue( String value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(java.io.InputStream)
+     */
+    public final void setValue( InputStream value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(long)
+     */
+    public final void setValue( long value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(double)
+     */
+    public final void setValue( double value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(java.util.Calendar)
+     */
+    public final void setValue( Calendar value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(boolean)
+     */
+    public final void setValue( boolean value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws ValueFormatException always
+     * @see javax.jcr.Property#setValue(javax.jcr.Node)
+     */
+    public final void setValue( Node value ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
+
 }

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -573,53 +573,6 @@
     }
 
     /**
-     * Compute the JCR {@link PropertyType} for the given DNA {@link org.jboss.dna.graph.property.PropertyType}.
-     * <p>
-     * See DNA-293 for complete discussion on why this method works the way it does. The best option appears to be basing the
-     * PropertyType on the first value, since that should be compatible with the PropertyType that was used when the values were
-     * set on the property in the first place.
-     * </p>
-     * 
-     * @param property the DNA property for which the {@link PropertyType} is to be determined; never null
-     * @return the JCR property type; always a valid value and never {@link PropertyType#UNDEFINED}.
-     */
-    static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.Property property ) {
-        Object value = property.getFirstValue();
-
-        // Get the DNA property type for this ...
-        switch (org.jboss.dna.graph.property.PropertyType.discoverType(value)) {
-            case STRING:
-                return PropertyType.STRING;
-            case NAME:
-                return PropertyType.NAME;
-            case LONG:
-                return PropertyType.LONG;
-            case UUID:
-                return PropertyType.STRING; // JCR treats UUID properties as strings
-            case URI:
-                return PropertyType.STRING;
-            case PATH:
-                return PropertyType.PATH;
-            case BOOLEAN:
-                return PropertyType.BOOLEAN;
-            case DATE:
-                return PropertyType.DATE;
-            case DECIMAL:
-                return PropertyType.STRING; // better than losing information
-            case DOUBLE:
-                return PropertyType.DOUBLE;
-            case BINARY:
-                return PropertyType.BINARY;
-            case OBJECT:
-                return PropertyType.UNDEFINED;
-            case REFERENCE:
-                return PropertyType.REFERENCE;
-        }
-        assert false;
-        return PropertyType.UNDEFINED;
-    }
-
-    /**
      * {@inheritDoc}
      * 
      * @see javax.jcr.Session#refresh(boolean)

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -27,9 +27,13 @@
 import java.util.Calendar;
 import java.util.UUID;
 import javax.jcr.Node;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
 import org.jboss.dna.graph.property.Binary;
 import org.jboss.dna.graph.property.Reference;
 import org.jboss.dna.graph.property.ValueFactories;
@@ -170,6 +174,129 @@
     /**
      * {@inheritDoc}
      * 
+     * @see javax.jcr.Property#setValue(javax.jcr.Value)
+     */
+    public void setValue( Value value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        JcrValue jcrValue = null;
+        if (value instanceof JcrValue) {
+            jcrValue = (JcrValue)value;
+            cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValue);
+            return;
+        }
+        // We have to convert from one Value implementation to ours ...
+        switch (value.getType()) {
+            case PropertyType.STRING:
+                setValue(value.getString());
+                break;
+            case PropertyType.BINARY:
+                setValue(value.getStream());
+                break;
+            case PropertyType.BOOLEAN:
+                setValue(value.getBoolean());
+                break;
+            case PropertyType.DATE:
+                setValue(value.getDate());
+                break;
+            case PropertyType.DOUBLE:
+                setValue(value.getDouble());
+                break;
+            case PropertyType.LONG:
+                setValue(value.getLong());
+                break;
+            case PropertyType.NAME:
+                setValue(value.getString());
+                break;
+            case PropertyType.PATH:
+                setValue(value.getString());
+                break;
+            case PropertyType.REFERENCE:
+                setValue(value.getString());
+                break;
+            default:
+                throw new RepositoryException();
+        }
+    }
+
+    protected void setValue( JcrValue jcrValue )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        cache.getEditorFor(propertyId.getNodeId()).setProperty(propertyId.getPropertyName(), jcrValue);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(java.lang.String)
+     */
+    public void setValue( String value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(value, PropertyType.STRING));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(java.io.InputStream)
+     */
+    public void setValue( InputStream value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(context().getValueFactories().getBinaryFactory().create(value), PropertyType.DATE));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(long)
+     */
+    public void setValue( long value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(new Long(value), PropertyType.LONG));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(double)
+     */
+    public void setValue( double value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(new Double(value), PropertyType.DOUBLE));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(java.util.Calendar)
+     */
+    public void setValue( Calendar value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(context().getValueFactories().getDateFactory().create(value), PropertyType.DATE));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(boolean)
+     */
+    public void setValue( boolean value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        setValue(createValue(new Boolean(value), PropertyType.BOOLEAN));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(javax.jcr.Node)
+     */
+    public void setValue( Node value )
+        throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        String uuid = value.getUUID();
+        setValue(createValue(uuid, PropertyType.REFERENCE));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @throws ValueFormatException always
      * @see javax.jcr.Property#getValues()
      */
@@ -177,60 +304,21 @@
         throw new ValueFormatException();
     }
 
-    /*
+    /**
      * {@inheritDoc}
      * 
-     * @throws IllegalArgumentException if <code>value</code> is <code>null</code>.
-     * @see javax.jcr.Property#setValue(javax.jcr.Value)
-     *
-    @SuppressWarnings( "fallthrough" )
-    public void setValue( Value value ) throws RepositoryException {
-        CheckArg.isNotNull(value, "value");
-        // TODOx: Check node type constraint
-        try {
-            jcrValue = JcrValue.class.cast(value);
-        } catch (ClassCastException error) {
-            // TODOx: not sure if this is even possible
-            ValueFactories valueFactories = getExecutionContext().getValueFactories();
-            int type = value.getType();
-            switch (type) {
-                case PropertyType.BINARY: {
-                    jcrValue = new JcrValue<InputStream>(valueFactories, type, value.getStream());
-                    break;
-                }
-                case PropertyType.BOOLEAN: {
-                    jcrValue = new JcrValue<Boolean>(valueFactories, type, value.getBoolean());
-                    break;
-                }
-                case PropertyType.DATE: {
-                    jcrValue = new JcrValue<Calendar>(valueFactories, type, value.getDate());
-                    break;
-                }
-                case PropertyType.DOUBLE: {
-                    jcrValue = new JcrValue<Double>(valueFactories, type, value.getDouble());
-                    break;
-                }
-                case PropertyType.LONG: {
-                    jcrValue = new JcrValue<Long>(valueFactories, type, value.getLong());
-                    break;
-                }
-                case PropertyType.REFERENCE: {
-                    try {
-                        jcrValue = new JcrValue<UUID>(valueFactories, type, UUID.fromString(value.getString()));
-                    } catch (IllegalArgumentException fallsThroughToString) {
-                    }
-                }
-                case PropertyType.NAME:
-                case PropertyType.PATH:
-                case PropertyType.STRING: {
-                    jcrValue = new JcrValue<String>(valueFactories, type, value.getString());
-                    break;
-                }
-                default: {
-                    throw new AssertionError("Unsupported PropertyType: " + value.getType());
-                }
-            }
-        }
+     * @see javax.jcr.Property#setValue(javax.jcr.Value[])
+     */
+    public void setValue( Value[] values ) throws ValueFormatException {
+        throw new ValueFormatException();
     }
-    */
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see javax.jcr.Property#setValue(java.lang.String[])
+     */
+    public void setValue( String[] values ) throws ValueFormatException {
+        throw new ValueFormatException();
+    }
 }

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyTypeUtil.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -0,0 +1,112 @@
+/*
+ * 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 javax.jcr.PropertyType;
+
+/**
+ * 
+ */
+class PropertyTypeUtil {
+
+    /**
+     * Compute the JCR {@link PropertyType} for the given DNA {@link org.jboss.dna.graph.property.PropertyType}.
+     * <p>
+     * See DNA-293 for complete discussion on why this method works the way it does. The best option appears to be basing the
+     * PropertyType on the first value, since that should be compatible with the PropertyType that was used when the values were
+     * set on the property in the first place.
+     * </p>
+     * 
+     * @param property the DNA property for which the {@link PropertyType} is to be determined; never null
+     * @return the JCR property type; always a valid value and never {@link PropertyType#UNDEFINED}.
+     */
+    static final int jcrPropertyTypeFor( org.jboss.dna.graph.property.Property property ) {
+        Object value = property.getFirstValue();
+
+        // Get the DNA property type for this ...
+        switch (org.jboss.dna.graph.property.PropertyType.discoverType(value)) {
+            case STRING:
+                return PropertyType.STRING;
+            case NAME:
+                return PropertyType.NAME;
+            case LONG:
+                return PropertyType.LONG;
+            case UUID:
+                return PropertyType.STRING; // JCR treats UUID properties as strings
+            case URI:
+                return PropertyType.STRING;
+            case PATH:
+                return PropertyType.PATH;
+            case BOOLEAN:
+                return PropertyType.BOOLEAN;
+            case DATE:
+                return PropertyType.DATE;
+            case DECIMAL:
+                return PropertyType.STRING; // better than losing information
+            case DOUBLE:
+                return PropertyType.DOUBLE;
+            case BINARY:
+                return PropertyType.BINARY;
+            case OBJECT:
+                return PropertyType.UNDEFINED;
+            case REFERENCE:
+                return PropertyType.REFERENCE;
+        }
+        assert false;
+        return PropertyType.UNDEFINED;
+    }
+
+    /**
+     * Compute the DNA {@link org.jboss.dna.graph.property.PropertyType} for the given JCR {@link PropertyType} value.
+     * 
+     * @param jcrPropertyType the DNA property for which the {@link PropertyType} is to be determined; never null
+     * @return the JCR property type; always a valid value and never {@link PropertyType#UNDEFINED}.
+     */
+    static final org.jboss.dna.graph.property.PropertyType dnaPropertyTypeFor( int jcrPropertyType ) {
+        // Make sure the value is the correct type ...
+        switch (jcrPropertyType) {
+            case PropertyType.STRING:
+                return org.jboss.dna.graph.property.PropertyType.STRING;
+            case PropertyType.BINARY:
+                return org.jboss.dna.graph.property.PropertyType.BINARY;
+            case PropertyType.BOOLEAN:
+                return org.jboss.dna.graph.property.PropertyType.BOOLEAN;
+            case PropertyType.DOUBLE:
+                return org.jboss.dna.graph.property.PropertyType.DOUBLE;
+            case PropertyType.LONG:
+                return org.jboss.dna.graph.property.PropertyType.LONG;
+            case PropertyType.DATE:
+                return org.jboss.dna.graph.property.PropertyType.DATE;
+            case PropertyType.PATH:
+                return org.jboss.dna.graph.property.PropertyType.PATH;
+            case PropertyType.NAME:
+                return org.jboss.dna.graph.property.PropertyType.NAME;
+            case PropertyType.REFERENCE:
+                return org.jboss.dna.graph.property.PropertyType.REFERENCE;
+        }
+        assert false;
+        return org.jboss.dna.graph.property.PropertyType.STRING;
+    }
+
+}


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

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -431,16 +431,74 @@
          * existing values will be replaced with the supplied value.
          * 
          * @param name the property name; may not be null
-         * @param propertyType the property type; must be a valid {@link PropertyType} value
-         * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
+         * @param value the new property values; may not be 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,
-                                 int propertyType,
-                                 Object value ) throws ConstraintViolationException {
-            Property dnaProp = propertyFactory.create(name, value);
-            setProperty(name, dnaProp, propertyType);
+                                 JcrValue value ) throws ConstraintViolationException {
+            assert name != null;
+            assert value != null;
+            JcrPropertyDefinition definition = null;
+            PropertyId id = null;
+
+            // Look for an existing property ...
+            PropertyInfo existing = node.getProperty(name);
+            if (existing != null) {
+                // Reuse the existing ID ...
+                id = existing.getPropertyId();
+
+                // We're replacing an existing property, but we still need to check that the property definition
+                // (still) defines a type. So, find the property definition for the existing property ...
+                definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId());
+
+                if (definition != null) {
+                    // The definition's require type must match the value's ...
+                    if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != value.getType()) {
+                        // 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(value)) definition = null;
+                    }
+                }
+            } else {
+                // This is a new property, so create a new ID ...
+                id = new PropertyId(node.getUuid(), name);
+            }
+            if (definition == null) {
+                // Look for a definition ...
+                definition = nodeTypes().findPropertyDefinition(node.getPrimaryTypeName(),
+                                                                node.getMixinTypeNames(),
+                                                                name,
+                                                                value,
+                                                                true,
+                                                                true);
+                if (definition == null) {
+                    throw new ConstraintViolationException();
+                }
+            }
+            // Create the DNA property ...
+            Object objValue = value.value();
+            int propertyType = definition.getRequiredType();
+            if (propertyType == PropertyType.UNDEFINED || propertyType == value.getType()) {
+                // Can use the values as is ...
+                propertyType = value.getType();
+            } else {
+                // A cast is required ...
+                org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
+                ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+                objValue = factory.create(value);
+            }
+            Property dnaProp = propertyFactory.create(name, objValue);
+
+            // Create the property info ...
+            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());
+            operations().set(dnaProp).on(currentLocation);
         }
 
         /**
@@ -448,63 +506,86 @@
          * existing values will be replaced with those that are supplied.
          * 
          * @param name the property name; may not be null
-         * @param propertyType the property type; must be a valid {@link PropertyType} value
-         * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
+         * @param values new property values, all of which must have the same {@link Value#getType() property type}; may not be
+         *        null or empty
          * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
          *         definition constraint
          */
         public void setProperty( Name name,
-                                 int propertyType,
-                                 Object[] value ) throws ConstraintViolationException {
-            Property dnaProp = propertyFactory.create(name, value);
-            setProperty(name, dnaProp, propertyType);
-        }
+                                 JcrValue[] values ) throws ConstraintViolationException {
+            assert name != null;
+            assert values != null;
+            int numValues = values.length;
+            assert numValues != 0;
+            JcrPropertyDefinition definition = null;
+            PropertyId id = null;
 
-        protected final void setProperty( Name name,
-                                          Property dnaProp,
-                                          int propertyType ) throws ConstraintViolationException {
-            assert propertyType != PropertyType.UNDEFINED;
+            // Look for an existing property ...
             PropertyInfo existing = node.getProperty(name);
-            PropertyInfo newProperty = null;
             if (existing != null) {
+                // Reuse the existing ID ...
+                id = existing.getPropertyId();
+
                 // We're replacing an existing property, but we still need to check that the property definition
-                // defines a type. So, find the property definition for the existing property ...
-                JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId());
-                if (definition == null && existing.getDefinitionId().allowsMultiple() && dnaProp.isSingle()) {
-                    // Look for a single-valued definition ...
-                    PropertyDefinitionId id = existing.getDefinitionId().asSingleValued();
-                    definition = nodeTypes().getPropertyDefinition(id);
+                // (still) defines a type. So, find the property definition for the existing property ...
+                definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId());
+
+                if (definition != null) {
+                    // The definition's require type must match the value's ...
+                    if (numValues == 0) {
+                        // Just use the definition as is ...
+                    } else {
+                        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.
+                            // 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;
+                        }
+                    }
                 }
+            } else {
+                // This is a new property, so create a new ID ...
+                id = new PropertyId(node.getUuid(), name);
+            }
+            if (definition == null) {
+                // Look for a definition ...
+                definition = nodeTypes().findPropertyDefinition(node.getPrimaryTypeName(),
+                                                                node.getMixinTypeNames(),
+                                                                name,
+                                                                values,
+                                                                true);
                 if (definition == null) {
-                    // Try to find a different (but existing) property definition ...
-                    definition = findBestPropertyDefintion(node.getPrimaryTypeName(),
-                                                           node.getMixinTypeNames(),
-                                                           dnaProp,
-                                                           propertyType,
-                                                           true);
-                }
-                if (definition == null) {
                     throw new ConstraintViolationException();
                 }
-
-                // Create the property info ...
-                newProperty = new PropertyInfo(existing.getPropertyId(), existing.getDefinitionId(), propertyType, dnaProp,
-                                               existing.isMultiValued());
+            }
+            // Create the DNA property ...
+            int type = values.length != 0 ? values[0].getType() : definition.getRequiredType();
+            Object[] objValues = new Object[values.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] = values[i].value();
+                }
             } else {
-                // It's a new property ...
-                PropertyId id = new PropertyId(node.getUuid(), name);
-                // Look find the property definition to use ...
-                JcrPropertyDefinition definition = findBestPropertyDefintion(node.getPrimaryTypeName(),
-                                                                             node.getMixinTypeNames(),
-                                                                             dnaProp,
-                                                                             propertyType,
-                                                                             true);
-                if (definition == null) {
-                    throw new ConstraintViolationException();
+                // A cast is required ...
+                assert propertyType != type;
+                org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(propertyType);
+                ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+                for (int i = 0; i != numValues; ++i) {
+                    objValues[i] = factory.create(values[i].value());
                 }
-                // Create the property info ...
-                newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
             }
+            Property dnaProp = propertyFactory.create(name, objValues);
+
+            // Create the property info ...
+            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());
             operations().set(dnaProp).on(currentLocation);
         }
@@ -571,7 +652,9 @@
             if (!definition.getId().equals(node.getDefinitionId())) {
                 // The node definition changed, so try to set the property ...
                 try {
-                    setProperty(DnaLexicon.NODE_DEFINITON, PropertyType.STRING, definition.getId().getString());
+                    JcrValue value = new JcrValue(context().getValueFactories(), 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).
                     // But we still want the node info to have the correct node definition.
@@ -1297,7 +1380,7 @@
             Name name = dnaProp.getName();
 
             // Figure out the JCR property type for this property ...
-            int propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+            int propertyType = PropertyTypeUtil.jcrPropertyTypeFor(dnaProp);
             PropertyDefinition propertyDefinition = findBestPropertyDefintion(primaryTypeName,
                                                                               mixinTypeNames,
                                                                               dnaProp,

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -39,7 +39,6 @@
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.Workspace;
-import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
@@ -277,56 +276,6 @@
         assertThat(prop.isSame(otherProp), is(false));
     }
 
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetBooleanValue() {
-        prop.setValue(false);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetCalendarValue() {
-        prop.setValue(Calendar.getInstance());
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetDoubleValue() {
-        prop.setValue(0.0);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetInputStreamValue() {
-        prop.setValue(Mockito.mock(InputStream.class));
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetLongValue() {
-        prop.setValue(0L);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetNodeValue() {
-        prop.setValue(Mockito.mock(Node.class));
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringValue() {
-        prop.setValue("");
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetStringArrayValue() {
-        prop.setValue(StringUtil.EMPTY_STRING_ARRAY);
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValueValue() {
-        prop.setValue(Mockito.mock(Value.class));
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotAllowSetValueArrayValue() {
-        prop.setValue(new Value[0]);
-    }
-
     private class MockAbstractJcrProperty extends AbstractJcrProperty {
 
         MockAbstractJcrProperty( SessionCache cache,
@@ -433,5 +382,95 @@
         public Value[] getValues() {
             return null;
         }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(javax.jcr.Value)
+         */
+        public void setValue( Value value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(javax.jcr.Value[])
+         */
+        public void setValue( Value[] values ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(java.lang.String)
+         */
+        public void setValue( String value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(java.lang.String[])
+         */
+        public void setValue( String[] values ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(java.io.InputStream)
+         */
+        public void setValue( InputStream value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(long)
+         */
+        public void setValue( long value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(double)
+         */
+        public void setValue( double value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(java.util.Calendar)
+         */
+        public void setValue( Calendar value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(boolean)
+         */
+        public void setValue( boolean value ) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see javax.jcr.Property#setValue(javax.jcr.Node)
+         */
+        public void setValue( Node value ) {
+            throw new UnsupportedOperationException();
+        }
     }
 }

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -32,6 +32,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.stub;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -325,12 +326,13 @@
 
     @Test
     public void shouldProvideValueFactory() throws Exception {
+        InputStream stream = new ByteArrayInputStream("something".getBytes());
         ValueFactory factory = session.getValueFactory();
         assertThat(factory, notNullValue());
         assertThat(factory.createValue(false), notNullValue());
         assertThat(factory.createValue(Calendar.getInstance()), notNullValue());
         assertThat(factory.createValue(0.0), notNullValue());
-        assertThat(factory.createValue(Mockito.mock(InputStream.class)), notNullValue());
+        assertThat(factory.createValue(stream), notNullValue());
         assertThat(factory.createValue(0L), notNullValue());
         Node node = Mockito.mock(Node.class);
         stub(node.getUUID()).toReturn(UUID.randomUUID().toString());

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-03-24 18:16:53 UTC (rev 794)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -23,6 +23,10 @@
  */
 package org.jboss.dna.jcr;
 
+import static javax.jcr.PropertyType.DOUBLE;
+import static javax.jcr.PropertyType.LONG;
+import static javax.jcr.PropertyType.STRING;
+import static javax.jcr.PropertyType.UNDEFINED;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsNull.nullValue;
@@ -34,15 +38,13 @@
 import static org.mockito.Mockito.stub;
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import javax.jcr.InvalidItemStateException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import org.jboss.dna.common.statistic.Stopwatch;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.graph.ExecutionContext;
@@ -54,7 +56,9 @@
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathNotFoundException;
 import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.jcr.SessionCache.NodeEditor;
+import org.jboss.dna.jcr.Vehicles.Lexicon;
 import org.jboss.dna.jcr.cache.Children;
 import org.jboss.dna.jcr.cache.NodeInfo;
 import org.jboss.dna.jcr.cache.PropertyInfo;
@@ -89,7 +93,7 @@
         cachesByName = new HashMap<String, SessionCache>();
 
         context = new ExecutionContext();
-        context.getNamespaceRegistry().register("vehix", "http://example.com/vehicles");
+        context.getNamespaceRegistry().register(Vehicles.Lexicon.Namespace.PREFIX, Vehicles.Lexicon.Namespace.URI);
 
         stub(session.getExecutionContext()).toReturn(context);
         stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
@@ -98,7 +102,7 @@
         JcrNodeTypeSource nodeTypeSource = null;
         nodeTypeSource = new JcrBuiltinNodeTypeSource(this.context, nodeTypeSource);
         nodeTypeSource = new DnaBuiltinNodeTypeSource(this.context, nodeTypeSource);
-        nodeTypeSource = new VehixNodeTypeSource(context, nodeTypeSource);
+        nodeTypeSource = new Vehicles.NodeTypeSource(context, nodeTypeSource);
         repoTypes = new RepositoryNodeTypeManager(context, nodeTypeSource);
         nodeTypes = new JcrNodeTypeManager(this.context, repoTypes);
         stub(session.nodeTypeManager()).toReturn(nodeTypes);
@@ -108,46 +112,6 @@
         cache = getCache("vehicles");
     }
 
-    /**
-     * Define the node types for the "vehix" namespace.
-     */
-    public static class VehixNodeTypeSource extends AbstractJcrNodeTypeSource {
-        private final List<JcrNodeType> nodeTypes;
-
-        public VehixNodeTypeSource( ExecutionContext context,
-                                    JcrNodeTypeSource predecessor ) {
-            super(predecessor);
-            this.nodeTypes = new ArrayList<JcrNodeType>();
-
-            Name carName = context.getValueFactories().getNameFactory().create("vehix:car");
-            Name aircraftName = context.getValueFactories().getNameFactory().create("vehix:aircraft");
-            JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
-
-            // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
-            JcrNodeType car = new JcrNodeType(context, (RepositoryNodeTypeManager)null, carName,
-                                              Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
-                                              NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
-
-            // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
-            JcrNodeType aircraft = new JcrNodeType(context, (RepositoryNodeTypeManager)null, aircraftName,
-                                                   Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
-                                                   NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
-
-            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredNodeTypes()
-         */
-        @Override
-        public Collection<JcrNodeType> getDeclaredNodeTypes() {
-            return nodeTypes;
-        }
-
-    }
-
     protected Graph createFrom( String repositoryName,
                                 String workspaceName,
                                 File file ) throws IOException, SAXException {
@@ -231,6 +195,11 @@
         }
     }
 
+    protected JcrValue value( int propertyType,
+                              Object value ) {
+        return new JcrValue(context.getValueFactories(), cache, propertyType, value);
+    }
+
     protected void assertDoesExist( Graph graph,
                                     Location location ) {
         Node node = store.getNodeAt(location);
@@ -296,6 +265,52 @@
         assertThat(info.getUuid(), is(uuid));
     }
 
+    protected void assertProperty( NodeInfo info,
+                                   Name propertyName,
+                                   int type,
+                                   Name nodeTypeName,
+                                   Name propertyDefinitionName,
+                                   int definitionType,
+                                   Object value ) {
+        PropertyDefinitionId defnId = new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, definitionType, false);
+        PropertyInfo propertyInfo = info.getProperty(propertyName);
+        assertThat(propertyInfo, is(notNullValue()));
+        assertThat(propertyInfo.getPropertyType(), is(type));
+        assertThat(propertyInfo.getDefinitionId(), is(defnId));
+        org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(type);
+        // Check the value ...
+        ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+        Object actual = factory.create(propertyInfo.getProperty().getFirstValue());
+        assertThat(actual, is(value));
+    }
+
+    protected void assertProperty( NodeInfo info,
+                                   Name propertyName,
+                                   int type,
+                                   Name nodeTypeName,
+                                   Name propertyDefinitionName,
+                                   int definitionType,
+                                   Object... values ) {
+        PropertyDefinitionId defnId = new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, definitionType, true);
+        PropertyInfo propertyInfo = info.getProperty(propertyName);
+        assertThat(propertyInfo, is(notNullValue()));
+        assertThat(propertyInfo.getPropertyType(), is(type));
+        assertThat(propertyInfo.getDefinitionId(), is(defnId));
+        org.jboss.dna.graph.property.PropertyType dnaPropertyType = PropertyTypeUtil.dnaPropertyTypeFor(type);
+        // Check the values ...
+        ValueFactory<?> factory = context.getValueFactories().getValueFactory(dnaPropertyType);
+        int i = 0;
+        for (Object value : propertyInfo.getProperty()) {
+            assertThat(factory.create(value), is(values[i++]));
+        }
+    }
+
+    protected void assertNoProperty( NodeInfo info,
+                                     Name propertyName ) {
+        PropertyInfo propertyInfo = info.getProperty(propertyName);
+        assertThat(propertyInfo, is(nullValue()));
+    }
+
     @Test
     public void shouldCreateWithValidParameters() {
         assertThat(cache, is(notNullValue()));
@@ -631,4 +646,91 @@
         assertDoesExist(store, sports.getLocation());
     }
 
+    @Test
+    public void shouldGetExistingPropertyOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3"));
+        assertProperty(lr3, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "LR3");
+        assertProperty(lr3, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Land Rover");
+        assertProperty(lr3, Lexicon.YEAR, LONG, Lexicon.CAR, Lexicon.YEAR, LONG, 2008L);
+        assertProperty(lr3, Lexicon.MSRP, STRING, Lexicon.CAR, Lexicon.MSRP, STRING, "$48,525");
+
+        NodeInfo db9 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Sports/vehix:Aston Martin DB9"));
+        assertProperty(db9, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "DB9");
+        assertProperty(db9, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Aston Martin");
+        assertProperty(db9, Lexicon.LENGTH_IN_INCHES, DOUBLE, Lexicon.CAR, Lexicon.LENGTH_IN_INCHES, DOUBLE, 185.5D);
+
+        NodeInfo b878 = cache.findNodeInfo(root.getUuid(),
+                                           path("/vehix:Vehicles/vehix:Aircraft/vehix:Commercial/vehix:Boeing 787"));
+        assertProperty(b878, Lexicon.MODEL, STRING, Lexicon.AIRCRAFT, Lexicon.MODEL, STRING, "787-3");
+        assertProperty(b878, Lexicon.MAKER, STRING, Lexicon.AIRCRAFT, Lexicon.MAKER, STRING, "Boeing");
+        assertProperty(b878, Lexicon.INTRODUCED, LONG, Lexicon.AIRCRAFT, Lexicon.INTRODUCED, LONG, 2009L);
+        assertProperty(b878, Lexicon.EMPTY_WEIGHT, STRING, JcrNtLexicon.UNSTRUCTURED, name("*"), UNDEFINED, "223000lb");
+
+    }
+
+    @Test
+    public void shouldNotFindNonExistantPropertyOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3"));
+        assertProperty(lr3, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "LR3");
+        assertProperty(lr3, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Land Rover");
+        assertNoProperty(lr3, Lexicon.LENGTH_IN_INCHES);
+        assertNoProperty(lr3, Lexicon.INTRODUCED);
+    }
+
+    @Test
+    public void shouldSetNewValueOnExistingPropertyOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3"));
+        assertProperty(lr3, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "LR3");
+        assertProperty(lr3, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Land Rover");
+        assertNoProperty(lr3, Lexicon.LENGTH_IN_INCHES);
+
+        SessionCache.NodeEditor editor = cache.getEditorFor(lr3.getUuid());
+        editor.setProperty(Lexicon.LENGTH_IN_INCHES, value(DOUBLE, 100.0D));
+    }
+
+    @Test
+    public void shouldSetNewPropertyOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo db9 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Sports/vehix:Aston Martin DB9"));
+        assertProperty(db9, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "DB9");
+        assertProperty(db9, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Aston Martin");
+        assertProperty(db9, Lexicon.LENGTH_IN_INCHES, DOUBLE, Lexicon.CAR, Lexicon.LENGTH_IN_INCHES, DOUBLE, 185.5D);
+
+        SessionCache.NodeEditor editor = cache.getEditorFor(db9.getUuid());
+        editor.setProperty(Lexicon.LENGTH_IN_INCHES, value(DOUBLE, 100.0D));
+    }
+
+    @Test( expected = ConstraintViolationException.class )
+    public void shouldFailToSetPropertyToInvalidValuesOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo db9 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Sports/vehix:Aston Martin DB9"));
+        assertProperty(db9, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "DB9");
+        assertProperty(db9, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Aston Martin");
+        assertProperty(db9, Lexicon.LENGTH_IN_INCHES, DOUBLE, Lexicon.CAR, Lexicon.LENGTH_IN_INCHES, DOUBLE, 185.5D);
+
+        SessionCache.NodeEditor editor = cache.getEditorFor(db9.getUuid());
+        editor.setProperty(Lexicon.LENGTH_IN_INCHES, value(STRING, "This is not a valid double"));
+    }
+
+    @Test
+    public void shouldRemoveExistingPropertyOnExistingNode() throws Exception {
+        NodeInfo root = cache.findNodeInfoForRoot();
+        NodeInfo db9 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Sports/vehix:Aston Martin DB9"));
+        assertProperty(db9, Lexicon.MODEL, STRING, Lexicon.CAR, Lexicon.MODEL, STRING, "DB9");
+        assertProperty(db9, Lexicon.MAKER, STRING, Lexicon.CAR, Lexicon.MAKER, STRING, "Aston Martin");
+        assertProperty(db9, Lexicon.LENGTH_IN_INCHES, DOUBLE, Lexicon.CAR, Lexicon.LENGTH_IN_INCHES, DOUBLE, 185.5D);
+
+        UUID db9uuid = db9.getUuid();
+        SessionCache.NodeEditor editor = cache.getEditorFor(db9uuid);
+        editor.removeProperty(Lexicon.LENGTH_IN_INCHES);
+
+        db9 = cache.findNodeInfo(db9uuid);
+        assertNoProperty(db9, Lexicon.LENGTH_IN_INCHES);
+
+        db9 = cache.findNodeInfo(root.getUuid(), path("/vehix:Vehicles/vehix:Cars/vehix:Sports/vehix:Aston Martin DB9"));
+        assertNoProperty(db9, Lexicon.LENGTH_IN_INCHES);
+    }
 }

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/Vehicles.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/Vehicles.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/Vehicles.java	2009-03-24 22:33:26 UTC (rev 795)
@@ -0,0 +1,170 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.jcr.PropertyType;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * Define the node types for the "vehix" namespace.
+ */
+public class Vehicles {
+
+    public static class Lexicon {
+        public static class Namespace {
+            public static final String URI = "http://example.com/vehicles";
+            public static final String PREFIX = "vehix";
+        }
+
+        public static final Name CAR = new BasicName(Namespace.URI, "car");
+        public static final Name AIRCRAFT = new BasicName(Namespace.URI, "aircraft");
+
+        public static final Name MAKER = new BasicName(Namespace.URI, "maker");
+        public static final Name MODEL = new BasicName(Namespace.URI, "model");
+        public static final Name INTRODUCED = new BasicName(Namespace.URI, "introduced");
+        public static final Name YEAR = new BasicName(Namespace.URI, "year");
+        public static final Name MSRP = new BasicName(Namespace.URI, "msrp");
+        public static final Name USER_RATING = new BasicName(Namespace.URI, "userRating");
+        public static final Name VALUE_RATING = new BasicName(Namespace.URI, "valueRating");
+        public static final Name MPG_CITY = new BasicName(Namespace.URI, "mpgCity");
+        public static final Name MPG_HIGHWAY = new BasicName(Namespace.URI, "mpgHighway");
+        public static final Name LENGTH_IN_INCHES = new BasicName(Namespace.URI, "lengthInInches");
+        public static final Name WHEELBASE_IN_INCHES = new BasicName(Namespace.URI, "wheelbaseInInches");
+        public static final Name ENGINE = new BasicName(Namespace.URI, "engine");
+
+        public static final Name EMPTY_WEIGHT = new BasicName(Namespace.URI, "emptyWeight");
+
+    }
+
+    public static class NodeTypeSource extends AbstractJcrNodeTypeSource {
+
+        private final List<JcrNodeType> nodeTypes;
+
+        public NodeTypeSource( ExecutionContext context,
+                               JcrNodeTypeSource predecessor ) {
+            super(predecessor);
+            this.nodeTypes = new ArrayList<JcrNodeType>();
+
+            JcrNodeType base = findType(JcrNtLexicon.BASE);
+            JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
+
+            // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
+            JcrNodeType car = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, Lexicon.CAR,
+                                              Arrays.asList(new JcrNodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+                                              Arrays.asList(new JcrPropertyDefinition[] {
+                                                  new JcrPropertyDefinition(context, null, Lexicon.MAKER,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.MODEL,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.INTRODUCED,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.LONG,
+                                                                            NO_CONSTRAINTS, false),
+                                                  /* Year IS mandatory for car */
+                                                  new JcrPropertyDefinition(context, null, Lexicon.YEAR,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            true, false, NO_DEFAULT_VALUES, PropertyType.LONG,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.MSRP,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.USER_RATING,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.DOUBLE,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.VALUE_RATING,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.DOUBLE,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.MPG_CITY,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.LONG,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.MPG_HIGHWAY,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.LONG,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.LENGTH_IN_INCHES,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.DOUBLE,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.WHEELBASE_IN_INCHES,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.DOUBLE,
+                                                                            NO_CONSTRAINTS, false),
+                                                  new JcrPropertyDefinition(context, null, Lexicon.ENGINE,
+                                                                            OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
+                                                                            false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
+                                                                            NO_CONSTRAINTS, false),}), NOT_MIXIN,
+                                              ORDERABLE_CHILD_NODES);
+
+            // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
+            JcrNodeType aircraft = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, Lexicon.AIRCRAFT,
+                                                   Arrays.asList(new JcrNodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
+                                                   NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+                                                       new JcrPropertyDefinition(context, null, Lexicon.MAKER,
+                                                                                 OnParentVersionBehavior.COMPUTE.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                       new JcrPropertyDefinition(context, null, Lexicon.MODEL,
+                                                                                 OnParentVersionBehavior.COMPUTE.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.STRING, NO_CONSTRAINTS, false),
+                                                       /* Year is NOT mandatory for aircraft */
+                                                       new JcrPropertyDefinition(context, null, Lexicon.YEAR,
+                                                                                 OnParentVersionBehavior.COMPUTE.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.LONG, NO_CONSTRAINTS, false),
+                                                       new JcrPropertyDefinition(context, null, Lexicon.INTRODUCED,
+                                                                                 OnParentVersionBehavior.COMPUTE.getJcrValue(),
+                                                                                 false, false, false, NO_DEFAULT_VALUES,
+                                                                                 PropertyType.LONG, NO_CONSTRAINTS, false),}),
+                                                   NOT_MIXIN, ORDERABLE_CHILD_NODES);
+
+            nodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredNodeTypes()
+         */
+        @Override
+        public Collection<JcrNodeType> getDeclaredNodeTypes() {
+            return nodeTypes;
+        }
+
+    }
+}


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



