[dna-commits] DNA SVN: r820 - in trunk/dna-jcr/src/main: resources/org/jboss/dna/jcr and 1 other directory.
dna-commits at lists.jboss.org
dna-commits at lists.jboss.org
Tue Apr 14 12:13:03 EDT 2009
Author: rhauch
Date: 2009-04-14 12:13:03 -0400 (Tue, 14 Apr 2009)
New Revision: 820
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
Log:
DNA-339 JCR Type Registration and Unregistration
Applying the patch, with some very minor modifications (particularly, some additional JavaDoc in NodeDefinitionId and PropertyDefinitionId). The Key classes in JcrPropertyDefinition and JcrNodeDefinition should be replaced with PropertyDefinitionId and NodeDefinitionId, respectively, but this can be done in another change set. The current approach works, but may actually not match the specification. (The JavaDoc added to the *DefinitionId classes helps to explain what information exactly is used to distinguish the definitions, and the Key classes use a different set of information.)
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -76,6 +76,7 @@
public static I18n errorWhileFindingNodeWithUuid;
public static I18n errorWhileFindingNodeWithPath;
public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
+ public static I18n noSnsDefinitionForNode;
public static I18n missingNodeTypeForExistingNode;
public static I18n unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
public static I18n unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
@@ -109,6 +110,23 @@
public static I18n notStoredQuery;
public static I18n invalidQueryLanguage;
+ // Type registration messages
+ public static I18n invalidNodeTypeName;
+ public static I18n nodeTypeAlreadyExists;
+ public static I18n invalidPrimaryTypeName;
+ public static I18n invalidSupertypeName;
+ public static I18n supertypesConflict;
+ public static I18n ambiguousPrimaryItemName;
+ public static I18n invalidPrimaryItemName;
+ public static I18n autocreatedNodesNeedDefaults;
+ public static I18n residualDefinitionsCannotBeMandatory;
+ public static I18n cannotOverrideProtectedDefinition;
+ public static I18n cannotMakeMandatoryDefinitionOptional;
+ public static I18n constraintsChangedInSubtype;
+ public static I18n cannotRedefineProperty;
+ public static I18n autocreatedPropertyNeedsDefault;
+ public static I18n singleValuedPropertyNeedsSingleValuedDefault;
+
static {
try {
I18n.initialize(JcrI18n.class);
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -27,11 +27,15 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Value;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
/**
* DNA implementation of the {@link NodeDefinition} class.
@@ -218,4 +222,73 @@
return getId().toString();
}
+ Key getKey(boolean includeTypes) {
+ return new Key(this, includeTypes);
+ }
+
+ /**
+ * Internal class that encapsulates composite unique identifier for child node definitions
+ */
+ class Key {
+ String keyString;
+
+ Key( Node node,
+ NamespaceRegistry registry ) throws Exception {
+ StringBuffer buff = new StringBuffer();
+
+ try {
+ String nodeName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+ buff.append(nodeName);
+ } catch (PathNotFoundException pnfe) {
+ // Ignorable, means name is not set.
+ buff.append(JcrNodeType.RESIDUAL_ITEM_NAME);
+ }
+
+ try {
+ Value[] requiredTypes = node.getProperty(JcrLexicon.REQUIRED_PRIMARY_TYPES.getString(registry)).getValues();
+
+ for (int i = 0; i < requiredTypes.length; i++) {
+ buff.append('_').append(requiredTypes[i].getString());
+ }
+ } catch (PathNotFoundException pnfe) {
+ // No required types. Weird, but not debilitating.
+ }
+
+ buff.append('_').append(node.getProperty(JcrLexicon.SAME_NAME_SIBLINGS.getString(registry)).getBoolean());
+
+ this.keyString = buff.toString();
+ }
+
+ Key( NodeDefinition def,
+ boolean includeTypes ) {
+ StringBuffer buff = new StringBuffer();
+ buff.append(def.getName());
+
+ if (includeTypes) {
+ NodeType[] requiredTypes = def.getRequiredPrimaryTypes();
+ for (int i = 0; i < requiredTypes.length; i++) {
+ buff.append('_').append(requiredTypes[i].getName());
+ }
+ }
+
+ buff.append('_').append(def.allowsSameNameSiblings());
+
+ this.keyString = buff.toString();
+ }
+
+ @Override
+ public boolean equals( Object ob ) {
+ if (!(ob instanceof Key)) {
+ return false;
+ }
+
+ return keyString.equals(((Key)ob).keyString);
+ }
+
+ @Override
+ public int hashCode() {
+ return keyString.hashCode();
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -34,7 +34,6 @@
import java.util.Set;
import javax.jcr.PropertyType;
import javax.jcr.Value;
-import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.Immutable;
@@ -334,7 +333,7 @@
*
* @see javax.jcr.nodetype.NodeType#getDeclaredChildNodeDefinitions()
*/
- public NodeDefinition[] getDeclaredChildNodeDefinitions() {
+ public JcrNodeDefinition[] getDeclaredChildNodeDefinitions() {
// Always have to make a copy to prevent changes ...
return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
}
@@ -344,10 +343,10 @@
*
* @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions()
*/
- public NodeDefinition[] getChildNodeDefinitions() {
+ public JcrNodeDefinition[] getChildNodeDefinitions() {
// Always have to make a copy to prevent changes ...
Collection<JcrNodeDefinition> definitions = this.allDefinitions.allChildNodeDefinitions();
- return definitions.toArray(new NodeDefinition[definitions.size()]);
+ return definitions.toArray(new JcrNodeDefinition[definitions.size()]);
}
/**
@@ -355,10 +354,10 @@
*
* @see javax.jcr.nodetype.NodeType#getPropertyDefinitions()
*/
- public PropertyDefinition[] getPropertyDefinitions() {
+ public JcrPropertyDefinition[] getPropertyDefinitions() {
// Always have to make a copy to prevent changes ...
Collection<JcrPropertyDefinition> definitions = this.allDefinitions.allPropertyDefinitions();
- return definitions.toArray(new PropertyDefinition[definitions.size()]);
+ return definitions.toArray(new JcrPropertyDefinition[definitions.size()]);
}
/**
@@ -366,9 +365,9 @@
*
* @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes()
*/
- public NodeType[] getDeclaredSupertypes() {
+ public JcrNodeType[] getDeclaredSupertypes() {
// Always have to make a copy to prevent changes ...
- return declaredSupertypes.toArray(new NodeType[declaredSupertypes.size()]);
+ return declaredSupertypes.toArray(new JcrNodeType[declaredSupertypes.size()]);
}
/**
@@ -382,16 +381,26 @@
}
/**
- * Returns the internal {@link Name} object for the note type. This method exists outside the JCR API and should not be
+ * Returns the internal {@link Name} object for the node type. This method exists outside the JCR API and should not be
* exposed outside of the package.
*
- * @return the internal {@link Name} object for the note type.
+ * @return the internal {@link Name} object for the node type.
*/
Name getInternalName() {
return name;
}
/**
+ * Returns the internal {@link Name} object for the primary item of this node type. This method exists outside the JCR API and
+ * should not be exposed outside of the package.
+ *
+ * @return the internal {@link Name} object for the primary item of this node type.
+ */
+ Name getInternalPrimaryItemName() {
+ return primaryItemName;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeType#getPrimaryItemName()
@@ -410,7 +419,7 @@
*
* @see javax.jcr.nodetype.NodeType#getDeclaredPropertyDefinitions()
*/
- public PropertyDefinition[] getDeclaredPropertyDefinitions() {
+ public JcrPropertyDefinition[] getDeclaredPropertyDefinitions() {
return propertyDefinitions.toArray(new JcrPropertyDefinition[propertyDefinitions.size()]);
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -26,6 +26,7 @@
import java.util.UUID;
import java.util.regex.Pattern;
import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
@@ -36,6 +37,7 @@
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.ValueFactories;
@@ -139,6 +141,20 @@
}
/**
+ * Creates a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+ * <code>context</code>. Provided to support immutable pattern for this class.
+ *
+ * @param context the {@link ExecutionContext} for the new <code>JcrPropertyDefinition</code>
+ * @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
+ * <code>context</code>.
+ */
+ JcrPropertyDefinition with( ExecutionContext context) {
+ return new JcrPropertyDefinition(context, this.declaringNodeType, this.name, this.getOnParentVersion(),
+ this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
+ this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
@@ -673,4 +689,51 @@
return false;
}
}
+
+ Key getKey(boolean includeType) {
+ return new Key(this, includeType);
+ }
+
+ /**
+ * Internal class that encapsulates composite unique identifier for property definitions
+ */
+ class Key {
+ String keyString;
+
+ Key( Node node,
+ NamespaceRegistry registry ) throws Exception {
+ String propertyName = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+ try {
+ propertyName = node.getProperty(JcrLexicon.NAME.getString(registry)).getString();
+ } catch (PathNotFoundException pnfe) {
+ // Ignorable, means name is not set.
+ }
+ String requiredType = node.getProperty(JcrLexicon.REQUIRED_TYPE.getString(registry)).getString();
+ boolean allowsMultiple = node.getProperty(JcrLexicon.MULTIPLE.getString(registry)).getBoolean();
+
+ this.keyString = propertyName + "-" + requiredType + "-" + allowsMultiple;
+ }
+
+ Key( PropertyDefinition def, boolean includeType ) {
+ String requiredType = PropertyType.nameFromValue(def.getRequiredType());
+
+ this.keyString = def.getName() + (includeType ? "-" + requiredType : "") + "-" + def.isMultiple();
+ }
+
+ @Override
+ public boolean equals( Object ob ) {
+ if (!(ob instanceof Key)) {
+ return false;
+ }
+
+ return keyString.equals(((Key)ob).keyString);
+ }
+
+ @Override
+ public int hashCode() {
+ return keyString.hashCode();
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -166,6 +166,10 @@
return this.executionContext.getNamespaceRegistry();
}
+ JcrWorkspace workspace() {
+ return this.workspace;
+ }
+
/**
* {@inheritDoc}
*
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -175,6 +175,10 @@
return this.nodeTypeManager;
}
+ final ExecutionContext context() {
+ return this.context;
+ }
+
/**
* {@inheritDoc}
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -24,6 +24,7 @@
package org.jboss.dna.jcr;
import java.io.Serializable;
+import java.util.HashMap;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
@@ -34,6 +35,19 @@
* within the graph as {@link #getString() string values} on a property. These string values can later be
* {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
* use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"Similarly, a node type may have two or more child node definitions with identical name attributes as long as they are
+ * distinguishable by the required primary types attribute (the value returned by
+ * NodeDefinition.getRequiredPrimaryTypes)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
*/
@Immutable
public final class NodeDefinitionId implements Serializable {
@@ -51,7 +65,10 @@
private final Name nodeTypeName;
private final Name childDefinitionName;
private final Name[] requiredPrimaryTypes;
- private final String stringVersion;
+ /**
+ * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+ */
+ private final String stringRepresentation;
/**
* Create an identifier for a node definition.
@@ -75,7 +92,7 @@
sb.append('/');
sb.append(requiredPrimaryType.getString());
}
- this.stringVersion = sb.toString();
+ this.stringRepresentation = sb.toString();
}
/**
@@ -121,7 +138,7 @@
* @return the string form
*/
public String getString() {
- return this.stringVersion;
+ return this.stringRepresentation;
}
/**
@@ -153,7 +170,7 @@
*/
@Override
public int hashCode() {
- return stringVersion.hashCode();
+ return stringRepresentation.hashCode();
}
/**
@@ -166,7 +183,7 @@
if (obj == this) return true;
if (obj instanceof NodeDefinitionId) {
NodeDefinitionId that = (NodeDefinitionId)obj;
- return this.stringVersion.equals(that.stringVersion);
+ return this.stringRepresentation.equals(that.stringRepresentation);
}
return false;
}
@@ -178,7 +195,7 @@
*/
@Override
public String toString() {
- return this.stringVersion;
+ return this.stringRepresentation;
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -24,6 +24,7 @@
package org.jboss.dna.jcr;
import java.io.Serializable;
+import java.util.HashMap;
import javax.jcr.PropertyType;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.property.Name;
@@ -35,6 +36,20 @@
* stored within the graph as {@link #getString() string values} on a property. These string values can later be
* {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
* use namespace prefixes, so they are long-lasting and durable.
+ * <p>
+ * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
+ * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
+ * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
+ * </p>
+ * <p>
+ * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
+ * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
+ * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
+ * PropertyDefinition.isMultiple)."</quote>
+ * </p>
+ * <p>
+ * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
+ * </p>
*/
@Immutable
public final class PropertyDefinitionId implements Serializable {
@@ -53,7 +68,10 @@
private final Name propertyDefinitionName;
private final int propertyType;
private final boolean allowsMultiple;
- private final String stringVersion;
+ /**
+ * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
+ */
+ private final String stringRepresentation;
/**
* Create a new identifier for a propety definition.
@@ -73,8 +91,8 @@
this.propertyDefinitionName = propertyDefinitionName;
this.propertyType = propertyType;
this.allowsMultiple = allowsMultiple;
- this.stringVersion = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
- + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
+ this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
+ + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
}
/**
@@ -129,7 +147,7 @@
* @return the string form
*/
public String getString() {
- return this.stringVersion;
+ return this.stringRepresentation;
}
/**
@@ -167,7 +185,7 @@
*/
@Override
public int hashCode() {
- return this.stringVersion.hashCode();
+ return this.stringRepresentation.hashCode();
}
/**
@@ -180,7 +198,7 @@
if (obj == this) return true;
if (obj instanceof PropertyDefinitionId) {
PropertyDefinitionId that = (PropertyDefinitionId)obj;
- return this.stringVersion.equals(that.stringVersion);
+ return this.stringRepresentation.equals(that.stringRepresentation);
}
return false;
}
@@ -192,7 +210,7 @@
*/
@Override
public String toString() {
- return this.stringVersion;
+ return this.stringRepresentation;
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java 2009-04-14 16:13:03 UTC (rev 820)
@@ -24,7 +24,9 @@
package org.jboss.dna.jcr;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -32,6 +34,7 @@
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
@@ -47,6 +50,7 @@
import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.jcr.JcrNodeTypeSource;
/**
* The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
@@ -70,6 +74,27 @@
private final PropertyFactory propertyFactory;
private final PathFactory pathFactory;
+ /**
+ * List of ways to filter the returned property definitions
+ *
+ * @see RepositoryNodeTypeManager#findPropertyDefinitions(List, Name, PropertyCardinality, List)
+ */
+ private enum PropertyCardinality {
+ SINGLE_VALUED_ONLY,
+ MULTI_VALUED_ONLY,
+ ANY
+ }
+
+ /**
+ * List of ways to filter the returned node definitions
+ *
+ * @see RepositoryNodeTypeManager#findChildNodeDefinitions(List, Name, NodeCardinality, List)
+ */
+ private enum NodeCardinality {
+ NO_SAME_NAME_SIBLINGS,
+ SAME_NAME_SIBLINGS
+ }
+
RepositoryNodeTypeManager( ExecutionContext context,
JcrNodeTypeSource source ) {
this.context = context;
@@ -454,6 +479,52 @@
}
/**
+ * Searches the supplied primary and mixin node types for all valid property definitions that match the given property name
+ * and cardinality.
+ * <p>
+ * If no satisfactory property definition could be found, this method returns an empty list.
+ * </p>
+ *
+ * @param typeNamesToCheck the name of the types to check; may not be null
+ * @param propertyName the name of the property for which the definitions should be retrieved
+ * @param typeToCheck the type of definitions to consider (single-valued only, multi-valued only, or all)
+ * @param pendingTypes a list of types that have been created during type registration but not yet registered in the type map
+ * @return a list of all valid property definitions that match the given property name and cardinality
+ */
+ private List<JcrPropertyDefinition> findPropertyDefinitions( List<Name> typeNamesToCheck,
+ Name propertyName,
+ PropertyCardinality typeToCheck,
+ List<JcrNodeType> pendingTypes ) {
+ assert typeNamesToCheck != null;
+ Collection<JcrPropertyDefinition> propDefs = null;
+ List<JcrPropertyDefinition> matchingDefs = new ArrayList<JcrPropertyDefinition>();
+
+ // Look for a single-value property definition on the mixin types that matches by name and type ...
+ for (Name typeNameToCheck : typeNamesToCheck) {
+ JcrNodeType typeName = findTypeInMapOrList(typeNameToCheck, pendingTypes);
+ if (typeName == null) continue;
+
+ switch (typeToCheck) {
+ case SINGLE_VALUED_ONLY:
+ propDefs = typeName.allSingleValuePropertyDefinitions(propertyName);
+ break;
+ case MULTI_VALUED_ONLY:
+ propDefs = typeName.allMultiValuePropertyDefinitions(propertyName);
+ break;
+ case ANY:
+ propDefs = typeName.allPropertyDefinitions(propertyName);
+ break;
+ default:
+ throw new IllegalStateException("Should be unreachable: " + typeToCheck);
+ }
+
+ matchingDefs.addAll(propDefs);
+ }
+
+ return matchingDefs;
+ }
+
+ /**
* Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied
* name to be removed.
*
@@ -566,6 +637,50 @@
}
/**
+ * Searches the supplied primary and mixin node types for all valid child node definitions that match the given child node
+ * name and cardinality.
+ * <p>
+ * If no satisfactory child node definition could be found, this method returns an empty list.
+ * </p>
+ *
+ * @param typeNamesToCheck the name of the types to check; may not be null
+ * @param childNodeName the name of the child node for which the definitions should be retrieved
+ * @param typesToCheck the type of definitions to consider (allows SNS or does not allow SNS)
+ * @param pendingTypes a list of types that have been created during type registration but not yet registered in the type map
+ * @return a list of all valid chlid node definitions that match the given child node name and cardinality
+ */
+ private List<JcrNodeDefinition> findChildNodeDefinitions( List<Name> typeNamesToCheck,
+ Name childNodeName,
+ NodeCardinality typesToCheck,
+ List<JcrNodeType> pendingTypes ) {
+ assert typeNamesToCheck != null;
+ Collection<JcrNodeDefinition> nodeDefs = null;
+ List<JcrNodeDefinition> matchingDefs = new ArrayList<JcrNodeDefinition>();
+
+ for (Name typeNameToCheck : typeNamesToCheck) {
+ JcrNodeType typeName = findTypeInMapOrList(typeNameToCheck, pendingTypes);
+ if (typeName == null) continue;
+
+ switch (typesToCheck) {
+ case NO_SAME_NAME_SIBLINGS:
+ nodeDefs = typeName.allChildNodeDefinitions(childNodeName);
+ break;
+ case SAME_NAME_SIBLINGS:
+ nodeDefs = typeName.allChildNodeDefinitions(childNodeName, true);
+ break;
+ }
+
+ assert nodeDefs != null;
+ for (JcrNodeDefinition definition : nodeDefs) {
+ if (NodeCardinality.NO_SAME_NAME_SIBLINGS == typesToCheck && definition.allowsSameNameSiblings()) continue;
+ matchingDefs.add(definition);
+ }
+ }
+
+ return matchingDefs;
+ }
+
+ /**
* Determine if the child node definitions of the supplied primary type and mixin types of a parent node allow all of the
* children with the supplied name to be removed.
*
@@ -819,4 +934,531 @@
batch.create(nodeDefPath).with(propsList).and();
}
+
+ /**
+ * Registers the node types from the given {@link JcrNodeTypeSource}.
+ * <p>
+ * The effect of this method is "all or nothing"; if an error occurs, no node types are registered or updated.
+ * </p>
+ * <p>
+ * <b>DNA Implementation Notes</b>
+ * </p>
+ * <p>
+ * DNA currently supports registration of batches of types with some constraints. DNA will allow types to be registered if
+ * they meet the following criteria:
+ * <ol>
+ * <li>Existing types cannot be modified in-place - They must be unregistered and re-registered</li>
+ * <li>Types must have a non-null, non-empty name</li>
+ * <li>If a primary item name is specified for the node type, it must match the name of a property OR a child node, not both</li>
+ * <li>Each type must have a valid set of supertypes - that is, the type's supertypes must meet the following criteria:
+ * <ol>
+ * <li>The type must have at least one supertype (unless the type is {@code nt:base}.</li>
+ * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a property ({@code p1} and {@code p2}) with the
+ * same name and cardinality ({@code p1.isMultiple() == p2.isMultiple()}). Note that this does prohibit each {@code t1} and
+ * {@code t2} from having a common supertype (or super-supertype, etc.) that declares a property).</li>
+ * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a child node ({@code n1} and {@code n2}) with the
+ * same name and SNS status ({@code p1.allowsSameNameSiblings() == p2.allowsSameNameSiblings()}). Note that this does prohibit
+ * each {@code t1} and {@code t2} from having a common supertype (or super-supertype, etc.) that declares a child node).</li>
+ * </ol>
+ * </li>
+ * <li>Each type must have a valid set of properties - that is, the type's properties must meet the following criteria:
+ * <ol>
+ * <li>Residual property definitions cannot be mandatory</li>
+ * <li>If the property is auto-created, it must specify a default value</li>
+ * <li>If the property is single-valued, it can only specify a single default value</li>
+ * <li>If the property overrides an existing property definition from a supertype, the new definition must be mandatory if the
+ * old definition was mandatory</li>
+ * <li>The property cannot override an existing property definition from a supertype if the ancestor definition is protected</li>
+ * <li>If the property overrides an existing property definition from a supertype that specifies value constraints, the new
+ * definition must have the same value constraints as the old definition. <i>This requirement may be relaxed in a future
+ * version of DNA.</i></li>
+ * <li>If the property overrides an existing property definition from a supertype, the new definition must have the same
+ * required type as the old definition or a required type that can ALWAYS be cast to the required type of the ancestor (see
+ * section 6.2.6 of the JCR 1.0.1 specification)</li>
+ * </ol>
+ * Note that an empty set of properties would meet the above criteria.</li>
+ * <li>The type must have a valid set of child nodes - that is, the types's child nodes must meet the following criteria:
+ * <ol>
+ * <li>Residual child node definitions cannot be mandatory</li>
+ * <li>If the child node is auto-created, it must specify a default primary type name</li>
+ * <li>All required primary types must already be fully registered with the type manager or must have been defined earlier in
+ * the batch. <i>This requirement may be relaxed in a future version of DNA.</i></li>
+ * <li>If the child node overrides an existing child node definition from a supertype, the new definition must be mandatory if
+ * the old definition was mandatory</li>
+ * <li>The child node cannot override an existing child node definition from a supertype if the ancestor definition is
+ * protected</li>
+ * <li>If the child node overrides an existing child node definition from a supertype, the required primary types of the new
+ * definition must be more restrictive than the required primary types of the old definition - that is, the new primary types
+ * must defined such that any type that satisfies all of the required primary types for the new definition must also satisfy
+ * all of the required primary types for the old definition. This requirement is analogous to the requirement that overriding
+ * property definitions have a required type that is always convertible to the required type of the overridden definition.</li>
+ * </ol>
+ * Note that an empty set of child nodes would meet the above criteria.</li>
+ * </p>
+ *
+ * @param nodeTypeSource the batch of {@link NodeType node types} to register
+ * @return the newly registered (or updated) {@link NodeType NodeTypes}
+ * @throws RepositoryException if any of the node types in the the {@link JcrNodeTypeSource} are invalid
+ * @throws RepositoryException if another error occurs
+ */
+ List<JcrNodeType> registerNodeTypes( JcrNodeTypeSource nodeTypeSource ) throws RepositoryException {
+
+ assert nodeTypeSource != null;
+ Collection<JcrNodeType> nodeTypeBatch = nodeTypeSource.getNodeTypes();
+
+ List<JcrNodeType> typesPendingRegistration = new ArrayList<JcrNodeType>(nodeTypeBatch.size());
+
+ for (JcrNodeType nodeType : nodeTypeBatch) {
+ if (nodeType.getInternalName() == null || nodeType.getName().length() == 0) {
+ throw new RepositoryException(JcrI18n.invalidNodeTypeName.text());
+ }
+
+ Name name = nodeType.getInternalName();
+
+ if (nodeTypes.containsKey(name)) {
+ throw new RepositoryException(JcrI18n.nodeTypeAlreadyExists.text(nodeType.getName()));
+ }
+
+ List<JcrNodeType> supertypes = supertypesFor(nodeType, typesPendingRegistration);
+
+ validate(nodeType, supertypes, typesPendingRegistration);
+
+ List<JcrPropertyDefinition> propertyDefs = new ArrayList<JcrPropertyDefinition>(
+ nodeType.getDeclaredPropertyDefinitions().length);
+
+ for (JcrPropertyDefinition propertyDef : nodeType.getDeclaredPropertyDefinitions()) {
+ propertyDefs.add(propertyDef.with(this.context));
+ }
+
+ List<JcrNodeDefinition> nodeDefs = new ArrayList<JcrNodeDefinition>(nodeType.getDeclaredChildNodeDefinitions().length);
+
+ for (JcrNodeDefinition nodeDef : nodeType.getDeclaredChildNodeDefinitions()) {
+ JcrNodeType[] requiredPrimaryTypes = new JcrNodeType[nodeDef.getRequiredPrimaryTypeNames().size()];
+
+ int i = 0;
+ for (Name primaryTypeName : nodeDef.getRequiredPrimaryTypeNames()) {
+ requiredPrimaryTypes[i] = findTypeInMapOrList(primaryTypeName, typesPendingRegistration);
+
+ if (requiredPrimaryTypes[i] == null) {
+ throw new RepositoryException(JcrI18n.invalidPrimaryTypeName.text(primaryTypeName, nodeType.getName()));
+ }
+ i++;
+ }
+
+ nodeDefs.add(nodeDef.with(this.context));
+ }
+
+ JcrNodeType newNodeType = new JcrNodeType(this.context, this, name, supertypes,
+ nodeType.getInternalPrimaryItemName(), nodeDefs, propertyDefs,
+ nodeType.isMixin(), nodeType.hasOrderableChildNodes());
+ typesPendingRegistration.add(newNodeType);
+ }
+
+ // Graph.Batch batch = graph.batch();
+ for (JcrNodeType nodeType : typesPendingRegistration) {
+ /*
+ * See comment in constructor. Using a ConcurrentHashMap seems to be to weak of a
+ * solution (even it were also used for childNodeDefinitions and propertyDefinitions).
+ * Probably need to block all read access to these maps during this phase of registration.
+ */
+ nodeTypes.put(nodeType.getInternalName(), nodeType);
+ for (JcrNodeDefinition childDefinition : nodeType.childNodeDefinitions()) {
+ childNodeDefinitions.put(childDefinition.getId(), childDefinition);
+ }
+ for (JcrPropertyDefinition propertyDefinition : nodeType.propertyDefinitions()) {
+ propertyDefinitions.put(propertyDefinition.getId(), propertyDefinition);
+ }
+
+ // projectNodeTypeOnto(nodeType, parentOfTypeNodes, batch);
+ }
+
+ // batch.execute();
+
+ return typesPendingRegistration;
+ }
+
+ /**
+ * Finds the named type in the given list of types pending registration if it exists, else returns the type definition from
+ * the repository
+ *
+ * @param typeName the name of the type to retrieve
+ * @param pendingList a list of types that have passed validation but have not yet been committed to the repository
+ * @return the node type with the given name from {@code pendingList} if it exists in the list or from the {@link #nodeTypes
+ * registered types} if it exists there; may be null
+ */
+ private JcrNodeType findTypeInMapOrList( Name typeName,
+ List<JcrNodeType> pendingList ) {
+ for (JcrNodeType pendingNodeType : pendingList) {
+ if (pendingNodeType.getInternalName().equals(typeName)) {
+ return pendingNodeType;
+ }
+ }
+
+ return nodeTypes.get(typeName);
+ }
+
+ /**
+ * Returns the list of node types for the supertypes defined in the given node type.
+ *
+ * @param nodeType a node type with a non-null array of supertypes
+ * @param pendingTypes the list of types that have been processed in this type batch but not yet committed to the repository's
+ * set of types
+ * @return a list of node types where each element is the node type for the corresponding element of the array of supertype
+ * names
+ * @throws RepositoryException if any of the names in the array of supertype names does not correspond to an
+ * already-registered node type or a node type that is pending registration
+ */
+ private List<JcrNodeType> supertypesFor( JcrNodeType nodeType,
+ List<JcrNodeType> pendingTypes ) throws RepositoryException {
+ assert nodeType != null;
+
+ // If no supertypes are provided, assume nt:base as a supertype
+ if (nodeType.getDeclaredSupertypes() == null || nodeType.getDeclaredSupertypes().length == 0) {
+ return Collections.<JcrNodeType>singletonList(nodeTypes.get(JcrNtLexicon.BASE));
+ }
+
+ JcrNodeType[] supertypesArray = nodeType.getDeclaredSupertypes();
+ List<JcrNodeType> supertypes = new ArrayList<JcrNodeType>(supertypesArray.length);
+
+ for (int i = 0; i < supertypesArray.length; i++) {
+ supertypes.add(findTypeInMapOrList(supertypesArray[i].getInternalName(), pendingTypes));
+
+ if (supertypes.get(i) == null) {
+ throw new RepositoryException(JcrI18n.invalidSupertypeName.text(supertypesArray[i].getInternalName(),
+ nodeType.getName()));
+ }
+ }
+
+ return supertypes;
+ }
+
+ /**
+ * Validates that the supertypes are compatible under DNA restrictions.
+ * <p>
+ * DNA imposes the following rules on the supertypes of a type:
+ * <ol>
+ * <li>The type must have at least one supertype (unless the type is {@code nt:base}.</li>
+ * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a property ({@code p1} and {@code p2}) with the
+ * same name and cardinality ({@code p1.isMultiple() == p2.isMultiple()}). Note that this does prohibit each {@code t1} and
+ * {@code t2} from having a common supertype (or super-supertype, etc.) that declares a property).</li>
+ * <li>No two supertypes {@code t1} and {@code t2} can declare each declare a child node ({@code n1} and {@code n2}) with the
+ * same name and SNS status ({@code p1.allowsSameNameSiblings() == p2.allowsSameNameSiblings()}). Note that this does prohibit
+ * each {@code t1} and {@code t2} from having a common supertype (or super-supertype, etc.) that declares a child node).</li>
+ * </ol>
+ * </p>
+ * <p>
+ * If any of these rules are violated, a {@link RepositoryException} is thrown.
+ * </p>
+ *
+ * @param supertypes the supertypes of this node type
+ * @param nodeName the name of the node for which the supertypes are being validated.
+ * @throws RepositoryException if any of the rules described above are violated
+ */
+ private void validate( List<JcrNodeType> supertypes,
+ String nodeName ) throws RepositoryException {
+ assert supertypes.size() > 0; // This is reasonable now that we default to having a supertype of nt:base
+
+ Map<JcrPropertyDefinition.Key, JcrPropertyDefinition> props = new HashMap<JcrPropertyDefinition.Key, JcrPropertyDefinition>();
+
+ for (JcrNodeType supertype : supertypes) {
+ for (JcrPropertyDefinition property : supertype.propertyDefinitions()) {
+ JcrPropertyDefinition oldProp = props.put(property.getKey(false), property);
+ if (oldProp != null) {
+ String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
+ String propTypeName = property.getDeclaringNodeType().getName();
+ if (!oldPropTypeName.equals(propTypeName)) {
+ throw new RepositoryException(JcrI18n.supertypesConflict.text(oldPropTypeName,
+ propTypeName,
+ "property",
+ property.getName()));
+ }
+ }
+ }
+ }
+
+ Map<JcrNodeDefinition.Key, JcrNodeDefinition> childNodes = new HashMap<JcrNodeDefinition.Key, JcrNodeDefinition>();
+
+ for (JcrNodeType supertype : supertypes) {
+ for (JcrNodeDefinition childNode : supertype.childNodeDefinitions()) {
+ JcrNodeDefinition oldNode = childNodes.put(childNode.getKey(false), childNode);
+ if (oldNode != null) {
+ String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
+ String childNodeTypeName = childNode.getDeclaringNodeType().getName();
+ if (!oldNodeTypeName.equals(childNodeTypeName)) {
+ throw new RepositoryException(JcrI18n.supertypesConflict.text(oldNodeTypeName,
+ childNodeTypeName,
+ "child node",
+ childNode.getName()));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates that the given node type definition is valid under the DNA and JCR type rules within the given context.
+ * <p>
+ * See {@link #registerNodeTypes(JcrNodeTypeSource)} for the list of criteria that determine whether a node type definition is
+ * valid.
+ * </p>
+ *
+ * @param nodeType the node type to attempt to validate
+ * @param supertypes the names of the supertypes of the node type to which this child node belongs
+ * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+ * @throws RepositoryException if the given node type template is not valid
+ */
+ private void validate( JcrNodeType nodeType,
+ List<JcrNodeType> supertypes,
+ List<JcrNodeType> pendingTypes ) throws RepositoryException {
+ validate(supertypes, nodeType.getName());
+
+ List<Name> supertypeNames = new ArrayList<Name>(supertypes.size());
+ for (JcrNodeType supertype : supertypes)
+ supertypeNames.add(supertype.getInternalName());
+
+ boolean found = false;
+ String primaryItemName = nodeType.getPrimaryItemName();
+ for (JcrNodeDefinition node : nodeType.getDeclaredChildNodeDefinitions()) {
+ validate(node, supertypeNames, pendingTypes);
+
+ if (primaryItemName != null && primaryItemName.equals(node.getName())) {
+ found = true;
+ }
+ }
+
+ for (JcrPropertyDefinition prop : nodeType.getDeclaredPropertyDefinitions()) {
+ validate(prop, supertypeNames, pendingTypes);
+ if (primaryItemName != null && primaryItemName.equals(prop.getName())) {
+ if (found) {
+ throw new RepositoryException(JcrI18n.ambiguousPrimaryItemName.text(primaryItemName));
+ }
+ found = true;
+ }
+ }
+
+ if (primaryItemName != null && !found) {
+ throw new RepositoryException(JcrI18n.invalidPrimaryItemName.text(primaryItemName));
+ }
+ }
+
+ /**
+ * Validates that the given child node definition is valid under the DNA and JCR type rules within the given context.
+ * <p>
+ * DNA considers a child node definition valid if it meets these criteria:
+ * <ol>
+ * <li>Residual child node definitions cannot be mandatory</li>
+ * <li>If the child node is auto-created, it must specify a default primary type name</li>
+ * <li>All required primary types must already be fully registered with the type manager or must have been defined earlier in
+ * the batch. This requirement may be relaxed in a future version of DNA.</li>
+ * <li>If the child node overrides an existing child node definition from a supertype, the new definition must be mandatory if
+ * the old definition was mandatory</li>
+ * <li>The child node cannot override an existing child node definition from a supertype if the ancestor definition is
+ * protected</li>
+ * <li>If the child node overrides an existing child node definition from a supertype, the required primary types of the new
+ * definition must be more restrictive than the required primary types of the old definition - that is, the new primary types
+ * must defined such that any type that satisfies all of the required primary types for the new definition must also satisfy
+ * all of the required primary types for the old definition. This requirement is analogous to the requirement that overriding
+ * property definitions have a required type that is always convertible to the required type of the overridden definition.</li>
+ * </ol>
+ * </p>
+ *
+ * @param node the child node definition to be validated
+ * @param supertypes the names of the supertypes of the node type to which this child node belongs
+ * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+ * @throws RepositoryException if the child node definition is not valid
+ */
+ private void validate( JcrNodeDefinition node,
+ List<Name> supertypes,
+ List<JcrNodeType> pendingTypes ) throws RepositoryException {
+ if (node.isAutoCreated() && node.getDefaultPrimaryType() == null) {
+ throw new RepositoryException(JcrI18n.autocreatedNodesNeedDefaults.text());
+ }
+ if (node.isMandatory() && node.getName() == null) {
+ throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("child nodes"));
+ }
+
+ Name nodeName = context.getValueFactories().getNameFactory().create(node.getName());
+ nodeName = nodeName == null ? JcrNodeType.RESIDUAL_NAME : nodeName;
+
+ List<JcrNodeDefinition> ancestors = findChildNodeDefinitions(supertypes,
+ nodeName,
+ node.allowsSameNameSiblings() ? NodeCardinality.SAME_NAME_SIBLINGS : NodeCardinality.NO_SAME_NAME_SIBLINGS,
+ pendingTypes);
+
+ for (JcrNodeDefinition ancestor : ancestors) {
+ if (ancestor.isProtected()) {
+ throw new RepositoryException(
+ JcrI18n.cannotOverrideProtectedDefinition.text(ancestor.getDeclaringNodeType().getName(),
+ "child node"));
+ }
+
+ if (ancestor.isMandatory() && !node.isMandatory()) {
+ throw new RepositoryException(
+ JcrI18n.cannotMakeMandatoryDefinitionOptional.text(ancestor.getDeclaringNodeType().getName(),
+ "child node"));
+
+ }
+
+ for (int i = 0; i < ancestor.getRequiredPrimaryTypes().length; i++) {
+ NodeType apt = ancestor.getRequiredPrimaryTypes()[i];
+ boolean found = false;
+ for (Name name : node.getRequiredPrimaryTypeNames()) {
+ JcrNodeType npt = findTypeInMapOrList(name, pendingTypes);
+
+ if (npt.isNodeType(apt.getName())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new RepositoryException(
+ "Cannot redefine child node '"
+ + nodeName
+ + "' with required type '"
+ + apt.getName()
+ + "' with new child node that does not required that type or a subtype of that type.");
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates that the given property definition is valid under the DNA and JCR type rules within the given context.
+ * <p>
+ * DNA considers a property definition valid if it meets these criteria:
+ * <ol>
+ * <li>Residual properties cannot be mandatory</li>
+ * <li>If the property is auto-created, it must specify a default value</li>
+ * <li>If the property is single-valued, it can only specify a single default value</li>
+ * <li>If the property overrides an existing property definition from a supertype, the new definition must be mandatory if the
+ * old definition was mandatory</li>
+ * <li>The property cannot override an existing property definition from a supertype if the ancestor definition is protected</li>
+ * <li>If the property overrides an existing property definition from a supertype, the new definition must have the same
+ * required type as the old definition or a required type that can ALWAYS be cast to the required type of the ancestor (see
+ * section 6.2.6 of the JCR 1.0.1 specification)</li>
+ * </ol>
+ * Note that an empty set of properties would meet the criteria above.
+ * </p>
+ *
+ * @param prop the property definition to be validated
+ * @param supertypes the names of the supertypes of the node type to which this property belongs
+ * @param pendingTypes the list of types previously registered in this batch but not yet committed to the repository
+ * @throws RepositoryException if the property definition is not valid
+ */
+ private void validate( JcrPropertyDefinition prop,
+ List<Name> supertypes,
+ List<JcrNodeType> pendingTypes ) throws RepositoryException {
+ assert prop != null;
+ assert supertypes != null;
+ assert pendingTypes != null;
+
+ if (prop.isMandatory() && !prop.isProtected() && prop.getName() == null) {
+ throw new RepositoryException(JcrI18n.residualDefinitionsCannotBeMandatory.text("properties"));
+ }
+
+ Value[] defaultValues = prop.getDefaultValues();
+ if (prop.isAutoCreated() && !prop.isProtected() && (defaultValues == null || defaultValues.length == 0)) {
+ throw new RepositoryException(JcrI18n.autocreatedPropertyNeedsDefault.text(prop.getName(),
+ prop.getDeclaringNodeType().getName()));
+ }
+
+ if (!prop.isMultiple() && (defaultValues != null && defaultValues.length > 1)) {
+ throw new RepositoryException(
+ JcrI18n.singleValuedPropertyNeedsSingleValuedDefault.text(prop.getName(),
+ prop.getDeclaringNodeType().getName()));
+ }
+
+ Name propName = context.getValueFactories().getNameFactory().create(prop.getName());
+ propName = propName == null ? JcrNodeType.RESIDUAL_NAME : propName;
+
+ List<JcrPropertyDefinition> ancestors = findPropertyDefinitions(supertypes,
+ propName,
+ prop.isMultiple() ? PropertyCardinality.MULTI_VALUED_ONLY : PropertyCardinality.SINGLE_VALUED_ONLY,
+ pendingTypes);
+
+ for (JcrPropertyDefinition ancestor : ancestors) {
+ if (ancestor.isProtected()) {
+ throw new RepositoryException(
+ JcrI18n.cannotOverrideProtectedDefinition.text(ancestor.getDeclaringNodeType().getName(),
+ "property"));
+ }
+
+ if (ancestor.isMandatory() && !prop.isMandatory()) {
+ throw new RepositoryException(
+ JcrI18n.cannotMakeMandatoryDefinitionOptional.text(ancestor.getDeclaringNodeType().getName(),
+ "property"));
+
+ }
+
+ // TODO: It would be nice if we could allow modification of constraints if the new constraints were more strict than
+ // the old
+ if (ancestor.getValueConstraints() != null
+ && !Arrays.equals(ancestor.getValueConstraints(), prop.getValueConstraints())) {
+ throw new RepositoryException(JcrI18n.constraintsChangedInSubtype.text(propName,
+ ancestor.getDeclaringNodeType().getName()));
+ }
+
+ if (!isAlwaysSafeConversion(prop.getRequiredType(), ancestor.getRequiredType())) {
+ throw new RepositoryException(
+ JcrI18n.cannotRedefineProperty.text(propName,
+ PropertyType.nameFromValue(prop.getRequiredType()),
+ ancestor.getDeclaringNodeType().getName(),
+ PropertyType.nameFromValue(ancestor.getRequiredType())));
+
+ }
+ }
+ }
+
+ /**
+ * Returns whether it is always possible to convert a value with JCR property type {@code fromType} to {@code toType}.
+ * <p>
+ * This method is based on the conversions which can never throw an exception in the chart in section 6.2.6 of the JCR 1.0.1
+ * specification.
+ * </p>
+ *
+ * @param fromType the type to be converted from
+ * @param toType the type to convert to
+ * @return true if any value with type {@code fromType} can be converted to a type of {@code toType} without a
+ * {@link ValueFormatException} being thrown.
+ * @see PropertyType
+ */
+ private boolean isAlwaysSafeConversion( int fromType,
+ int toType ) {
+
+ if (fromType == toType) return true;
+
+ switch (toType) {
+ case PropertyType.BOOLEAN:
+ return fromType == PropertyType.BINARY || fromType == PropertyType.STRING;
+
+ case PropertyType.DATE:
+ return fromType == PropertyType.DOUBLE || fromType == PropertyType.LONG;
+
+ case PropertyType.DOUBLE:
+ // Conversion from DATE could result in out-of-range value
+ return fromType == PropertyType.LONG;
+ case PropertyType.LONG:
+ // Conversion from DATE could result in out-of-range value
+ return fromType == PropertyType.DOUBLE;
+
+ case PropertyType.PATH:
+ return fromType == PropertyType.NAME;
+
+ // Values of any type MAY fail when converting to these types
+ case PropertyType.NAME:
+ case PropertyType.REFERENCE:
+ return false;
+
+ // Any type can be converted to these types
+ case PropertyType.BINARY:
+ case PropertyType.STRING:
+ case PropertyType.UNDEFINED:
+ return true;
+
+ default:
+ throw new IllegalStateException("Unexpected state: " + toType);
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-04-13 19:29:46 UTC (rev 819)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-04-14 16:13:03 UTC (rev 820)
@@ -66,6 +66,7 @@
errorWhileFindingNodeWithUuid = Error while finding the node with UUID "{0}" in workspace "{1}": {2}
errorWhileFindingNodeWithPath = Error while finding the node "{0}" in workspace "{1}"
nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
+noSnsDefinitionForNode = A node definition that allows same name siblings could not be found for the node "{0}" in workspace "{1}"
missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
unableToCreateNodeWithPrimaryTypeThatDoesNotExist = Unable to create child "{1}" in workspace "{2}" because the node type "{0}" does not exist
unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition = Unable to create child "{2}" in workspace "{3}" because the node definition "{0}" on the "{1}" node type has no default primary type
@@ -93,4 +94,20 @@
tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this node
notStoredQuery=This query has not been stored or loaded
-invalidQueryLanguage="{0}" is not a valid query langauge. Supported languages are: {1}
\ No newline at end of file
+invalidQueryLanguage="{0}" is not a valid query langauge. Supported languages are\: {1}
+
+invalidNodeTypeName=Node types cannot have a null or empty name
+nodeTypeAlreadyExists=Node type '{0}' already exists
+invalidPrimaryTypeName=Required primary type '{0}' in type '{1}' does not exist
+invalidSupertypeName=Supertype '{0}' from type '{1}' does not exist
+supertypesConflict=Types '{0}' and '{1}' cannot both be supertypes of the same type, as both separately declare {2} '{3}'
+ambiguousPrimaryItemName=Primary item name '{0}' matches the name of a child node and a property
+invalidPrimaryItemName=Primary item name '{0}' does not match the name of any child nodes or properties
+autocreatedNodesNeedDefaults=Autocreated child nodes must specify a default primary type
+residualDefinitionsCannotBeMandatory=Residual {0} cannot be mandatory
+cannotOverrideProtectedDefinition=Cannot override protected {1} definition from '{0}'
+cannotMakeMandatoryDefinitionOptional=Cannot override mandatory {1} definition from '{0}' with a non-mandatory definition.
+constraintsChangedInSubtype=Must use exact same constraints when overriding the property definition for {0} from type '{1}'
+cannotRedefineProperty=Cannot redefine property '{0}' with new type '{1}' when existing property with same name in type '{2}' has incompatible type '{3}'
+autocreatedPropertyNeedsDefault=Auto-created property '{0}' in type '{1}' must specify a default value
+singleValuedPropertyNeedsSingleValuedDefault=Single-valued property '{0}' in type '{1}' cannot have multiple default values
More information about the dna-commits
mailing list