[dna-commits] DNA SVN: r788 - in trunk/dna-jcr/src: main/java/org/jboss/dna/jcr/cache and 2 other directories.
dna-commits at lists.jboss.org
dna-commits at lists.jboss.org
Mon Mar 23 22:16:35 EDT 2009
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>"nt:base"</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 ...
More information about the dna-commits
mailing list