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/03/19 11:54:44 UTC

svn commit: r1458219 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/ oak-core/src/main/...

Author: jukka
Date: Tue Mar 19 10:54:44 2013
New Revision: 1458219

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

Major optimization of type validation using precompiled type information

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditorProvider.java
      - copied, changed from r1458183, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidatorProvider.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidatorProvider.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.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/write/InitialContent.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeTemplateImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/VisibleEditor.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorTest.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java Tue Mar 19 10:54:44 2013
@@ -142,10 +142,14 @@ public class RootImpl implements Root {
         rootTree = new TreeImpl(this, lastMove);
     }
 
-    // TODO: review if this constructor really makes sense and cannot be replaced.
+    // TODO: review if these constructors really make sense and cannot be replaced.
     public RootImpl(NodeStore store) {
+        this(store, EmptyHook.INSTANCE);
+    }
+
+    public RootImpl(NodeStore store, CommitHook hook) {
         // FIXME: define proper default or pass workspace name with the constructor
-        this(store, EmptyHook.INSTANCE, Oak.DEFAULT_WORKSPACE_NAME, SystemSubject.INSTANCE,
+        this(store, hook, Oak.DEFAULT_WORKSPACE_NAME, SystemSubject.INSTANCE,
                 new OpenSecurityProvider(), new CompositeQueryIndexProvider());
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java Tue Mar 19 10:54:44 2013
@@ -49,6 +49,20 @@ class NameValidator extends DefaultValid
         }
 
         String local = name.substring(colon + 1);
+
+        int n = local.length();
+        if (n > 3 && local.charAt(n - 1) == ']') {
+            int i = n - 2;
+            while (i > 1 && Character.isDigit(local.charAt(i))) {
+                i--;
+            }
+            if (local.charAt(i) != '[') {
+                throw new CommitFailedException("Invalid name index " + name);
+            } else {
+                local = local.substring(0, i);
+            }
+        }
+
         if (!Namespaces.isValidLocalName(local)) {
             throw new CommitFailedException("Invalid name: " + name);
         }

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=1458219&r1=1458218&r2=1458219&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 Tue Mar 19 10:54:44 2013
@@ -18,14 +18,17 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.JcrConstants.JCR_CHILDNODEDEFINITION;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_MULTIPLE;
 import static org.apache.jackrabbit.JcrConstants.JCR_NAME;
 import static org.apache.jackrabbit.JcrConstants.JCR_NODETYPENAME;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.JCR_PROPERTYDEFINITION;
+import static org.apache.jackrabbit.JcrConstants.JCR_REQUIREDPRIMARYTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_REQUIREDTYPE;
 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.oak.api.Type.NAME;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
@@ -126,12 +129,20 @@ class RegistrationEditor extends Default
         PropertyState name = definition.getProperty(JCR_NAME);
         NodeBuilder definitions;
         if (name != null) {
+            String escapedName = name.getValue(NAME);
+            if (JCR_PRIMARYTYPE.equals(escapedName)) {
+                escapedName = "oak:primaryType";
+            } else if (JCR_MIXINTYPES.equals(escapedName)) {
+                escapedName = "oak:mixinTypes";
+            } else if (JCR_UUID.equals(escapedName)) {
+                escapedName = "oak:uuid";
+            }
             definitions = type.child("oak:namedPropertyDefinitions");
             definitions.setProperty(
                     JCR_PRIMARYTYPE, "oak:namedPropertyDefinitions", NAME);
-            definitions = definitions.child(name.getValue(NAME));
+            definitions = definitions.child(escapedName);
         } else {
-            definitions = type.child("oak:residualChildNodeDefinitions");
+            definitions = type.child("oak:residualPropertyDefinitions");
         }
         definitions.setProperty(
                 JCR_PRIMARYTYPE, "oak:propertyDefinitions", NAME);
@@ -149,7 +160,11 @@ class RegistrationEditor extends Default
         // - jcr:multiple (BOOLEAN) protected mandatory
         PropertyState multiple = definition.getProperty(JCR_MULTIPLE);
         if (multiple != null && multiple.getValue(Type.BOOLEAN)) {
-            key = key + "-ARRAY";
+            if ("BINARY".equals(key)) {
+                key = "BINARIES";
+            } else {
+                key = key + "S";
+            }
         }
 
         definitions.setNode(key, definition);
@@ -162,9 +177,9 @@ class RegistrationEditor extends Default
         PropertyState name = definition.getProperty(JCR_NAME);
         NodeBuilder definitions;
         if (name != null) {
-            definitions = type.child("oak:namedPropertyDefinitions");
+            definitions = type.child("oak:namedChildNodeDefinitions");
             definitions.setProperty(
-                    JCR_PRIMARYTYPE, "oak:namedPropertyDefinitions", NAME);
+                    JCR_PRIMARYTYPE, "oak:namedChildNodeDefinitions", NAME);
             definitions = definitions.child(name.getValue(NAME));
         } else {
             definitions = type.child("oak:residualChildNodeDefinitions");
@@ -174,7 +189,8 @@ class RegistrationEditor extends Default
 
         // - jcr:requiredPrimaryTypes (NAME)
         //   = 'nt:base' protected mandatory multiple
-        PropertyState requiredTypes = definition.getProperty(JCR_REQUIREDTYPE);
+        PropertyState requiredTypes =
+                definition.getProperty(JCR_REQUIREDPRIMARYTYPES);
         if (requiredTypes != null) {
             for (String key : requiredTypes.getValue(NAMES)) {
                 if (!types.hasChildNode(key)) {

Added: 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=1458219&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditor.java Tue Mar 19 10:54:44 2013
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.nodetype;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+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_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_REQUIREDTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_SUPERTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VALUECONSTRAINTS;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+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.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_IS_ABSTRACT;
+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.annotation.Nonnull;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.ConstraintViolationException;
+
+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.plugins.value.ValueFactoryImpl;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+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.
+ *
+ * TODO: check protected properties and the structure they enforce. some of
+ *       those checks may have to go into separate validator classes. This class
+ *       should only perform checks based on node type information. E.g. it
+ *       cannot and should not check whether the value of the protected jcr:uuid
+ *       is unique.
+ */
+class TypeEditor extends DefaultEditor {
+
+    private final TypeEditor parent;
+
+    private final String nodeName;
+
+    private final NodeState types;
+
+    private final List<NodeState> effective = Lists.newArrayList();
+
+    TypeEditor(NodeState types) {
+        this.parent = null;
+        this.nodeName = null;
+        this.types = checkNotNull(types);
+    }
+
+    private TypeEditor(TypeEditor parent, String name) {
+        this.parent = checkNotNull(parent);
+        this.nodeName = checkNotNull(name);
+        this.types = parent.types;
+    }
+
+    /**
+     * Computes the effective type of the modified type.
+     */
+    @Override
+    public void enter(NodeState before, NodeState after)
+            throws CommitFailedException {
+        Iterable<String> names = computeEffectiveType(after);
+
+        // find matching entry in the parent node's effective type
+        if (parent != null) {
+            parent.getDefinition(nodeName, names);
+        }
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        // TODO: add any auto-created items that are still missing
+
+        // verify the presence of all mandatory items
+        for (NodeState type : effective) {
+            NodeState properties =
+                    type.getChildNode("oak:namedPropertyDefinitions");
+            if (properties != null) {
+                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 (after.getProperty(name) == null
+                            && isMandatory(name, entry.getNodeState())) {
+                        throw constraintViolation(
+                                "Missing mandatory property " + name);
+                    }
+                }
+            }
+
+            NodeState childNodes =
+                    type.getChildNode("oak:namedChildNodeDefinitions");
+            if (childNodes != null) {
+                for (ChildNodeEntry entry : childNodes.getChildNodeEntries()) {
+                    String name = entry.getName();
+                    if (!after.hasChildNode(name)
+                            && isMandatory(name, entry.getNodeState())) {
+                        throw constraintViolation(
+                                "Missing mandatory child node " + name);
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean isMandatory(String name, NodeState definitions) {
+        for (ChildNodeEntry entry : definitions.getChildNodeEntries()) {
+            NodeState definition = entry.getNodeState();
+            if (getBoolean(definition, JCR_MANDATORY)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+;    private CommitFailedException constraintViolation(String message) {
+        return new CommitFailedException(
+                new ConstraintViolationException(message));
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) throws CommitFailedException {
+        NodeState definition = getDefinition(after);
+        checkValueConstraints(definition, after);
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after)
+            throws CommitFailedException {
+        NodeState definition = getDefinition(after);
+        checkValueConstraints(definition, after);
+    }
+
+    @Override
+    public Editor childNodeAdded(String name, NodeState after)
+            throws CommitFailedException {
+        return new TypeEditor(this, name);
+    }
+
+    @Override
+    public Editor childNodeChanged(
+            String name, NodeState before, NodeState after)
+            throws CommitFailedException {
+        return new TypeEditor(this, name);
+    }
+
+    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) {
+            return "BINARIES";
+        } else if (type == Type.BINARY) {
+            return "BINARY";
+        } else if (type == Type.BOOLEAN) {
+            return "BOOLEAN";
+        } else if (type == Type.BOOLEANS) {
+            return "BOOLEANS";
+        } else if (type == Type.DATE) {
+            return "DATE";
+        } else if (type == Type.DATES) {
+            return "DATES";
+        } else if (type == Type.DECIMAL) {
+            return "DECIMAL";
+        } else if (type == Type.DECIMALS) {
+            return "DECIMALS";
+        } else if (type == Type.DOUBLE) {
+            return "DOUBLE";
+        } else if (type == Type.DOUBLES) {
+            return "DOUBLES";
+        } else if (type == Type.LONG) {
+            return "LONG";
+        } else if (type == Type.LONGS) {
+            return "LONGS";
+        } else if (type == Type.NAME) {
+            return "NAME";
+        } else if (type == Type.NAMES) {
+            return "NAMES";
+        } else if (type == Type.PATH) {
+            return "PATH";
+        } else if (type == Type.PATHS) {
+            return "PATHS";
+        } else if (type == Type.REFERENCE) {
+            return "REFERENCE";
+        } else if (type == Type.REFERENCES) {
+            return "REFERENCES";
+        } else if (type == Type.STRING) {
+            return "STRING";
+        } else if (type == Type.STRINGS) {
+            return "STRINGS";
+        } else if (type == Type.URI) {
+            return "URI";
+        } else if (type == Type.URIS) {
+            return "URIS";
+        } else if (type == Type.WEAKREFERENCE) {
+            return "WEAKREFERENCE";
+        } else if (type == Type.WEAKREFERENCES) {
+            return "WEAKREFERENCES";
+        } else {
+            return "unknown";
+        }
+    }
+
+    /**
+     * Finds a matching definition for a property with the given name and type.
+     *
+     * @param property modified property
+     * @return matching property definition
+     * @throws CommitFailedException if a matching definition was not found
+     */
+    @Nonnull
+    private NodeState getDefinition(PropertyState property)
+            throws CommitFailedException {
+        String propertyName = property.getName();
+        Type<?> propertyType = property.getType();
+
+        String escapedName;
+        if (JCR_PRIMARYTYPE.equals(propertyName)) {
+            escapedName = "oak:primaryType";
+        } else if (JCR_MIXINTYPES.equals(propertyName)) {
+            escapedName = "oak:mixinTypes";
+        } else if (JCR_UUID.equals(propertyName)) {
+            escapedName = "oak:uuid";
+        } else {
+            escapedName = propertyName;
+        }
+
+        String definedType = getTypeKey(propertyType);
+        String undefinedType;
+        if (propertyType.isArray()) {
+            undefinedType = "UNDEFINEDS";
+        } else {
+            undefinedType = "UNDEFINED";
+        }
+
+        // Find matching named property definition
+        for (NodeState type : effective) {
+            NodeState named = type.getChildNode("oak:namedPropertyDefinitions");
+            if (named != null) {
+                NodeState definitions = named.getChildNode(escapedName);
+                if (definitions != null) {
+                    NodeState definition = definitions.getChildNode(definedType);
+                    if (definition == null) {
+                        definition = definitions.getChildNode(undefinedType);
+                    }
+                    if (definition != null) {
+                        return definition;
+                    } else {
+                        throw constraintViolation(
+                                "No matching definition found for property "
+                                        + propertyName);
+                    }
+                }
+            }
+        }
+
+        // Find matching residual property definition
+        for (NodeState type : effective) {
+            NodeState residual =
+                    type.getChildNode("oak:residualPropertyDefinitions");
+            if (residual != null) {
+                NodeState definition = residual.getChildNode(definedType);
+                if (definition == null) {
+                    definition = residual.getChildNode(undefinedType);
+                }
+                if (definition != null) {
+                    return definition;
+                }
+            }
+        }
+
+        throw constraintViolation(
+                "No matching definition found for property " + propertyName);
+    }
+
+    /**
+     * Finds a matching definition for a child node with the given name and
+     * types.
+     *
+     * @param nodeName child node name
+     * @param nodeType effective types of the child node
+     * @return matching child node definition
+     * @throws CommitFailedException if a matching definition was not found
+     */
+    @Nonnull
+    private NodeState getDefinition(String nodeName, Iterable<String> nodeType)
+            throws CommitFailedException {
+        boolean sns = false;
+        int n = nodeName.length();
+        if (n > 3 && nodeName.charAt(n - 1) == ']') {
+            int i = n - 2;
+            while (i > 1 && Character.isDigit(nodeName.charAt(i))) {
+                i--;
+            }
+            if (nodeName.charAt(i) == '[') {
+                nodeName = nodeName.substring(0, i);
+                sns = true;
+            }
+        }
+
+        // Find matching named property definition
+        for (NodeState type : effective) {
+            NodeState named = type.getChildNode("oak:namedChildNodeDefinitions");
+            if (named != null) {
+                NodeState definitions = named.getChildNode(nodeName);
+                if (definitions != null) {
+                    for (String typeName : nodeType) {
+                        NodeState definition = definitions.getChildNode(typeName);
+                        if (definition != null) {
+                            return definition;
+                        }
+                    }
+
+                    throw constraintViolation(
+                            "Incorrect node type of child node " + nodeName);
+                }
+            }
+        }
+
+        // Find matching residual property definition
+        for (NodeState type : effective) {
+            NodeState residual =
+                    type.getChildNode("oak:residualChildNodeDefinitions");
+            if (residual != null) {
+                for (String typeName : nodeType) {
+                    NodeState definition = residual.getChildNode(typeName);
+                    if (definition != null) {
+                        return definition;
+                    }
+                }
+            }
+        }
+
+        throw constraintViolation(
+                "Incorrect node type of child node " + nodeName);
+    }
+
+    private void checkValueConstraints(
+            NodeState definition, PropertyState property)
+            throws CommitFailedException {
+        if (property.count() == 0) {
+            return;
+        }
+
+        PropertyState constraints =
+                definition.getProperty(JCR_VALUECONSTRAINTS);
+        if (constraints == null || constraints.count() == 0) {
+            return;
+        }
+
+        PropertyState required = definition.getProperty(JCR_REQUIREDTYPE);
+        if (required == null) {
+            return;
+        }
+
+        int type;
+        String value = required.getValue(STRING);
+        if ("BINARY".equals(value)) {
+            type = PropertyType.BINARY;
+        } else if ("BOOLEAN".equals(value)) {
+            type = PropertyType.BOOLEAN;
+        } else if ("DATE".equals(value)) {
+            type = PropertyType.DATE;
+        } else if ("DECIMAL".equals(value)) {
+            type = PropertyType.DECIMAL;
+        } else if ("DOUBLE".equals(value)) {
+            type = PropertyType.DOUBLE;
+        } else if ("LONG".equals(value)) {
+            type = PropertyType.LONG;
+        } else if ("NAME".equals(value)) {
+            type = PropertyType.NAME;
+        } else if ("PATH".equals(value)) {
+            type = PropertyType.PATH;
+        } else if ("REFERENCE".equals(value)) {
+            type = PropertyType.REFERENCE;
+        } else if ("STRING".equals(value)) {
+            type = PropertyType.STRING;
+        } else if ("URI".equals(value)) {
+            type = PropertyType.URI;
+        } else if ("WEAKREFERENCE".equals(value)) {
+            type = PropertyType.WEAKREFERENCE;
+        } else {
+            return;
+        }
+
+        for (String constraint : constraints.getValue(STRINGS)) {
+            Predicate<Value> predicate = valueConstraint(type, constraint);
+            for (Value v : ValueFactoryImpl.createValues(property, null)) {
+                if (predicate.apply(v)) {
+                    return;
+                }
+            }
+        }
+        throw constraintViolation("Value constraint violation");
+    }
+
+    /**
+     * Collects the primary and mixin types and all related supertypes
+     * of the given node and places them in the {@link #effective} list
+     * 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)
+            throws CommitFailedException {
+        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);
+
+            NodeState type = types.getChildNode(name);
+            if (type == null) {
+                throw constraintViolation(
+                        "Primary node type " + name + " does not exist");
+            } else if (getBoolean(type, JCR_ISMIXIN)) {
+                throw constraintViolation(
+                        "Can not use mixin type " + name + " as primary");
+            } else if (getBoolean(type, JCR_IS_ABSTRACT)) {
+                throw constraintViolation(
+                        "Can not use abstract type " + name + " as primary");
+            }
+
+            effective.add(type);
+        }
+
+        // mixin types
+        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 == null) {
+                        throw constraintViolation(
+                                "Mixin node type " + name + " does not exist");
+                    } else if (!getBoolean(type, JCR_ISMIXIN)) {
+                        throw constraintViolation(
+                                "Can not use primary type " + name + " as mixin");
+                    } else if (getBoolean(type, JCR_IS_ABSTRACT)) {
+                        throw constraintViolation(
+                                "Can not use abstract type " + name + " as mixin");
+                    }
+
+                    effective.add(type);
+                }
+            }
+        }
+
+        // supertypes
+        Queue<NodeState> queue = Queues.newArrayDeque(effective);
+        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 != null) {
+                            effective.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 != null) {
+                effective.add(base);
+            } else {
+                // TODO: ignore/warning/error?
+            }
+        }
+
+        return names;
+    }
+
+}

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditorProvider.java (from r1458183, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidatorProvider.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditorProvider.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditorProvider.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidatorProvider.java&r1=1458183&r2=1458219&rev=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypeEditorProvider.java Tue Mar 19 10:54:44 2013
@@ -16,36 +16,32 @@
  */
 package org.apache.jackrabbit.oak.plugins.nodetype;
 
-import java.util.Map;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
-import org.apache.jackrabbit.oak.core.ReadOnlyTree;
-import org.apache.jackrabbit.oak.namepath.GlobalNameMapper;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.namepath.NamePathMapperImpl;
-import org.apache.jackrabbit.oak.plugins.name.Namespaces;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
-import org.apache.jackrabbit.oak.spi.commit.Validator;
-import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 @Component
 @Service(EditorProvider.class)
-public class TypeValidatorProvider extends ValidatorProvider {
+public class TypeEditorProvider implements EditorProvider {
 
     @Override
-    public Validator getRootValidator(NodeState before, final NodeState after) {
-        ReadOnlyNodeTypeManager ntm =
-                ReadOnlyNodeTypeManager.getInstance(after);
-        final ReadOnlyTree root = new ReadOnlyTree(after);
-        NamePathMapper mapper = new NamePathMapperImpl(new GlobalNameMapper() {
-            @Override
-            protected Map<String, String> getNamespaceMap() {
-                return Namespaces.getNamespaceMap(root);
+    public Editor getRootEditor(
+            NodeState before, NodeState after, NodeBuilder builder) {
+        NodeState system = after.getChildNode(JCR_SYSTEM);
+        if (system != null) {
+            NodeState types = system.getChildNode(JCR_NODE_TYPES);
+            if (types != null) {
+                return new VisibleEditor(new TypeEditor(types));
             }
-        });
-        return new TypeValidator(ntm, root, mapper);
+        }
+        return null;
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java Tue Mar 19 10:54:44 2013
@@ -24,6 +24,8 @@ import org.apache.jackrabbit.oak.core.Ro
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -80,7 +82,7 @@ public class InitialContent implements R
         } catch (CommitFailedException e) {
             throw new RuntimeException(e);
         }
-        BuiltInNodeTypes.register(new RootImpl(store));
+        BuiltInNodeTypes.register(new RootImpl(store, new EditorHook(new RegistrationEditorProvider())));
         return store.getRoot();
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeTemplateImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeTemplateImpl.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeTemplateImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeTemplateImpl.java Tue Mar 19 10:54:44 2013
@@ -166,7 +166,7 @@ class NodeTypeTemplateImpl extends Named
         if (propertyDefinitionTemplates != null) {
             int pdn = 1;
             for (PropertyDefinitionTemplateImpl pdt : propertyDefinitionTemplates) {
-                Tree tree = type.addChild(JCR_PROPERTYDEFINITION + pdn++);
+                Tree tree = type.addChild(JCR_PROPERTYDEFINITION + "[" + pdn++ + "]");
                 tree.setProperty(
                         JCR_PRIMARYTYPE, NT_PROPERTYDEFINITION, Type.NAME);
                 pdt.writeTo(tree);
@@ -176,7 +176,7 @@ class NodeTypeTemplateImpl extends Named
         if (nodeDefinitionTemplates != null) {
             int ndn = 1;
             for (NodeDefinitionTemplateImpl ndt : nodeDefinitionTemplates) {
-                Tree tree = type.addChild(JCR_CHILDNODEDEFINITION + ndn++);
+                Tree tree = type.addChild(JCR_CHILDNODEDEFINITION + "[" + ndn++ + "]");
                 tree.setProperty(
                         JCR_PRIMARYTYPE, NT_CHILDNODEDEFINITION, Type.NAME);
                 ndt.writeTo(tree);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/VisibleEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/VisibleEditor.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/VisibleEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/VisibleEditor.java Tue Mar 19 10:54:44 2013
@@ -31,7 +31,7 @@ import org.apache.jackrabbit.oak.spi.sta
  *
  * @since Oak 0.7
  */
-public class VisibleEditor extends DefaultEditor {
+public class VisibleEditor implements Editor {
 
     @CheckForNull
     public static Editor wrap(@CheckForNull Editor editor) {
@@ -53,6 +53,18 @@ public class VisibleEditor extends Defau
     }
 
     @Override
+    public void enter(NodeState before, NodeState after)
+            throws CommitFailedException {
+        editor.enter(before, after);
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        editor.leave(before, after);
+    }
+
+    @Override
     public void propertyAdded(PropertyState after) throws CommitFailedException {
         if (isVisible(after.getName())) {
             editor.propertyAdded(after);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorTest.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorTest.java Tue Mar 19 10:54:44 2013
@@ -59,11 +59,16 @@ public class NameValidatorTest {
         validator.childNodeAdded("invalid/name", EMPTY_NODE);
     }
 
-    @Test(expected = CommitFailedException.class)
-    public void testIndexInName() throws CommitFailedException {
+    @Test
+    public void testValidIndexInName() throws CommitFailedException {
         validator.childNodeAdded("name[1]", EMPTY_NODE);
     }
 
+    @Test(expected = CommitFailedException.class)
+    public void testInvalidIndexInName() throws CommitFailedException {
+        validator.childNodeAdded("name[x]", EMPTY_NODE);
+    }
+
     @Test
     public void testValidName() throws CommitFailedException {
         validator.childNodeAdded("name", EMPTY_NODE);

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java Tue Mar 19 10:54:44 2013
@@ -34,7 +34,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.name.NamespaceValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.DefaultTypeEditor;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
-import org.apache.jackrabbit.oak.plugins.nodetype.TypeValidatorProvider;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
 import org.apache.jackrabbit.oak.plugins.version.VersionHook;
 import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
@@ -70,7 +70,7 @@ public class Jcr {
 
         with(new NameValidatorProvider());
         with(new NamespaceValidatorProvider());
-        with(new TypeValidatorProvider());
+        with(new TypeEditorProvider());
         with(new RegistrationEditorProvider());
         with(new ConflictValidatorProvider());
 

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java?rev=1458219&r1=1458218&r2=1458219&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java Tue Mar 19 10:54:44 2013
@@ -36,7 +36,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.name.NamespaceValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.DefaultTypeEditor;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
-import org.apache.jackrabbit.oak.plugins.nodetype.TypeValidatorProvider;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
 import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
 import org.apache.jackrabbit.webdav.jcr.JCRWebdavServerServlet;
@@ -167,7 +167,7 @@ public class Main {
                 .with(new ConflictValidatorProvider())
                 .with(new NameValidatorProvider())
                 .with(new NamespaceValidatorProvider())
-                .with(new TypeValidatorProvider())
+                .with(new TypeEditorProvider())
                 .with(new RegistrationEditorProvider())
                 .with(new DefaultTypeEditor())
                 .with(new Property2IndexHookProvider())