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.
+ */
+@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 ...