You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2013/04/12 12:47:45 UTC

svn commit: r1467235 - in /jackrabbit/oak/trunk/oak-core/src/main: java/org/apache/jackrabbit/oak/plugins/nodetype/ resources/org/apache/jackrabbit/oak/plugins/nodetype/write/

Author: jukka
Date: Fri Apr 12 10:47:44 2013
New Revision: 1467235

URL: http://svn.apache.org/r1467235
Log:
OAK-411: Validator for node type management

Improve the set of pre-compiled type information

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/EffectiveType.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/NodeTypeConstants.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ReadOnlyNodeTypeManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/RegistrationEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/EffectiveType.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/EffectiveType.java?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/EffectiveType.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/EffectiveType.java Fri Apr 12 10:47:44 2013
@@ -17,11 +17,13 @@
 package org.apache.jackrabbit.oak.plugins.nodetype;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.jackrabbit.JcrConstants.JCR_MANDATORY;
+import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_NODETYPENAME;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
-import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
+import static org.apache.jackrabbit.oak.api.Type.NAME;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
 
 import java.util.List;
 import java.util.Set;
@@ -31,10 +33,10 @@ import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 class EffectiveType {
 
@@ -49,29 +51,18 @@ class EffectiveType {
         ImmutableSet.Builder<String> builder = ImmutableSet.builder();
 
         for (NodeState type : types) {
-            NodeState properties =
-                    type.getChildNode("oak:namedPropertyDefinitions");
-            for (ChildNodeEntry entry : properties.getChildNodeEntries()) {
-                String name = entry.getName();
-                if ("oak:primaryType".equals(name)) {
-                    name = JCR_PRIMARYTYPE;
-                } else if ("oak:mixinTypes".equals(name)) {
-                    name = JCR_MIXINTYPES;
-                } else if ("oak:uuid".equals(name)) {
-                    name = JCR_UUID;
-                }
-                if (node.getProperty(name) == null
-                        && isMandatory(name, entry.getNodeState())) {
+            PropertyState properties =
+                    type.getProperty("oak:mandatoryProperties");
+            for (String name : properties.getValue(NAMES)) {
+                if (node.getProperty(name) == null) {
                     builder.add(name);
                 }
             }
 
-            NodeState childNodes =
-                    type.getChildNode("oak:namedChildNodeDefinitions");
-            for (ChildNodeEntry entry : childNodes.getChildNodeEntries()) {
-                String name = entry.getName();
-                if (!node.hasChildNode(name)
-                        && isMandatory(name, entry.getNodeState())) {
+            PropertyState childNodes =
+                    type.getProperty("oak:mandatoryChildNodes");
+            for (String name : childNodes.getValue(NAMES)) {
+                if (!node.hasChildNode(name)) {
                     builder.add(name);
                 }
             }
@@ -200,24 +191,16 @@ class EffectiveType {
         return null;
     }
 
-    //-----------------------------------------------------------< private >--
-    
-    private boolean isMandatory(String name, NodeState definitions) {
-        for (ChildNodeEntry entry : definitions.getChildNodeEntries()) {
-            NodeState definition = entry.getNodeState();
-            if (getBoolean(definition, JCR_MANDATORY)) {
-                return true;
-            }
+    Set<String> getTypeNames() {
+        Set<String> names = newHashSet();
+        for (NodeState type : types) {
+            names.add(type.getProperty(JCR_NODETYPENAME).getValue(NAME));
+            Iterables.addAll(names, type.getProperty("oak:supertypes").getValue(NAMES));
         }
-        return false;
+        return names;
     }
 
-    private boolean getBoolean(NodeState node, String name) {
-        PropertyState property = node.getProperty(name);
-        return property != null
-                && property.getType() == BOOLEAN
-                && property.getValue(BOOLEAN);
-    }
+    //-----------------------------------------------------------< private >--
 
     private String getTypeKey(Type<?> type) {
         if (type == Type.BINARIES) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/NodeTypeConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/NodeTypeConstants.java?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/NodeTypeConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/NodeTypeConstants.java Fri Apr 12 10:47:44 2013
@@ -58,4 +58,22 @@ public interface NodeTypeConstants exten
     String DELETE_DELETED = "deleteDeleted";
 
     String RESIDUAL_NAME = "*";
-}
\ No newline at end of file
+
+    // Precompiled Oak type information fields
+    String OAK_SUPERTYPES = "oak:supertypes";
+    String OAK_SUBTYPES = "oak:subtypes";
+    String OAK_NAMED_PROPERTIES = "oak:namedProperties";
+    String OAK_MANDATORY_PROPERTIES = "oak:mandatoryProperties";
+    String OAK_MANDATORY_CHILD_NODES = "oak:mandatoryChildNodes";
+    String OAK_RESIDUAL_CHILD_NODE_DEFINITIONS =
+            "oak:residualChildNodeDefinitions";
+    String OAK_NAMED_CHILD_NODE_DEFINITIONS =
+            "oak:namedChildNodeDefinitions";
+    String OAK_RESIDUAL_PROPERTY_DEFINITIONS =
+            "oak:residualPropertyDefinitions";
+    String OAK_NAMED_PROPERTY_DEFINITIONS =
+            "oak:namedPropertyDefinitions";
+    String OAK_PROPERTY_DEFINITIONS = "oak:propertyDefinitions";
+    String OAK_CHILD_NODE_DEFINITIONS = "oak:childNodeDefinitions";
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ReadOnlyNodeTypeManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ReadOnlyNodeTypeManager.java?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ReadOnlyNodeTypeManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ReadOnlyNodeTypeManager.java Fri Apr 12 10:47:44 2013
@@ -17,14 +17,14 @@
 package org.apache.jackrabbit.oak.plugins.nodetype;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.contains;
 import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_SUPERTYPES;
 
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
@@ -44,7 +44,6 @@ import javax.jcr.nodetype.PropertyDefini
 import javax.jcr.nodetype.PropertyDefinitionTemplate;
 
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.commons.iterator.NodeTypeIteratorAdapter;
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -282,49 +281,41 @@ public abstract class ReadOnlyNodeTypeMa
             return false;
         }
 
-        Set<String> typeNames = Sets.newHashSet();
+        Tree types = getTypes();
 
         PropertyState primary = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE);
         if (primary != null && primary.getType() == Type.NAME) {
             String name = primary.getValue(Type.NAME);
-            if (oakNtName.equals(name)) {
+            if (isa(types, name, oakNtName)) {
                 return true;
-            } else {
-                typeNames.add(name);
             }
         }
 
         PropertyState mixins = tree.getProperty(JcrConstants.JCR_MIXINTYPES);
         if (mixins != null && mixins.getType() == Type.NAMES) {
             for (String name : mixins.getValue(Type.NAMES)) {
-                if (oakNtName.equals(name)) {
+                if (isa(types, name, oakNtName)) {
                     return true;
-                } else {
-                    typeNames.add(name);
                 }
             }
         }
 
-        Tree types = getTypes();
-        LinkedList<String> queue = Lists.newLinkedList(typeNames);
-        while (!queue.isEmpty()) {
-            Tree type = types.getChild(queue.removeFirst());
-            if (type != null) {
-                PropertyState supertypes =
-                        type.getProperty(JcrConstants.JCR_SUPERTYPES);
-                if (supertypes != null && supertypes.getType() == Type.NAMES) {
-                    for (String name : supertypes.getValue(Type.NAMES)) {
-                        if (oakNtName.equals(name)) {
-                            return true;
-                        } else if (typeNames.add(name)) {
-                            queue.addLast(name);
-                        }
-                    }
-                }
-            }
+        return false;
+    }
+
+    private static boolean isa(Tree types, String typeName, String superName) {
+        if (typeName.equals(superName)) {
+            return true;
         }
 
-        return false;
+        Tree type = types.getChild(typeName);
+        if (type == null) {
+            return false;
+        }
+
+        PropertyState supertypes = type.getProperty(OAK_SUPERTYPES);
+        return supertypes != null
+                && contains(supertypes.getValue(Type.NAMES), superName);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/RegistrationEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/RegistrationEditor.java?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/RegistrationEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/RegistrationEditor.java Fri Apr 12 10:47:44 2013
@@ -17,7 +17,14 @@
 package org.apache.jackrabbit.oak.plugins.nodetype;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static java.util.Collections.emptyList;
 import static org.apache.jackrabbit.JcrConstants.JCR_CHILDNODEDEFINITION;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISMIXIN;
+import static org.apache.jackrabbit.JcrConstants.JCR_MANDATORY;
 import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_MULTIPLE;
 import static org.apache.jackrabbit.JcrConstants.JCR_NAME;
@@ -29,30 +36,43 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.JcrConstants.JCR_SUPERTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
 import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
+import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
 import static org.apache.jackrabbit.oak.api.Type.NAME;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
 import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
-import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
-
-import java.util.Queue;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_CHILD_NODE_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_MANDATORY_CHILD_NODES;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_MANDATORY_PROPERTIES;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_NAMED_CHILD_NODE_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_NAMED_PROPERTIES;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_NAMED_PROPERTY_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_PROPERTY_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_RESIDUAL_CHILD_NODE_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_RESIDUAL_PROPERTY_DEFINITIONS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_SUBTYPES;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.OAK_SUPERTYPES;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
-import com.google.common.collect.Queues;
-import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
 import org.apache.jackrabbit.oak.spi.commit.Validator;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.collect.Iterables;
+
 /**
  * Editor that validates the consistency of the in-content node type registry
  * under {@code /jcr:system/jcr:nodeTypes} and maintains the access-optimized
- * version under {@code /jcr:system/oak:nodeTypes}.
+ * versions of node type information as defined in {@code oak:nodeType}.
  *
  * <ul>
  *     <li>validate new definitions</li>
@@ -66,71 +86,181 @@ class RegistrationEditor extends Default
 
     private final NodeBuilder builder;
 
-    private final Set<String> changedTypes = Sets.newHashSet();
+    private final Set<String> changedTypes = newHashSet();
+
+    private final Set<String> removedTypes = newHashSet();
 
-    private final Set<String> removedTypes = Sets.newHashSet();
+    private boolean modified = false;
 
     RegistrationEditor(NodeBuilder builder) {
         this.builder = checkNotNull(builder);
     }
 
-    private void validateAndCompile(String name, NodeState after)
+    /**
+     * Validates the inheritance hierarchy of the identified node type and
+     * merges supertype information to the pre-compiled type information
+     * fields. This makes full type information directly accessible without
+     * having to traverse up the type hierarchy.
+     *
+     * @param types
+     * @param name
+     * @return
+     * @throws CommitFailedException
+     */
+    private void mergeSupertypes(NodeBuilder types, NodeBuilder type)
             throws CommitFailedException {
-        NodeBuilder types = builder.child(JCR_SYSTEM).child(JCR_NODE_TYPES);
+        if (type.getProperty(OAK_SUPERTYPES) == null) {
+            List<String> empty = Collections.emptyList();
+            type.setProperty(OAK_SUPERTYPES, empty, NAMES);
+
+            // - jcr:supertypes (NAME) protected multiple
+            PropertyState supertypes = type.getProperty(JCR_SUPERTYPES);
+            if (supertypes != null) {
+                for (String supername : supertypes.getValue(NAMES)) {
+                    if (types.hasChildNode(supername)) {
+                        NodeBuilder supertype = types.child(supername);
+                        mergeSupertypes(types, supertype);
+                        mergeSupertype(type, supertype.getNodeState());
+                    } else {
+                        throw new CommitFailedException(
+                                CONSTRAINT, 35,
+                                "Missing supertype " + supername);
+                    }
+                }
+            }
+
+            if (!getBoolean(type, JCR_ISMIXIN)
+                    && !contains(getNames(type, OAK_SUPERTYPES), NT_BASE)
+                    && !NT_BASE.equals(type.getProperty(JCR_NODETYPENAME).getValue(NAME))) {
+                if (types.hasChildNode(NT_BASE)) {
+                    NodeBuilder supertype = types.child(NT_BASE);
+                    mergeSupertypes(types, supertype);
+                    mergeSupertype(type, supertype.getNodeState());
+                } else {
+                    throw new CommitFailedException(
+                            CONSTRAINT, 35,
+                            "Missing supertype " + NT_BASE);
+                }
+            }
+        }
+    }
+
+    private boolean getBoolean(NodeBuilder builder, String name) {
+        PropertyState property = builder.getProperty(name);
+        return property != null && property.getValue(BOOLEAN);
+    }
+
+    private Iterable<String> getNames(NodeBuilder builder, String name) {
+        PropertyState property = builder.getProperty(name);
+        if (property != null) {
+            return property.getValue(NAMES);
+        } else {
+            return Collections.<String>emptyList();
+        }
+    }
+
+    private void mergeSupertype(NodeBuilder type, NodeState supertype) {
+        String supername =
+                supertype.getProperty(JCR_NODETYPENAME).getValue(NAME);
+        addNameToList(type, OAK_SUPERTYPES, supername);
+        mergeNameList(type, supertype, OAK_SUPERTYPES);
+        mergeNameList(type, supertype, OAK_NAMED_PROPERTIES);
+        mergeNameList(type, supertype, OAK_MANDATORY_PROPERTIES);
+        mergeNameList(type, supertype, OAK_MANDATORY_CHILD_NODES);
+        mergeSubtree(type, supertype, OAK_NAMED_PROPERTY_DEFINITIONS, 2);
+        mergeSubtree(type, supertype, OAK_RESIDUAL_PROPERTY_DEFINITIONS, 1);
+        mergeSubtree(type, supertype, OAK_NAMED_CHILD_NODE_DEFINITIONS, 2);
+        mergeSubtree(type, supertype, OAK_RESIDUAL_CHILD_NODE_DEFINITIONS, 1);
+    }
+
+    private void mergeNameList(
+            NodeBuilder builder, NodeState state, String listName) {
+        LinkedHashSet<String> nameList =
+                newLinkedHashSet(getNames(builder, listName));
+        Iterables.addAll(
+                nameList, state.getProperty(listName).getValue(NAMES));
+        builder.setProperty(listName, nameList, NAMES);
+    }
+
+    private void mergeSubtree(NodeBuilder builder, NodeState state, String name, int depth) {
+        NodeState subtree = state.getChildNode(name);
+        if (subtree.exists()) {
+            if (!builder.hasChildNode(name)) {
+                builder.setNode(name, subtree);
+            } else if (depth > 0) {
+                NodeBuilder subbuilder = builder.child(name);
+                for (String subname : subtree.getChildNodeNames()) {
+                    mergeSubtree(subbuilder, subtree, subname, depth - 1);
+                }
+            }
+        }
+    }
 
-        String path = NODE_TYPES_PATH + "/" + name;
+    /**
+     * Validates and pre-compiles the named node type.
+     *
+     * @param types builder for the /jcr:system/jcr:nodeTypes node
+     * @param name name of the node type to validate and compile
+     * @throws CommitFailedException if type validation fails
+     */
+    private void validateAndCompileType(NodeBuilder types, String name)
+            throws CommitFailedException {
         NodeBuilder type = types.child(name);
 
         // - jcr:nodeTypeName (NAME) protected mandatory
-        PropertyState nodeTypeName = after.getProperty(JCR_NODETYPENAME);
+        PropertyState nodeTypeName = type.getProperty(JCR_NODETYPENAME);
         if (nodeTypeName == null
                 || !name.equals(nodeTypeName.getValue(NAME))) {
             throw new CommitFailedException(
-                    "Constraint", 34,
-                    "Unexpected " + JCR_NODETYPENAME + " in " + path);
-        }
-
-        // - jcr:supertypes (NAME) protected multiple
-        PropertyState supertypes = after.getProperty(JCR_SUPERTYPES);
-        if (supertypes != null) {
-            for (String value : supertypes.getValue(NAMES)) {
-                if (!types.hasChildNode(value)) {
-                    throw new CommitFailedException(
-                            "Constraint", 35,
-                            "Missing supertype " + value + " in " + path);
-                }
-            }
+                    CONSTRAINT, 34,
+                    "Unexpected " + JCR_NODETYPENAME + " in type " + name);
         }
 
+        // Prepare the type node pre-compilation of the oak:nodeType info
+        Iterable<String> empty = emptyList();
         type.setProperty(JCR_PRIMARYTYPE, "oak:nodeType", NAME);
-        type.removeNode("oak:namedPropertyDefinitions");
-        type.removeNode("oak:residualPropertyDefinitions");
-        type.removeNode("oak:namedChildNodeDefinitions");
-        type.removeNode("oak:residualChildNodeDefinitions");
+        type.removeProperty(OAK_SUPERTYPES);
+        type.setProperty(OAK_SUBTYPES, empty, NAMES);
+        type.setProperty(OAK_NAMED_PROPERTIES, empty, NAMES);
+        type.setProperty(OAK_MANDATORY_PROPERTIES, empty, NAMES);
+        type.setProperty(OAK_MANDATORY_CHILD_NODES, empty, NAMES);
+        type.removeNode(OAK_NAMED_PROPERTY_DEFINITIONS);
+        type.removeNode(OAK_RESIDUAL_PROPERTY_DEFINITIONS);
+        type.removeNode(OAK_NAMED_CHILD_NODE_DEFINITIONS);
+        type.removeNode(OAK_RESIDUAL_CHILD_NODE_DEFINITIONS);
 
         // + jcr:propertyDefinition (nt:propertyDefinition)
         //   = nt:propertyDefinition protected sns
         // + jcr:childNodeDefinition (nt:childNodeDefinition)
         //   = nt:childNodeDefinition protected sns
-        for (ChildNodeEntry entry : after.getChildNodeEntries()) {
-            String childName = entry.getName();
-            if (childName.startsWith(JCR_PROPERTYDEFINITION)) {
-                processPropertyDefinition(type, entry.getNodeState());
-            } else if (childName.startsWith(JCR_CHILDNODEDEFINITION)) {
-                processChildNodeDefinition(
-                        types, type, entry.getNodeState());
+        for (String childNodeName : type.getChildNodeNames()) {
+            NodeState definition = type.child(childNodeName).getNodeState();
+            if (childNodeName.startsWith(JCR_PROPERTYDEFINITION)) {
+                validateAndCompilePropertyDefinition(type, definition);
+            } else if (childNodeName.startsWith(JCR_CHILDNODEDEFINITION)) {
+                validateAndCompileChildNodeDefinition(types, type, definition);
             }
         }
     }
 
-    private void processPropertyDefinition(
+    private void addNameToList(NodeBuilder type, String name, String value) {
+        List<String> values;
+        values = newArrayList(getNames(type, name));
+        if (!values.contains(value)) {
+            values.add(value);
+        }
+        type.setProperty(name, values, NAMES);
+    }
+
+    private void validateAndCompilePropertyDefinition(
             NodeBuilder type, NodeState definition)
             throws CommitFailedException {
         // - jcr:name (NAME) protected 
         PropertyState name = definition.getProperty(JCR_NAME);
         NodeBuilder definitions;
         if (name != null) {
-            String escapedName = name.getValue(NAME);
+            String propertyName = name.getValue(NAME);
+            String escapedName = propertyName;
             if (JCR_PRIMARYTYPE.equals(escapedName)) {
                 escapedName = "oak:primaryType";
             } else if (JCR_MIXINTYPES.equals(escapedName)) {
@@ -138,15 +268,21 @@ class RegistrationEditor extends Default
             } else if (JCR_UUID.equals(escapedName)) {
                 escapedName = "oak:uuid";
             }
-            definitions = type.child("oak:namedPropertyDefinitions");
+            definitions = type.child(OAK_NAMED_PROPERTY_DEFINITIONS);
             definitions.setProperty(
-                    JCR_PRIMARYTYPE, "oak:namedPropertyDefinitions", NAME);
+                    JCR_PRIMARYTYPE, OAK_NAMED_PROPERTY_DEFINITIONS, NAME);
             definitions = definitions.child(escapedName);
+
+            // - jcr:mandatory (BOOLEAN) protected mandatory
+            PropertyState mandatory = definition.getProperty(JCR_MANDATORY);
+            if (mandatory != null && mandatory.getValue(BOOLEAN)) {
+                addNameToList(type, OAK_MANDATORY_PROPERTIES, propertyName);
+            }
         } else {
-            definitions = type.child("oak:residualPropertyDefinitions");
+            definitions = type.child(OAK_RESIDUAL_PROPERTY_DEFINITIONS);
         }
         definitions.setProperty(
-                JCR_PRIMARYTYPE, "oak:propertyDefinitions", NAME);
+                JCR_PRIMARYTYPE, OAK_PROPERTY_DEFINITIONS, NAME);
 
         // - jcr:requiredType (STRING) protected mandatory
         // < 'STRING', 'URI', 'BINARY', 'LONG', 'DOUBLE',
@@ -160,7 +296,7 @@ class RegistrationEditor extends Default
 
         // - jcr:multiple (BOOLEAN) protected mandatory
         PropertyState multiple = definition.getProperty(JCR_MULTIPLE);
-        if (multiple != null && multiple.getValue(Type.BOOLEAN)) {
+        if (multiple != null && multiple.getValue(BOOLEAN)) {
             if ("BINARY".equals(key)) {
                 key = "BINARIES";
             } else {
@@ -171,22 +307,29 @@ class RegistrationEditor extends Default
         definitions.setNode(key, definition);
     }
 
-    private void processChildNodeDefinition(
+    private void validateAndCompileChildNodeDefinition(
             NodeBuilder types, NodeBuilder type, NodeState definition)
             throws CommitFailedException {
         // - jcr:name (NAME) protected 
         PropertyState name = definition.getProperty(JCR_NAME);
         NodeBuilder definitions;
         if (name != null) {
-            definitions = type.child("oak:namedChildNodeDefinitions");
+            String childNodeName = name.getValue(NAME);
+            definitions = type.child(OAK_NAMED_CHILD_NODE_DEFINITIONS);
             definitions.setProperty(
-                    JCR_PRIMARYTYPE, "oak:namedChildNodeDefinitions", NAME);
-            definitions = definitions.child(name.getValue(NAME));
+                    JCR_PRIMARYTYPE, OAK_NAMED_CHILD_NODE_DEFINITIONS, NAME);
+            definitions = definitions.child(childNodeName);
+
+            // - jcr:mandatory (BOOLEAN) protected mandatory
+            PropertyState mandatory = definition.getProperty(JCR_MANDATORY);
+            if (mandatory != null && mandatory.getValue(BOOLEAN)) {
+                addNameToList(type, OAK_MANDATORY_CHILD_NODES, childNodeName);
+            }
         } else {
-            definitions = type.child("oak:residualChildNodeDefinitions");
+            definitions = type.child(OAK_RESIDUAL_CHILD_NODE_DEFINITIONS);
         }
         definitions.setProperty(
-                JCR_PRIMARYTYPE, "oak:childNodeDefinitions", NAME);
+                JCR_PRIMARYTYPE, OAK_CHILD_NODE_DEFINITIONS, NAME);
 
         // - jcr:requiredPrimaryTypes (NAME)
         //   = 'nt:base' protected mandatory multiple
@@ -205,123 +348,50 @@ class RegistrationEditor extends Default
         }
     }
 
-    /**
-     * Updates the {@link #changedTypes} set to contain also all subtypes
-     * that may have been affected by the content changes even if they haven't
-     * been directly modified.
-     *
-     * @param types {@code /jcr:system/jcr:nodeTypes} after the changes
-     */
-    private void findAllAffectedTypes(NodeState types) {
-        Queue<String> queue = Queues.newArrayDeque(changedTypes);
-        while (!queue.isEmpty()) {
-            String name = queue.remove();
-
-            // TODO: We should be able to do this with just one pass
-            for (ChildNodeEntry entry : types.getChildNodeEntries()) {
-                NodeState type = entry.getNodeState();
-                PropertyState supertypes = type.getProperty(JCR_SUPERTYPES);
-                if (supertypes != null) {
-                    for (String superName : supertypes.getValue(NAMES)) {
-                        if (name.equals(superName)) {
-                            if (!changedTypes.add(entry.getName())) {
-                                queue.add(entry.getName());
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Verifies that none of the remaining node types still references
-     * one of the removed types.
-     *
-     * @param types {@code /jcr:system/jcr:nodeTypes} after the changes
-     * @throws CommitFailedException if a removed type is still referenced
-     */
-    private void checkTypeReferencesToRemovedTypes(NodeState types)
-            throws CommitFailedException {
-        for (ChildNodeEntry entry : types.getChildNodeEntries()) {
-            NodeState type = entry.getNodeState();
-
-            // Are there any supertype references to removed types?
-            PropertyState supertypes = type.getProperty(JCR_SUPERTYPES);
-            if (supertypes != null) {
-                for (String superName : supertypes.getValue(NAMES)) {
-                    if (removedTypes.contains(superName)) {
-                        throw new CommitFailedException(
-                                "Constraint", 31,
-                                "Removed type " + superName
-                                + " is still referenced as a supertype of "
-                                + entry.getName());
-                    }
-                }
-            }
-
-            // Are there any child node definition references to removed types?
-            for (ChildNodeEntry childEntry : types.getChildNodeEntries()) {
-                String childName = childEntry.getName();
-                if (childName.startsWith(JCR_CHILDNODEDEFINITION)) {
-                    NodeState definition = childEntry.getNodeState();
-                    PropertyState requiredTypes =
-                            definition.getProperty(JCR_REQUIREDTYPE);
-                    if (requiredTypes != null) {
-                        for (String required : requiredTypes.getValue(NAMES)) {
-                            if (removedTypes.contains(required)) {
-                                throw new CommitFailedException(
-                                        "Constraint", 32,
-                                        "Removed type " + required
-                                        + " is still referenced as a required "
-                                        + " primary child node type in "
-                                        + entry.getName());
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     //------------------------------------------------------------< Editor >--
 
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
-        if (!removedTypes.isEmpty()) {
-            checkTypeReferencesToRemovedTypes(after);
-        }
-
-        if (!changedTypes.isEmpty()) {
-            findAllAffectedTypes(after);
-        }
+        if (modified) {
+            NodeBuilder types = builder.child(JCR_SYSTEM).child(JCR_NODE_TYPES);
+            for (String name : types.getChildNodeNames()) {
+                validateAndCompileType(types, name);
+            }
+            for (String name : types.getChildNodeNames()) {
+                mergeSupertypes(types, types.child(name));
+            }
+            for (String name : types.getChildNodeNames()) {
+                NodeBuilder type = types.child(name);
+                for (String supername : getNames(type, OAK_SUPERTYPES)) {
+                    addNameToList(types.child(supername), OAK_SUBTYPES, name);
+                }
+            }
 
-        if (!changedTypes.isEmpty() || !removedTypes.isEmpty()) {
-            // TODO: Find and re-validate any nodes in the repository that
-            // refer to any of the changed (or removed) node types.
+            if (!changedTypes.isEmpty() || !removedTypes.isEmpty()) {
+                // TODO: Find and re-validate any nodes in the repository that
+                // refer to any of the changed (or removed) node types.
+            }
         }
     }
 
     @Override
-    public Validator childNodeAdded(String name, NodeState after)
-            throws CommitFailedException {
-        validateAndCompile(name, after);
+    public Validator childNodeAdded(String name, NodeState after) {
+        modified = true;
         return null;
     }
 
     @Override
     public Validator childNodeChanged(
-            String name, NodeState before, NodeState after)
-            throws CommitFailedException {
-        validateAndCompile(name, after);
+            String name, NodeState before, NodeState after) {
+        modified = true;
         changedTypes.add(name);
         return null;
     }
 
     @Override
-    public Validator childNodeDeleted(String name, NodeState before)
-            throws CommitFailedException {
+    public Validator childNodeDeleted(String name, NodeState before) {
+        modified = true;
         removedTypes.add(name);
         return null;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java Fri Apr 12 10:47:44 2013
@@ -17,13 +17,13 @@
 package org.apache.jackrabbit.oak.plugins.nodetype;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.newArrayList;
 import static org.apache.jackrabbit.JcrConstants.JCR_ISMIXIN;
 import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.JCR_REQUIREDTYPE;
-import static org.apache.jackrabbit.JcrConstants.JCR_SUPERTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_VALUECONSTRAINTS;
-import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
 import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
 import static org.apache.jackrabbit.oak.api.Type.NAME;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
@@ -33,7 +33,6 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.nodetype.constraint.Constraints.valueConstraint;
 
 import java.util.List;
-import java.util.Queue;
 import java.util.Set;
 
 import javax.jcr.PropertyType;
@@ -49,8 +48,6 @@ import org.apache.jackrabbit.oak.spi.sta
 import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Queues;
-import com.google.common.collect.Sets;
 
 /**
  * Validator implementation that check JCR node type constraints.
@@ -69,6 +66,8 @@ class TypeEditor extends DefaultEditor {
 
     private final NodeState types;
 
+    private final List<String> typeNames = newArrayList();
+
     private EffectiveType effective = null;
 
     TypeEditor(NodeState types) {
@@ -99,15 +98,14 @@ class TypeEditor extends DefaultEditor {
     @Override
     public void enter(NodeState before, NodeState after)
             throws CommitFailedException {
-        Iterable<String> names = computeEffectiveType(after);
+        computeEffectiveType(after);
 
         // find matching entry in the parent node's effective type
         // TODO: this should be in childNodeAdded()
-        if (parent != null
-                && parent.effective.getDefinition(nodeName, names) == null) {
+        if (parent != null && parent.effective.getDefinition(
+                nodeName, effective.getTypeNames()) == null) {
             throw constraintViolation(
-                    1, "Incorrect node type of child node " + nodeName
-                    + " with types " + Joiner.on(", ").join(names));
+                    1, "Incorrect node type of child node " + nodeName);
         }
     }
 
@@ -126,7 +124,7 @@ class TypeEditor extends DefaultEditor {
 
     private CommitFailedException constraintViolation(int code, String message) {
         return new CommitFailedException(
-                "Constraint", code, getPath() + ": " + message);
+                CONSTRAINT, code, getPath() + ": " + message + " " + typeNames);
     }
 
     @Override
@@ -230,19 +228,17 @@ class TypeEditor extends DefaultEditor {
      * of effective node type definitions.
      *
      * @param node node state
-     * @return names of the types that make up the effective type
      * @throws CommitFailedException if the effective node type is invalid
      */
-    private Iterable<String> computeEffectiveType(NodeState node)
+    private void computeEffectiveType(NodeState node)
             throws CommitFailedException {
         List<NodeState> list = Lists.newArrayList();
-        Set<String> names = Sets.newLinkedHashSet();
 
         // primary type
         PropertyState primary = node.getProperty(JCR_PRIMARYTYPE);
         if (primary != null && primary.getType() == NAME) {
             String name = primary.getValue(NAME);
-            names.add(name);
+            typeNames.add(name);
 
             NodeState type = types.getChildNode(name);
             if (!type.exists()) {
@@ -263,57 +259,25 @@ class TypeEditor extends DefaultEditor {
         PropertyState mixins = node.getProperty(JCR_MIXINTYPES);
         if (mixins != null && mixins.getType() == NAMES) {
             for (String name : mixins.getValue(NAMES)) {
-                if (names.add(name)) {
-                    NodeState type = types.getChildNode(name);
-                    if (!type.exists()) {
-                        throw constraintViolation(
-                                9, "Mixin node type " + name + " does not exist");
-                    } else if (!getBoolean(type, JCR_ISMIXIN)) {
-                        throw constraintViolation(
-                                10, "Can not use primary type " + name + " as mixin");
-                    } else if (getBoolean(type, JCR_IS_ABSTRACT)) {
-                        throw constraintViolation(
-                                11, "Can not use abstract type " + name + " as mixin");
-                    }
+                typeNames.add(name);
 
-                    list.add(type);
+                NodeState type = types.getChildNode(name);
+                if (!type.exists()) {
+                    throw constraintViolation(
+                            9, "Mixin node type " + name + " does not exist");
+                } else if (!getBoolean(type, JCR_ISMIXIN)) {
+                    throw constraintViolation(
+                            10, "Can not use primary type " + name + " as mixin");
+                } else if (getBoolean(type, JCR_IS_ABSTRACT)) {
+                    throw constraintViolation(
+                            11, "Can not use abstract type " + name + " as mixin");
                 }
-            }
-        }
-
-        // supertypes
-        Queue<NodeState> queue = Queues.newArrayDeque(list);
-        while (!queue.isEmpty()) {
-            NodeState type = queue.remove();
-
-            PropertyState supertypes = type.getProperty(JCR_SUPERTYPES);
-            if (supertypes != null) {
-                for (String name : supertypes.getValue(NAMES)) {
-                    if (names.add(name)) {
-                        NodeState supertype = types.getChildNode(name);
-                        if (supertype.exists()) {
-                            list.add(supertype);
-                            queue.add(supertype);
-                        } else {
-                            // TODO: ignore/warning/error?
-                        }
-                    }
-                }
-            }
-        }
 
-        // always include nt:base
-        if (names.add(NT_BASE)) {
-            NodeState base = types.getChildNode(NT_BASE);
-            if (base.exists()) {
-                list.add(base);
-            } else {
-                // TODO: ignore/warning/error?
+                list.add(type);
             }
         }
 
         effective = new EffectiveType(list);
-        return names;
     }
 
     private boolean getBoolean(NodeState node, String name) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd?rev=1467235&r1=1467234&r2=1467235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd Fri Apr 12 10:47:44 2013
@@ -426,6 +426,11 @@
   + jcr:childNodeDefinition (nt:childNodeDefinition) = nt:childNodeDefinition protected sns
 
 [oak:nodeType] > nt:nodeType
+  - oak:supertypes (NAME) protected multiple autocreated
+  - oak:subtypes (NAME) protected multiple autocreated
+  - oak:namedProperties (NAME) protected multiple autocreated
+  - oak:mandatoryProperties (NAME) protected multiple autocreated
+  - oak:mandatoryChildNodes (NAME) protected multiple autocreated
   + oak:namedPropertyDefinitions (oak:namedPropertyDefinitions) = oak:namedPropertyDefinitions protected mandatory
   + oak:residualPropertyDefinitions (oak:propertyDefinitions) = oak:propertyDefinitions protected mandatory
   + oak:namedChildNodeDefinitions (oak:namedChildNodeDefinitions) = oak:namedChildNodeDefinitions protected mandatory