Index: dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java (working copy) @@ -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 mixinNodeTypes = new ArrayList((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[0]); } /** @@ -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,20 @@ /** * {@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 matchingProps = new HashSet(); + for (Property prop : properties) { + String propName = prop.getName(); + if (propName.equals(namePattern)) + matchingProps.add(prop); + } + + return new JcrPropertyIterator(matchingProps); } /** @@ -522,7 +552,20 @@ * @return false * @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; } Index: dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java (working copy) @@ -28,6 +28,7 @@ import java.util.Calendar; import java.util.Date; import java.util.UUID; + import javax.jcr.Item; import javax.jcr.ItemVisitor; import javax.jcr.Node; @@ -36,7 +37,9 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; + 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; @@ -48,13 +51,13 @@ */ @NotThreadSafe abstract class AbstractJcrProperty extends AbstractJcrItem implements Property { - + private final Node node; private final ExecutionContext executionContext; private final Name name; AbstractJcrProperty( Node node, - ExecutionContext executionContext, + ExecutionContext executionContext, Name name ) { assert node != null; assert executionContext != null; Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java (revision 0) @@ -0,0 +1,333 @@ +/* + * 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 org.jboss.dna.graph.property.Name; +import org.jboss.dna.graph.property.basic.BasicName; + +/** + * {@link JcrNodeTypeSource} that provides built-in node types per spec. + * NOTE: This class is not complete and does not yet provide all built-in + * types. + */ +class JcrBuiltinNodeTypeSource implements JcrNodeTypeSource { + + private static final Value[] NO_DEFAULT_VALUES = new Value[0]; + private static final String[] NO_CONSTRAINTS = new String[0]; + private static final List NO_SUPERTYPES = Collections.emptyList(); + private static final List NO_CHILD_NODES = Collections.emptyList(); + private static final List NO_PROPERTIES = Collections.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; + + private final List primaryNodeTypes; + private final List mixinNodeTypes; + + JcrBuiltinNodeTypeSource(JcrSession session) { + primaryNodeTypes = new ArrayList(); + + 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, + 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, true, 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, childNodeDefinition, new NodeType[] { childNodeDefinition }), + new JcrNodeDefinition(session, null, JcrLexicon.PROPERTY_DEFINITION, OnParentVersionBehavior.VERSION.getJcrValue(), false, false, false, true, propertyDefinition, 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[] { + //TODO: Need to make the default type unstructured as well + new JcrNodeDefinition(session, null, ALL_NODES, OnParentVersionBehavior.VERSION.getJcrValue(), false, false, false, true, base, 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, version, new NodeType[] { version }), + new JcrNodeDefinition(session, null, JcrLexicon.VERSION_LABELS, OnParentVersionBehavior.ABORT.getJcrValue(), true, true, true, false, versionLabels, new NodeType[] { versionLabels }), + new JcrNodeDefinition(session, null, ALL_NODES, OnParentVersionBehavior.ABORT.getJcrValue(), false, false, true, false, 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 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 getMixinNodeTypes() { + return mixinNodeTypes; + } + + /** + * {@inheritDoc} + * + * @see org.jboss.dna.jcr.JcrNodeTypeSource#getPrimaryNodeTypes() + */ + public Collection getPrimaryNodeTypes() { + return primaryNodeTypes; + } + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java (revision 0) @@ -0,0 +1,114 @@ +/* + * 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 org.jboss.dna.graph.property.Name; + +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; + } + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java (working copy) @@ -23,10 +23,56 @@ */ package org.jboss.dna.jcr; +import org.jboss.dna.graph.property.Name; +import org.jboss.dna.graph.property.basic.BasicName; + /** * @author Randall Hauch */ 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 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"); + } Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMixLexicon.java (revision 0) @@ -0,0 +1,5 @@ +package org.jboss.dna.jcr; + +public class JcrMixLexicon extends org.jboss.dna.graph.JcrMixLexicon { + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java (revision 0) @@ -0,0 +1,92 @@ +/* + * 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.NodeDefinition; +import javax.jcr.nodetype.NodeType; + +import org.jboss.dna.graph.property.Name; + +/** + * @author Brian + * + */ +class JcrNodeDefinition extends JcrItemDefinition implements NodeDefinition { + + private final boolean allowsSameNameSiblings; + private final NodeType defaultPrimaryType; + private final NodeType[] requiredPrimaryTypes; + + JcrNodeDefinition(JcrSession session, NodeType declaringNodeType, Name name, + int onParentVersion, boolean autoCreated, boolean mandatory, + boolean protectedItem, boolean allowsSameNameSiblings, + NodeType defaultPrimaryType, NodeType[] requiredPrimaryTypes) { + super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, + protectedItem); + this.allowsSameNameSiblings = allowsSameNameSiblings; + this.defaultPrimaryType = defaultPrimaryType; + 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() { + return defaultPrimaryType; + } + + /** + * {@inheritDoc} + * + * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes() + */ + public NodeType[] getRequiredPrimaryTypes() { + return requiredPrimaryTypes; + } + + /** + * Creates a new JcrNodeDefinition that is identical to the current + * object, but with the given declaringNodeType. + * Provided to support immutable pattern for this class. + * @param declaringNodeType the declaring node type for the new JcrNodeDefinition + * @return a new JcrNodeDefinition that is identical to the current + * object, but with the given declaringNodeType. + */ + JcrNodeDefinition with(NodeType declaringNodeType) { + return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), + isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(), + defaultPrimaryType, requiredPrimaryTypes); + } +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java (revision 0) @@ -0,0 +1,493 @@ +/* + * 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. + */ +@Immutable +class JcrNodeType implements NodeType { + + private final Name name; + private final Name primaryItemName; + private final Set childNodeDefinitions; + private final Set propertyDefinitions; + private final List declaredSupertypes; + + private boolean mixin; + private boolean orderableChildNodes; + + // Somewhat oddly, we need a reference to the session to support namespace remapping + private JcrSession session; + + JcrNodeType(JcrSession session, Name name, List declaredSupertypes, Name primaryItemName, Collection childNodeDefinitions, Collection propertyDefinitions, boolean mixin, boolean orderableChildNodes) { + this.session = session; + this.name = name; + this.primaryItemName = primaryItemName; + this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.emptyList(); + this.mixin = mixin; + this.orderableChildNodes = orderableChildNodes; + this.propertyDefinitions = new HashSet(propertyDefinitions.size()); + for (JcrPropertyDefinition property : propertyDefinitions) { + this.propertyDefinitions.add(property.with(this)); + } + + this.childNodeDefinitions = new HashSet(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 relPath) { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String, java.lang.String) + */ + public boolean canAddChildNode(String relPath, String primaryNodeTypeName) { + throw new UnsupportedOperationException(); + } + + /** + * {@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: + *
    + *
  1. If the value is null, return the value of {@link JcrNodeType#canRemoveItem(String)}
  2. + *
  3. If the property definition has a no required type ({@link PropertyType#UNDEFINED}), return true
  4. + *
  5. Compare the type of the given value to the required type and see if they are compatible
  6. + *
+ * + * @param property the property to be set + * @param value the value to set (may be null) + * @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 true if the following algorithm would return true when applied + * to each non-null value in values: + *
    + *
  1. If the value is null, return the value of {@link JcrNodeType#canRemoveItem(String)}
  2. + *
  3. If the property definition has a no required type ({@link PropertyType#UNDEFINED}), return true
  4. + *
  5. Compare the type of the given value to the required type and see if they are compatible
  6. + *
+ * + * @param property the property to be set + * @param values the array of values to set (may be null) + * @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 nodeDefs = new HashSet(); + 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 propDefs = new HashSet(); + 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 supertypes = new HashSet(); + Stack unvisitedSupertypes = new Stack(); + + 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.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; + } +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeIterator.java (revision 0) @@ -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. + * + * 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; + +/** + * Type-safe {@link Iterator} implementation for NodeTypes, as per the JCR specification. + */ +final class JcrNodeTypeIterator implements NodeTypeIterator { + + private int size; + private int position; + private Iterator iterator; + + JcrNodeTypeIterator(Collection 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"); + } + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeSource.java (revision 0) @@ -0,0 +1,22 @@ +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 { + + // For now, this should be sufficiently self-explanatory + public Collection getPrimaryNodeTypes(); + public Collection getMixinNodeTypes(); + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNtLexicon.java (revision 0) @@ -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"); + +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java (revision 0) @@ -0,0 +1,103 @@ +/* + * 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; + +@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 JcrPropertyDefinition that is identical to the current + * object, but with the given declaringNodeType. + * Provided to support immutable pattern for this class. + * @param declaringNodeType the declaring node type for the new JcrPropertyDefinition + * @return a new JcrPropertyDefinition that is identical to the current + * object, but with the given declaringNodeType. + */ + 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()); + } +} Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java (working copy) @@ -36,10 +36,12 @@ private final Iterator iterator; private int ndx; + private int size; JcrPropertyIterator( Set properties ) { assert properties != null; iterator = properties.iterator(); + size = properties.size(); } /** @@ -54,11 +56,10 @@ /** * {@inheritDoc} * - * @return -1L * @see javax.jcr.RangeIterator#getSize() */ public long getSize() { - return -1L; + return size; } /** Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java (working copy) @@ -28,11 +28,15 @@ import java.security.AccessControlException; import java.security.Principal; import java.util.Calendar; +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.NamespaceException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.Property; @@ -46,17 +50,22 @@ import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; + import net.jcip.annotations.NotThreadSafe; + import org.jboss.dna.common.util.CheckArg; import org.jboss.dna.common.util.StringUtil; import org.jboss.dna.graph.DnaLexicon; import org.jboss.dna.graph.ExecutionContext; import org.jboss.dna.graph.Graph; +import org.jboss.dna.graph.JcrNtLexicon; import org.jboss.dna.graph.property.Name; +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.xml.sax.ContentHandler; + import com.google.common.base.ReferenceType; import com.google.common.collect.ReferenceMap; @@ -72,6 +81,7 @@ private final ExecutionContext executionContext; private final ReferenceMap nodesByUuid; private final ReferenceMap nodesByJcrUuid; + private final JcrNamespaceRegistry sessionNamespaceRegistry; private boolean isLive; private Workspace workspace; private JcrRootNode rootNode; @@ -86,17 +96,41 @@ assert nodesByUuid != null; this.repository = repository; this.graph = graph; - this.executionContext = graph.getContext(); - assert this.executionContext != null; + ExecutionContext executionContext = graph.getContext(); + assert executionContext != null; this.nodesByUuid = nodesByUuid; this.nodesByJcrUuid = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); this.isLive = true; // Following must be initialized after session's state is initialized this.workspace = new JcrWorkspace(this, workspaceName); + + // Following must be set after the workspace's state is initialized + /* + * The workspace must be created before the namespace registry in the execution context + * is redirected. This allows the workspace namespace registry to point to the permanent + * DNA namespace registry while the execution context also supports the transient + * namespace remappings from the session/ + */ + DelegatingNamespaceRegistry dnr = new DelegatingNamespaceRegistry(executionContext.getNamespaceRegistry()); + this.executionContext = executionContext.with(dnr); + assert this.executionContext != null; + + /* + * Create a session-scoped namespace registry to ensure that the session namespace methods have the same semantics + * as the workspace namespace methods. + */ + + sessionNamespaceRegistry = new JcrNamespaceRegistry(dnr); + } ExecutionContext getExecutionContext() { - return this.executionContext; + //TODO: This is a hack to create the JcrWorkspace before the final execution context is set. + if (this.executionContext == null) { + return graph.getContext(); + } + + return this.executionContext; } /** @@ -261,7 +295,7 @@ * @see javax.jcr.Session#getNamespacePrefix(java.lang.String) */ public String getNamespacePrefix( String uri ) throws RepositoryException { - return workspace.getNamespaceRegistry().getPrefix(uri); + return sessionNamespaceRegistry.getPrefix(uri); } /** @@ -269,8 +303,8 @@ * * @see javax.jcr.Session#getNamespacePrefixes() */ - public String[] getNamespacePrefixes() throws RepositoryException { - return workspace.getNamespaceRegistry().getPrefixes(); + public String[] getNamespacePrefixes() { + return sessionNamespaceRegistry.getPrefixes(); } /** @@ -279,7 +313,7 @@ * @see javax.jcr.Session#getNamespaceURI(java.lang.String) */ public String getNamespaceURI( String prefix ) throws RepositoryException { - return workspace.getNamespaceRegistry().getURI(prefix); + return sessionNamespaceRegistry.getURI(prefix); } private Node getNode( Path path ) throws RepositoryException { @@ -344,6 +378,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: This is ugly + rootNode.properties.add(primaryType); + return rootNode; } @@ -368,39 +411,39 @@ public ValueFactory getValueFactory() { final ValueFactories valueFactories = executionContext.getValueFactories(); return new ValueFactory() { - public Value createValue( String value, - int propertyType ) { - return new JcrValue(valueFactories, propertyType, value); - } - - public Value createValue( Node value ) throws RepositoryException { - return new JcrValue(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID())); - } - - public Value createValue( InputStream value ) { - return new JcrValue(valueFactories, PropertyType.BINARY, value); - } - - public Value createValue( Calendar value ) { - return new JcrValue(valueFactories, PropertyType.DATE, value); - } - - public Value createValue( boolean value ) { - return new JcrValue(valueFactories, PropertyType.BOOLEAN, value); - } - - public Value createValue( double value ) { - return new JcrValue(valueFactories, PropertyType.DOUBLE, value); - } - - public Value createValue( long value ) { - return new JcrValue(valueFactories, PropertyType.LONG, value); - } - - public Value createValue( String value ) { - return new JcrValue(valueFactories, PropertyType.STRING, value); - } + int propertyType ) { + return new JcrValue(valueFactories, propertyType, value); + } + + public Value createValue( Node value ) throws RepositoryException { + return new JcrValue(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID())); + } + + public Value createValue( InputStream value ) { + return new JcrValue(valueFactories, PropertyType.BINARY, value); + } + + public Value createValue( Calendar value ) { + return new JcrValue(valueFactories, PropertyType.DATE, value); + } + + public Value createValue( boolean value ) { + return new JcrValue(valueFactories, PropertyType.BOOLEAN, value); + } + + public Value createValue( double value ) { + return new JcrValue(valueFactories, PropertyType.DOUBLE, value); + } + + public Value createValue( long value ) { + return new JcrValue(valueFactories, PropertyType.LONG, value); + } + + public Value createValue( String value ) { + return new JcrValue(valueFactories, PropertyType.STRING, value); + } + }; } @@ -577,11 +620,139 @@ /** * {@inheritDoc} * - * @throws UnsupportedOperationException always * @see javax.jcr.Session#setNamespacePrefix(java.lang.String, java.lang.String) */ public void setNamespacePrefix( String newPrefix, - String existingUri ) { - throw new UnsupportedOperationException(); + String existingUri ) + throws NamespaceException, RepositoryException + { + + try { + String preexistingUri = sessionNamespaceRegistry.getURI(newPrefix); + throw new NamespaceException("Prefix " + newPrefix + " is already mapped to " + + preexistingUri); + } + catch (NamespaceException ne) { + // No error. This indicates that the prefix is not already mapped. + } + + // This will throw a NamespaceException for us if the URI is NOT already mapped. + sessionNamespaceRegistry.getPrefix(existingUri); + + if (newPrefix.length() > 2 && "xml".equalsIgnoreCase(newPrefix.substring(0, 3))) { + throw new NamespaceException("Invalid attempt to remap URL to a prefix beginning with 'xml'."); + } + + // Get the delegating namespace registry so that the change is transient. + executionContext.getNamespaceRegistry().register(newPrefix, existingUri); } + + class DelegatingNamespaceRegistry implements NamespaceRegistry { + private final NamespaceRegistry delegate; + private final Map remappedPrefixes; + private final Map remappedURIs; + + DelegatingNamespaceRegistry(NamespaceRegistry delegate) { + this.delegate = delegate; + + this.remappedPrefixes = new HashMap(); + this.remappedURIs = new HashMap(); + } + + + /** + * {@inheritDoc} + * + * @see org.jboss.dna.graph.property.NamespaceRegistry#getDefaultNamespaceUri() + */ + public String getDefaultNamespaceUri() { + return delegate.getDefaultNamespaceUri(); + } + + /** + * {@inheritDoc} + * + * @see org.jboss.dna.graph.property.NamespaceRegistry#getNamespaceForPrefix(String) + */ + public String getNamespaceForPrefix( String prefix ) { + String remappedUri = remappedPrefixes.get(prefix); + if (remappedUri != null) { + return remappedUri; + } + + return delegate.getNamespaceForPrefix(prefix); + } + + /** + * {@inheritDoc} + * + * @see org.jboss.dna.graph.property.NamespaceRegistry#getPrefixForNamespaceUri(String,boolean) + */ + public String getPrefixForNamespaceUri( String namespaceUri, + boolean generateIfMissing ) { + String remappedPrefix = remappedURIs.get(namespaceUri); + if (remappedPrefix != null) { + return remappedPrefix; + } + + return delegate.getPrefixForNamespaceUri(namespaceUri, generateIfMissing); + } + + /** + * {@inheritDoc} + * + * @see org.jboss.dna.graph.property.NamespaceRegistry#isRegisteredNamespaceUri(String) + */ + public boolean isRegisteredNamespaceUri( String namespaceUri ) { + return delegate.isRegisteredNamespaceUri(namespaceUri); + } + + /** + * {@inheritDoc} + * Note: This implementation will NOT delegate registry updates to the underlying registry. + * @see org.jboss.dna.graph.property.NamespaceRegistry#register(String, String) + */ + public String register( String prefix, + String namespaceUri ) { + String oldPrefix = remappedURIs.get(namespaceUri); + if (oldPrefix == null) { + oldPrefix = delegate.getPrefixForNamespaceUri(namespaceUri, false); + } + + remappedPrefixes.put(prefix, namespaceUri); + remappedURIs.put(namespaceUri, prefix); + + return oldPrefix; + } + + /** + * {@inheritDoc} + * + * @throws UnsupportedOperationException always thrown + * @see org.jboss.dna.graph.property.NamespaceRegistry#unregister(String) + */ + public boolean unregister( String namespaceUri ) { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * Note: This implementation returns these URIs directly from the delegate. + * @see org.jboss.dna.graph.property.NamespaceRegistry#getRegisteredNamespaceUris() + */ + public Set getRegisteredNamespaceUris() { + return delegate.getRegisteredNamespaceUris(); + } + + /** + * {@inheritDoc} + * + * @throws UnsupportedOperationException always thrown + * @see org.jboss.dna.graph.property.NamespaceRegistry#getNamespaces() + */ + public Set getNamespaces() { + throw new UnsupportedOperationException(); + } + } + } Index: dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java (revision 731) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java (working copy) @@ -24,14 +24,23 @@ package org.jboss.dna.jcr; 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 javax.jcr.NamespaceRegistry; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Workspace; +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 org.jboss.dna.graph.property.Name; import org.xml.sax.ContentHandler; /** @@ -40,15 +49,19 @@ */ final class JcrWorkspace implements Workspace { + // Kept at protected visibility so that it is visible to inner class (JcrNodeTypeManager) + protected final JcrSession session; + private final String name; - private final JcrSession session; private final NamespaceRegistry namespaceRegistry; + private final NodeTypeManager nodeTypeManager; /** * @param session the session that owns this workspace; may not be null * @param name the name of the workspace; may not be null * @throws RepositoryException */ + @SuppressWarnings("synthetic-access") JcrWorkspace( JcrSession session, String name ) throws RepositoryException { assert session != null; @@ -56,6 +69,7 @@ this.session = session; this.name = name; this.namespaceRegistry = new JcrNamespaceRegistry(session.getExecutionContext().getNamespaceRegistry()); + this.nodeTypeManager = new JcrNodeTypeManager(new JcrBuiltinNodeTypeSource(session)); // Ensure workspace with supplied name is accessible // if (name == null) name = session.getDnaRepository().getSource(session.getSubject()).getName(); // String matchedName = null; @@ -154,7 +168,7 @@ * {@inheritDoc} */ public NodeTypeManager getNodeTypeManager() { - throw new UnsupportedOperationException(); + return nodeTypeManager; } /** @@ -202,4 +216,95 @@ 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. + */ + private class JcrNodeTypeManager implements NodeTypeManager { + + private final Map primaryNodeTypes; + private final Map mixinNodeTypes; + + private JcrNodeTypeManager(JcrNodeTypeSource source) { + Collection primary = source.getPrimaryNodeTypes(); + Collection mixins = source.getMixinNodeTypes(); + + primaryNodeTypes = new HashMap(primary.size()); + for(JcrNodeType nodeType : primary) { + primaryNodeTypes.put(nodeType.getInternalName(), nodeType); + } + + mixinNodeTypes = new HashMap(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 allTypes = new ArrayList(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) + */ + 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("No node type named: " + nodeTypeName); + } + + /** + * {@inheritDoc} + * + * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes() + */ + public NodeTypeIterator getPrimaryNodeTypes() { + return new JcrNodeTypeIterator(primaryNodeTypes.values()); + } + + } + +} \ No newline at end of file Index: dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java =================================================================== --- dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java (revision 0) +++ dna-jcr/src/main/java/org/jboss/dna/jcr/OnParentVersionBehavior.java (revision 0) @@ -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 javax.jcr.version.OnParentVersionAction; + +/** + * Enumeration of possible behaviors for on-parent-version setting of properties and child nodes + * in JCR specification. + * + */ +public enum OnParentVersionBehavior { + ABORT(OnParentVersionAction.ABORT, OnParentVersionAction.ACTIONNAME_ABORT), + COMPUTE(OnParentVersionAction.COMPUTE, OnParentVersionAction.ACTIONNAME_COMPUTE), + COPY(OnParentVersionAction.COPY, OnParentVersionAction.ACTIONNAME_COPY), + IGNORE(OnParentVersionAction.IGNORE, OnParentVersionAction.ACTIONNAME_IGNORE), + INITIALIZE(OnParentVersionAction.INITIALIZE, OnParentVersionAction.ACTIONNAME_INITIALIZE), + 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 fromOnParentVersionAction(int onParentVersionAction) { + for (OnParentVersionBehavior opvb : OnParentVersionBehavior.values()) { + if (opvb.jcrValue == onParentVersionAction) { + return opvb; + } + } + + throw new IllegalStateException("No matching version for value: " + onParentVersionAction); + } +} Index: dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java =================================================================== --- dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java (revision 732) +++ dna-jcr/src/test/java/org/jboss/dna/jcr/JackrabbitJcrTckTest.java (working copy) @@ -73,30 +73,30 @@ // 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.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.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.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.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.WorkspaceReadMethodsTest.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); + 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); @@ -113,7 +113,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()); } } Index: dna-jcr/src/test/resources/repositoryJackRabbitTck.xml =================================================================== --- dna-jcr/src/test/resources/repositoryJackRabbitTck.xml (revision 736) +++ dna-jcr/src/test/resources/repositoryJackRabbitTck.xml (working copy) @@ -24,9 +24,11 @@ ~ 51 Franklin Street, Fifth Floor ~ Boston, MA 02110-1301 USA --> - - - - - + + + + + \ No newline at end of file