[dna-commits] DNA SVN: r759 - trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Thu Mar 5 13:14:57 EST 2009


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

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

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

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




More information about the dna-commits mailing list