You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2006/01/30 15:08:06 UTC

svn commit: r373516 - in /incubator/jackrabbit/trunk/jackrabbit/src: main/java/org/apache/jackrabbit/core/nodetype/ test/java/org/apache/jackrabbit/core/nodetype/

Author: stefan
Date: Mon Jan 30 06:07:41 2006
New Revision: 373516

URL: http://svn.apache.org/viewcvs?rev=373516&view=rev
Log:
JCR-274: committing sandro's patch with some modifications (mainly formatting related)
thanks sandro!

Added:
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java   (with props)
Modified:
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java?rev=373516&r1=373515&r2=373516&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java Mon Jan 30 06:07:41 2006
@@ -25,11 +25,12 @@
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.TreeSet;
-import java.util.Arrays;
 
 /**
  * An <code>EffectiveNodeType</code> represents one or more
@@ -69,8 +70,8 @@
     }
 
     /**
-     * Factory method: creates an effective node type
-     * representation of an existing (i.e. registered) node type.
+     * Factory method: creates an effective node type representation of an
+     * existing (i.e. registered) node type.
      *
      * @param ntReg
      * @param nodeTypeName
@@ -84,10 +85,10 @@
     }
 
     /**
-     * Factory method: creates an effective node type
-     * representation of a node type definition. Whereas all referenced
-     * node types must exist (i.e. must be registered), the definition itself
-     * is not required to be registered.
+     * Factory method: creates an effective node type representation of a
+     * node type definition. Whereas all referenced node types must exist
+     * (i.e. must be registered), the definition itself is not required to be
+     * registered.
      *
      * @param ntReg
      * @param ntd
@@ -97,6 +98,29 @@
      */
     public static EffectiveNodeType create(NodeTypeRegistry ntReg, NodeTypeDef ntd)
             throws NodeTypeConflictException, NoSuchNodeTypeException {
+        return create(ntReg, ntd, null, null);
+    }
+
+    /**
+     * Package private factory method.
+     * <p/>
+     * Creates an effective node type representation of a node type definition.
+     * Whereas all referenced node types must exist (i.e. must be registered),
+     * the definition itself is not required to be registered.
+     * todo check javadoc/param names
+     * @param ntReg
+     * @param ntd
+     * @param anEntCache
+     * @param aRegisteredNTDefCache
+     * @return
+     * @throws NodeTypeConflictException
+     * @throws NoSuchNodeTypeException
+     */
+    static EffectiveNodeType create(NodeTypeRegistry ntReg,
+                                    NodeTypeDef ntd,
+                                    EffectiveNodeTypeCache anEntCache,
+                                    Map aRegisteredNTDefCache)
+            throws NodeTypeConflictException, NoSuchNodeTypeException {
         // create empty effective node type instance
         EffectiveNodeType ent = new EffectiveNodeType(ntReg);
         QName ntName = ntd.getName();
@@ -210,7 +234,11 @@
         // resolve supertypes recursively
         QName[] supertypes = ntd.getSupertypes();
         if (supertypes != null && supertypes.length > 0) {
-            ent.internalMerge(ntReg.getEffectiveNodeType(supertypes), true);
+            if (anEntCache == null || aRegisteredNTDefCache == null) {
+                ent.internalMerge(ntReg.getEffectiveNodeType(supertypes), true);
+            } else {
+                ent.internalMerge(ntReg.getEffectiveNodeType(supertypes, anEntCache, aRegisteredNTDefCache), true);
+            }
         }
 
         // we're done
@@ -218,9 +246,10 @@
     }
 
     /**
-     * Factory method: creates a new 'empty' effective node type instance
+     * Package private factory method for creating a new 'empty' effective
+     * node type instance.
      *
-     * @return
+     * @return an 'empty' effective node type instance.
      */
     static EffectiveNodeType create(NodeTypeRegistry ntReg) {
         return new EffectiveNodeType(ntReg);
@@ -905,7 +934,8 @@
      * @throws ConstraintViolationException
      * @throws NoSuchNodeTypeException
      */
-    public void checkRequiredPrimaryType(QName nodeTypeName, QName[] requiredPrimaryTypes)
+    public void checkRequiredPrimaryType(QName nodeTypeName,
+                                         QName[] requiredPrimaryTypes)
             throws ConstraintViolationException, NoSuchNodeTypeException {
         if (requiredPrimaryTypes == null) {
             // no constraint

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java?rev=373516&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java Mon Jan 30 06:07:41 2006
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.core.nodetype;
+
+import org.apache.jackrabbit.core.util.Dumpable;
+import org.apache.jackrabbit.name.QName;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * <code>EffectiveNodeTypeCache</code> ...
+ */
+class EffectiveNodeTypeCache implements Dumpable {
+    /** ordered set of keys */
+    final TreeSet sortedKeys;
+    /** cache of pre-built aggregations of node types */
+    final HashMap aggregates;
+
+    EffectiveNodeTypeCache() {
+        sortedKeys = new TreeSet();
+        aggregates = new HashMap();
+    }
+
+    EffectiveNodeTypeCache(EffectiveNodeTypeCache entCache) {
+        sortedKeys = new TreeSet(entCache.sortedKeys);
+        aggregates = new HashMap(entCache.aggregates);
+    }
+
+    void put(EffectiveNodeType ent) {
+        // we define the weight as the total number of included node types
+        // (through aggregation and inheritance)
+        int weight = ent.getAllNodeTypes().length;
+        // the effective node type is identified by the list of merged
+        // (i.e. aggregated) node types
+        WeightedKey k = new WeightedKey(ent.getMergedNodeTypes(), weight);
+        aggregates.put(k, ent);
+        sortedKeys.add(k);
+    }
+
+    boolean contains(QName[] ntNames) {
+        return aggregates.containsKey(new WeightedKey(ntNames));
+    }
+
+    boolean contains(WeightedKey key) {
+        return aggregates.containsKey(key);
+    }
+
+    EffectiveNodeType get(QName[] ntNames) {
+        return (EffectiveNodeType) aggregates.get(new WeightedKey(ntNames));
+    }
+
+    EffectiveNodeType get(WeightedKey key) {
+        return (EffectiveNodeType) aggregates.get(key);
+    }
+
+    EffectiveNodeType remove(QName[] ntNames) {
+        return remove(new WeightedKey(ntNames));
+    }
+
+    EffectiveNodeType remove(WeightedKey key) {
+        EffectiveNodeType removed = (EffectiveNodeType) aggregates.remove(key);
+        if (removed != null) {
+            // remove index entry
+
+            // FIXME: can't simply call TreeSet.remove(key) because the entry
+            // in sortedKeys might have a different weight and would thus
+            // not be found
+            Iterator iter = sortedKeys.iterator();
+            while (iter.hasNext()) {
+                WeightedKey k = (WeightedKey) iter.next();
+                // WeightedKey.equals(Object) ignores the weight
+                if (key.equals(k)) {
+                    sortedKeys.remove(k);
+                    break;
+                }
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Returns an iterator over the keys. The order of the returned keys is:
+     * <ol>
+     * <li>descending weight</li>
+     * <li>ascending key (i.e. unique identifier of aggregate)</li>
+     * </ol>
+     *
+     * @see WeightedKey#compareTo
+     */
+    Iterator keys() {
+        return sortedKeys.iterator();
+    }
+
+    //-------------------------------------------------------------< Dumpable >
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(PrintStream ps) {
+        ps.println("EffectiveNodeTypeCache (" + this + ")");
+        ps.println();
+        ps.println("EffectiveNodeTypes in cache:");
+        ps.println();
+        Iterator iter = sortedKeys.iterator();
+        while (iter.hasNext()) {
+            WeightedKey k = (WeightedKey) iter.next();
+            //EffectiveNodeType ent = (EffectiveNodeType) aggregates.get(k);
+            ps.println(k);
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+    /**
+     * A <code>WeightedKey</code> uniquely identifies
+     * a combination (i.e. an aggregation) of one or more node types.
+     * The weight is an indicator for the cost involved in building such an
+     * aggregate (e.g. an aggregation of multiple complex node types with deep
+     * inheritance trees is more costly to build/validate than an agreggation
+     * of two very simple node types with just one property definition each).
+     * <p/>
+     * A very simple (and not very accurate) approximation of the weight would
+     * be the number of explicitly aggregated node types (ignoring inheritance
+     * and complexity of each involved node type). A better approximation would
+     * be the number of <b>all</b>, explicitly and implicitly (note that
+     * inheritance is also an aggregation) aggregated node types.
+     * <p/>
+     * The more accurate the weight definition, the more efficient is the
+     * the building of new aggregates.
+     * <p/>
+     * It is important to note that the weight is not part of the key value,
+     * i.e. it is not considered by the <code>hashCode()</code> and
+     * <code>equals(Object)</code> methods. It does however affect the order
+     * of <code>WeightedKey</code> instances. See
+     * <code>{@link #compareTo(Object)}</code> for more information.
+     * <p/>
+     * Let's assume we have an aggregation of node types named "b", "a" and "c".
+     * Its key would be "[a, b, c]" and the weight 3 (using the simple
+     * approximation).
+     */
+    static class WeightedKey implements Comparable {
+        /**
+         * set of node type names, sorted in ascending order
+         */
+        private final TreeSet set;
+        private final int weight;
+
+        /**
+         * @param ntNames
+         */
+        WeightedKey(QName[] ntNames) {
+            this(ntNames, ntNames.length);
+        }
+
+        /**
+         * @param ntNames
+         * @param weight
+         */
+        WeightedKey(QName[] ntNames, int weight) {
+            this.weight = weight;
+
+            set = new TreeSet();
+            for (int i = 0; i < ntNames.length; i++) {
+                // add name to this sorted set
+                set.add(ntNames[i]);
+            }
+        }
+
+        /**
+         * @param ntNames
+         */
+        WeightedKey(Collection ntNames) {
+            this(ntNames, ntNames.size());
+        }
+
+        /**
+         * @param ntNames
+         * @param weight
+         */
+        WeightedKey(Collection ntNames, int weight) {
+            this.weight = weight;
+            set = new TreeSet(ntNames);
+        }
+
+        /**
+         * The key is the string representation of this sorted set
+         * (e.g. the key for a set containing entries "c", "b" and "a" would
+         * be "[a, b, c]").
+         *
+         * @return string representation of this sorted set
+         * @see java.util.AbstractCollection#toString
+         */
+        String getKey() {
+            return set.toString();
+        }
+
+        /**
+         * @return
+         */
+        int getWeight() {
+            return weight;
+        }
+
+        int size() {
+            return set.size();
+        }
+
+        Iterator iterator() {
+            return Collections.unmodifiableSortedSet(set).iterator();
+        }
+
+        Set getSet() {
+            return Collections.unmodifiableSortedSet(set);
+        }
+
+        QName[] toArray() {
+            return (QName[]) set.toArray(new QName[set.size()]);
+        }
+
+        boolean contains(WeightedKey otherKey) {
+            return set.containsAll(otherKey.getSet());
+        }
+
+        WeightedKey subtract(WeightedKey otherKey) {
+            Set tmp = (Set) set.clone();
+            tmp.removeAll(otherKey.getSet());
+            return new WeightedKey(tmp);
+
+        }
+
+        /**
+         * The resulting sort-order is: 1. descending weight, 2. ascending key
+         * (i.e. string representation of this sorted set).
+         *
+         * @param o
+         * @return
+         */
+        public int compareTo(Object o) {
+            WeightedKey other = (WeightedKey) o;
+            if (getWeight() > other.getWeight()) {
+                return -1;
+            } else if (getWeight() < other.getWeight()) {
+                return 1;
+            }
+            return getKey().compareTo(other.getKey());
+        }
+
+        public int hashCode() {
+            int h = 17;
+            // ignore weight
+            Iterator i = set.iterator();
+            while (i.hasNext()) {
+                h *= 37;
+                Object obj = i.next();
+                if (obj != null) {
+                    h += obj.hashCode();
+                }
+            }
+            return h;
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof WeightedKey) {
+                WeightedKey other = (WeightedKey) obj;
+                // ignore weight
+                return set.equals(other.set);
+            }
+            return false;
+        }
+
+        public String toString() {
+            return set.toString() + " (" + weight + ")";
+        }
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeTypeCache.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java?rev=373516&r1=373515&r2=373516&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java Mon Jan 30 06:07:41 2006
@@ -20,8 +20,8 @@
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.fs.FileSystemException;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
-import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.core.util.Dumpable;
+import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.name.QName;
 import org.apache.log4j.Logger;
 
@@ -44,7 +44,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
-import java.util.TreeSet;
 
 /**
  * A <code>NodeTypeRegistry</code> ...
@@ -206,7 +205,7 @@
         // FIXME need a fake declaring node type:
         // rep:root is not quite correct but better than a non-existing node type 
         def.setDeclaringNodeType(QName.REP_ROOT);
-        def.setRequiredPrimaryTypes(new QName[] { QName.REP_ROOT });
+        def.setRequiredPrimaryTypes(new QName[]{QName.REP_ROOT});
         def.setDefaultPrimaryType(QName.REP_ROOT);
         def.setMandatory(true);
         def.setProtected(false);
@@ -216,56 +215,6 @@
         return def;
     }
 
-    /**
-     * Validates and registers the specified collection of <code>NodeTypeDef</code>
-     * objects. An <code>InvalidNodeTypeDefException</code> is thrown if the
-     * validation of any of the contained <code>NodeTypeDef</code> objects fails.
-     * <p/>
-     * Note that in the case an exception is thrown some node types might have
-     * been nevertheless successfully registered.
-     *
-     * @param ntDefs collection of <code>NodeTypeDef</code> objects
-     * @throws InvalidNodeTypeDefException
-     * @throws RepositoryException
-     * @see #registerNodeType
-     */
-    private synchronized void internalRegister(Collection ntDefs)
-            throws InvalidNodeTypeDefException, RepositoryException {
-        ArrayList list = new ArrayList(ntDefs);
-
-        // iterate over definitions until there are no more definitions with
-        // unresolved (i.e. unregistered) dependencies or an error occurs;
-
-        int count = -1;  // number of registered nt's per iteration
-        while (list.size() > 0 && count != 0) {
-            count = 0;
-            Iterator iterator = list.iterator();
-            while (iterator.hasNext()) {
-                NodeTypeDef ntd = (NodeTypeDef) iterator.next();
-                // check if definition has unresolved dependencies
-                if (registeredNTDefs.keySet().containsAll(ntd.getDependencies())) {
-                    // try to register it
-                    internalRegister(ntd);
-                    // remove it from list
-                    iterator.remove();
-                    // increase count
-                    count++;
-                }
-            }
-        }
-        if (list.size() > 0) {
-            StringBuffer msg = new StringBuffer();
-            msg.append("the following node types could not be registered because of unresolvable dependencies: ");
-            Iterator iterator = list.iterator();
-            while (iterator.hasNext()) {
-                msg.append(((NodeTypeDef) iterator.next()).getName());
-                msg.append(" ");
-            }
-            log.error(msg.toString());
-            throw new InvalidNodeTypeDefException(msg.toString());
-        }
-    }
-
     private EffectiveNodeType internalRegister(NodeTypeDef ntd)
             throws InvalidNodeTypeDefException, RepositoryException {
         QName name = ntd.getName();
@@ -275,7 +224,7 @@
             throw new InvalidNodeTypeDefException(msg);
         }
 
-        EffectiveNodeType ent = validateNodeTypeDef(ntd);
+        EffectiveNodeType ent = validateNodeTypeDef(ntd, entCache, registeredNTDefs);
 
         // store new effective node type instance
         entCache.put(ent);
@@ -326,7 +275,8 @@
         }
         iter = keys.iterator();
         while (iter.hasNext()) {
-            WeightedKey k = (WeightedKey) iter.next();
+            EffectiveNodeTypeCache.WeightedKey k =
+                    (EffectiveNodeTypeCache.WeightedKey) iter.next();
             EffectiveNodeType ent = entCache.get(k);
             if (ent.includesNodeType(name)) {
                 entCache.remove(k);
@@ -425,6 +375,7 @@
     /**
      * Utility method for verifying that the namespace of a <code>QName</code>
      * is registered; a <code>null</code> argument is silently ignored.
+     *
      * @param name name whose namespace is to be checked
      * @throws RepositoryException if the namespace of the given name is not
      *                             registered or if an unspecified error occured
@@ -436,7 +387,18 @@
         }
     }
 
-    private EffectiveNodeType validateNodeTypeDef(NodeTypeDef ntd)
+    /**
+     * Validates the specified NodeTypeDef within the context of the two other parameter and
+     * returns an EffectiveNodeType.
+     *
+     * @param ntd
+     * @param anEntCache
+     * @param aRegisteredNTDefCache
+     * @return The EffectiveNodeType
+     * @throws InvalidNodeTypeDefException
+     * @throws RepositoryException
+     */
+    private EffectiveNodeType validateNodeTypeDef(NodeTypeDef ntd, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache)
             throws InvalidNodeTypeDefException, RepositoryException {
 
         /**
@@ -472,7 +434,7 @@
                     log.debug(msg);
                     throw new InvalidNodeTypeDefException(msg);
                 }
-                if (!registeredNTDefs.containsKey(supertypes[i])) {
+                if (!aRegisteredNTDefCache.containsKey(supertypes[i])) {
                     String msg = "[" + name + "] invalid supertype: "
                             + supertypes[i];
                     log.debug(msg);
@@ -486,7 +448,7 @@
              */
             Stack inheritanceChain = new Stack();
             inheritanceChain.push(name);
-            checkForCircularInheritance(supertypes, inheritanceChain);
+            checkForCircularInheritance(supertypes, inheritanceChain, aRegisteredNTDefCache);
         }
 
         /**
@@ -502,12 +464,12 @@
          */
         if (supertypes != null && supertypes.length > 0) {
             try {
-                EffectiveNodeType est = getEffectiveNodeType(supertypes);
+                EffectiveNodeType est = getEffectiveNodeType(supertypes, anEntCache, aRegisteredNTDefCache);
                 // make sure that all primary types except nt:base extend from nt:base
                 if (!ntd.isMixin() && !QName.NT_BASE.equals(ntd.getName())
                         && !est.includesNodeType(QName.NT_BASE)) {
                     String msg = "[" + name + "] all primary node types except"
-                        + " nt:base itself must be (directly or indirectly) derived from nt:base";
+                            + " nt:base itself must be (directly or indirectly) derived from nt:base";
                     log.debug(msg);
                     throw new InvalidNodeTypeDefException(msg);
                 }
@@ -633,7 +595,7 @@
                     for (int j = 0; j < constraints.length; j++) {
                         ReferenceConstraint rc = (ReferenceConstraint) constraints[j];
                         QName ntName = rc.getNodeTypeName();
-                        if (!name.equals(ntName) && !registeredNTDefs.containsKey(ntName)) {
+                        if (!name.equals(ntName) && !aRegisteredNTDefCache.containsKey(ntName)) {
                             String msg = "[" + name + "#" + pd.getName()
                                     + "] invalid REFERENCE value constraint '"
                                     + ntName + "' (unknown node type)";
@@ -689,7 +651,7 @@
                  * the default primary type must be registered, with one notable
                  * exception: the node type just being registered
                  */
-                if (!name.equals(dpt) && !registeredNTDefs.containsKey(dpt)) {
+                if (!name.equals(dpt) && !aRegisteredNTDefCache.containsKey(dpt)) {
                     String msg = "[" + name + "#" + cnd.getName()
                             + "] invalid default primary type '" + dpt + "'";
                     log.debug(msg);
@@ -701,14 +663,14 @@
                  */
                 try {
                     if (!referenceToSelf) {
-                        defaultENT = getEffectiveNodeType(dpt);
+                        defaultENT = getEffectiveNodeType(dpt, anEntCache, aRegisteredNTDefCache);
                     } else {
                         /**
                          * the default primary type is identical with the node
                          * type just being registered; we have to instantiate it
                          * 'manually'
                          */
-                        ent = EffectiveNodeType.create(this, ntd);
+                        ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache);
                         defaultENT = ent;
                     }
                     if (cnd.isAutoCreated()) {
@@ -719,7 +681,7 @@
                          */
                         Stack definingNTs = new Stack();
                         definingNTs.push(name);
-                        checkForCircularNodeAutoCreation(defaultENT, definingNTs);
+                        checkForCircularNodeAutoCreation(defaultENT, definingNTs, anEntCache, aRegisteredNTDefCache);
                     }
                 } catch (NodeTypeConflictException ntce) {
                     String msg = "[" + name + "#" + cnd.getName()
@@ -752,7 +714,7 @@
                      * the required primary type must be registered, with one
                      * notable exception: the node type just being registered
                      */
-                    if (!name.equals(rpt) && !registeredNTDefs.containsKey(rpt)) {
+                    if (!name.equals(rpt) && !aRegisteredNTDefCache.containsKey(rpt)) {
                         String msg = "[" + name + "#" + cnd.getName()
                                 + "] invalid required primary type: " + rpt;
                         log.debug(msg);
@@ -775,7 +737,7 @@
                      */
                     try {
                         if (!referenceToSelf) {
-                            getEffectiveNodeType(rpt);
+                            getEffectiveNodeType(rpt, anEntCache, aRegisteredNTDefCache);
                         } else {
                             /**
                              * the required primary type is identical with the
@@ -783,7 +745,7 @@
                              * instantiate it 'manually'
                              */
                             if (ent == null) {
-                                ent = EffectiveNodeType.create(this, ntd);
+                                ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache);
                             }
                         }
                     } catch (NodeTypeConflictException ntce) {
@@ -808,7 +770,7 @@
          */
         if (ent == null) {
             try {
-                ent = EffectiveNodeType.create(this, ntd);
+                ent = EffectiveNodeType.create(this, ntd, anEntCache, aRegisteredNTDefCache);
             } catch (NodeTypeConflictException ntce) {
                 String msg = "[" + name + "] failed to resolve node type definition";
                 log.debug(msg);
@@ -823,45 +785,40 @@
     }
 
     /**
-     * Returns the names of all registered node types. That includes primary
-     * and mixin node types.
-     *
-     * @return the names of all registered node types.
-     */
-    public synchronized QName[] getRegisteredNodeTypes() {
-        return (QName[]) registeredNTDefs.keySet().toArray(new QName[registeredNTDefs.size()]);
-    }
-
-    /**
-     * @return
-     */
-    public NodeDef getRootNodeDef() {
-        return rootNodeDef;
-    }
-
-    /**
      * @param ntName
+     * @param anEntCache
+     * @param aRegisteredNTDefCache
      * @return
      * @throws NoSuchNodeTypeException
      */
-    public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName)
+    public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache)
             throws NoSuchNodeTypeException {
-        // 1. make sure that the specified node type is registered
-        if (!registeredNTDefs.containsKey(ntName)) {
+        // 1. make sure that the specified node type exists
+        if (!aRegisteredNTDefCache.containsKey(ntName)) {
             throw new NoSuchNodeTypeException(ntName.toString());
         }
 
         // 2. check if effective node type has already been built
-        WeightedKey key = new WeightedKey(new QName[]{ntName});
-        if (entCache.contains(key)) {
-            return entCache.get(key);
+        EffectiveNodeType ent = anEntCache.get(new QName[]{ntName});
+        if (ent != null) {
+            return ent;
         }
 
         // 3. build effective node type
         try {
-            EffectiveNodeType ent = EffectiveNodeType.create(this, ntName);
+            NodeTypeDef def = (NodeTypeDef) aRegisteredNTDefCache.get(ntName);
+            NodeTypeDef ntDef4ENT;
+            // return clone to make sure nobody messes around with the 'live' definition
+            try {
+                ntDef4ENT = (NodeTypeDef) def.clone();
+            } catch (CloneNotSupportedException e) {
+                // should never get here
+                log.fatal("internal error", e);
+                throw new InternalError(e.getMessage());
+            }
+            ent = EffectiveNodeType.create(this, ntDef4ENT, anEntCache, aRegisteredNTDefCache);
             // store new effective node type
-            entCache.put(ent);
+            anEntCache.put(ent);
             return ent;
         } catch (NodeTypeConflictException ntce) {
             // should never get here as all registered node types have to be valid!
@@ -873,24 +830,27 @@
 
     /**
      * @param ntNames
+     * @param anEntCache
+     * @param aRegisteredNTDefCache
      * @return
      * @throws NodeTypeConflictException
      * @throws NoSuchNodeTypeException
      */
-    public synchronized EffectiveNodeType getEffectiveNodeType(QName[] ntNames)
+    public synchronized EffectiveNodeType getEffectiveNodeType(QName[] ntNames, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache)
             throws NodeTypeConflictException, NoSuchNodeTypeException {
-        // 1. make sure every single node type is registered
+        // 1. make sure every single node type exists
         for (int i = 0; i < ntNames.length; i++) {
-            if (!registeredNTDefs.containsKey(ntNames[i])) {
+            if (!aRegisteredNTDefCache.containsKey(ntNames[i])) {
                 throw new NoSuchNodeTypeException(ntNames[i].toString());
             }
         }
 
-        WeightedKey key = new WeightedKey(ntNames);
+        EffectiveNodeTypeCache.WeightedKey key =
+                new EffectiveNodeTypeCache.WeightedKey(ntNames);
 
         // 2. check if aggregate has already been built
-        if (entCache.contains(key)) {
-            return entCache.get(key);
+        if (anEntCache.contains(key)) {
+            return anEntCache.get(key);
         }
 
         // 3. build aggregate
@@ -900,8 +860,8 @@
         ArrayList tmpResults = new ArrayList();
         while (key.size() > 0) {
             // check if we've already built this aggregate
-            if (entCache.contains(key)) {
-                tmpResults.add(entCache.get(key));
+            if (anEntCache.contains(key)) {
+                tmpResults.add(anEntCache.get(key));
                 // subtract the result from the temporary key
                 // (which is 'empty' now)
                 key = key.subtract(key);
@@ -912,15 +872,16 @@
              * aggregate (i.e. the cost of building it)
              */
             boolean foundSubResult = false;
-            Iterator iter = entCache.keys();
+            Iterator iter = anEntCache.keys();
             while (iter.hasNext()) {
-                WeightedKey k = (WeightedKey) iter.next();
+                EffectiveNodeTypeCache.WeightedKey k =
+                        (EffectiveNodeTypeCache.WeightedKey) iter.next();
                 /**
                  * check if the existing aggregate is a 'subset' of the one
                  * we're looking for
                  */
                 if (key.contains(k)) {
-                    tmpResults.add(entCache.get(k));
+                    tmpResults.add(anEntCache.get(k));
                     // subtract the result from the temporary key
                     key = key.subtract(k);
                     foundSubResult = true;
@@ -934,16 +895,27 @@
                  */
                 QName[] remainder = key.toArray();
                 for (int i = 0; i < remainder.length; i++) {
+                    NodeTypeDef def = (NodeTypeDef) aRegisteredNTDefCache.get(remainder[i]);
+                    NodeTypeDef clonedDef;
+                    // return clone to make sure nobody messes around with the 'live' definition
+                    try {
+                        clonedDef = (NodeTypeDef) def.clone();
+                    } catch (CloneNotSupportedException e) {
+                        // should never get here
+                        log.fatal("internal error", e);
+                        throw new InternalError(e.getMessage());
+                    }
+
                     EffectiveNodeType ent =
-                            EffectiveNodeType.create(this, remainder[i]);
+                            EffectiveNodeType.create(this, clonedDef, anEntCache, aRegisteredNTDefCache);
                     // store new effective node type
-                    entCache.put(ent);
+                    anEntCache.put(ent);
                     if (result == null) {
                         result = ent;
                     } else {
                         result = result.merge(ent);
                         // store intermediate result (sub-aggregate)
-                        entCache.put(result);
+                        anEntCache.put(result);
                     }
                 }
                 // add aggregate of remaining node types to result list
@@ -958,14 +930,52 @@
             } else {
                 result = result.merge((EffectiveNodeType) tmpResults.get(i));
                 // store intermediate result
-                entCache.put(result);
+                anEntCache.put(result);
             }
         }
         // we're done
         return result;
     }
 
-    void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain)
+    /**
+     * Returns the names of all registered node types. That includes primary
+     * and mixin node types.
+     *
+     * @return the names of all registered node types.
+     */
+    public synchronized QName[] getRegisteredNodeTypes() {
+        return (QName[]) registeredNTDefs.keySet().toArray(new QName[registeredNTDefs.size()]);
+    }
+
+    /**
+     * @return
+     */
+    public NodeDef getRootNodeDef() {
+        return rootNodeDef;
+    }
+
+    /**
+     * @param ntName
+     * @return
+     * @throws NoSuchNodeTypeException
+     */
+    public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName)
+            throws NoSuchNodeTypeException {
+        return getEffectiveNodeType(ntName, entCache, registeredNTDefs);
+    }
+
+    /**
+     * @param ntNames
+     * @return
+     * @throws NodeTypeConflictException
+     * @throws NoSuchNodeTypeException
+     */
+    public synchronized EffectiveNodeType getEffectiveNodeType(QName[] ntNames)
+            throws NodeTypeConflictException, NoSuchNodeTypeException {
+        return getEffectiveNodeType(ntNames, entCache, registeredNTDefs);
+    }
+
+    void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain, Map aRegisteredNTDefCache)
             throws InvalidNodeTypeDefException, RepositoryException {
         for (int i = 0; i < supertypes.length; i++) {
             QName nt = supertypes[i];
@@ -985,11 +995,13 @@
             }
 
             try {
-                QName[] sta = getNodeTypeDef(nt).getSupertypes();
+
+                NodeTypeDef ntd = (NodeTypeDef) aRegisteredNTDefCache.get(nt);
+                QName[] sta = ntd.getSupertypes();
                 if (sta != null && sta.length > 0) {
                     // check recursively
                     inheritanceChain.push(nt);
-                    checkForCircularInheritance(sta, inheritanceChain);
+                    checkForCircularInheritance(sta, inheritanceChain, aRegisteredNTDefCache);
                     inheritanceChain.pop();
                 }
             } catch (NoSuchNodeTypeException nsnte) {
@@ -1001,7 +1013,7 @@
     }
 
     void checkForCircularNodeAutoCreation(EffectiveNodeType childNodeENT,
-                                          Stack definingParentNTs)
+                                          Stack definingParentNTs, EffectiveNodeTypeCache anEntCache, Map aRegisteredNTDefCache)
             throws InvalidNodeTypeDefException {
         // check for circularity through default node types of auto-created child nodes
         // (node type 'a' defines auto-created child node with default node type 'a')
@@ -1035,8 +1047,8 @@
                 if (dnt != null) {
                     // check recursively
                     definingParentNTs.push(definingNT);
-                    checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt),
-                            definingParentNTs);
+                    checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt, anEntCache, aRegisteredNTDefCache),
+                            definingParentNTs, anEntCache, aRegisteredNTDefCache);
                     definingParentNTs.pop();
                 }
             } catch (NoSuchNodeTypeException nsnte) {
@@ -1110,22 +1122,18 @@
      * Note that in the case an exception is thrown, some node types might have
      * been nevertheless successfully registered.
      *
-     * @param ntDefs a collection of <code>NodeTypeDef<code>s
+     * @param newNTDefs a collection of <code>NodeTypeDef<code>s
      * @throws InvalidNodeTypeDefException
      * @throws RepositoryException
      */
-    public synchronized void registerNodeTypes(Collection ntDefs)
+    public synchronized void registerNodeTypes(Collection newNTDefs)
             throws InvalidNodeTypeDefException, RepositoryException {
         // exceptions that might be thrown by internalRegister(Collection)
         RepositoryException re = null;
         InvalidNodeTypeDefException intde = null;
 
-        // store names of currently registered node types before proceeding
-        HashSet oldNTNames = new HashSet(registeredNTDefs.keySet());
-
         try {
-            // validate and register new node type definitions
-            internalRegister(ntDefs);
+            internalRegister(newNTDefs);
         } catch (RepositoryException e) {
             // store exception so it can be re-thrown later on
             re = e;
@@ -1133,35 +1141,107 @@
             // store exception so it can be re-thrown later on
             intde = e;
         }
-
-        /**
-         * build set of names of actually registered new node types
-         * (potentially a subset of those specified in ntDefs if an exception
-         * had been thrown)
-         */
-        HashSet newNTNames = new HashSet(registeredNTDefs.keySet());
-        newNTNames.removeAll(oldNTNames);
-
-        if (newNTNames.size() > 0) {
-            // persist new node type definitions
-            for (Iterator iter = newNTNames.iterator(); iter.hasNext();) {
-                QName ntName = (QName) iter.next();
-                customNTDefs.add((NodeTypeDef) registeredNTDefs.get(ntName));
+        boolean allNodeTypeDefsAreValid = re == null && intde == null;
+        if (allNodeTypeDefsAreValid) {
+            Iterator validNTDsIterator = newNTDefs.iterator();
+            while (validNTDsIterator.hasNext()) {
+                NodeTypeDef ntd = (NodeTypeDef) validNTDsIterator.next();
+                // store property & child node definitions of new node type by id
+                customNTDefs.add(ntd);
             }
             persistCustomNodeTypeDefs(customNTDefs);
-
             // notify listeners
-            for (Iterator iter = newNTNames.iterator(); iter.hasNext();) {
-                QName ntName = (QName) iter.next();
-                notifyRegistered(ntName);
+            for (Iterator iter = newNTDefs.iterator(); iter.hasNext();) {
+                NodeTypeDef ntDef = (NodeTypeDef) iter.next();
+                notifyRegistered(ntDef.getName());
+            }
+        } else {
+            // re-throw the exception
+            if (re != null) {
+                throw re;
+            } else if (intde != null) {
+                throw intde;
             }
         }
+    }
 
-        // re-throw exception as necessary
-        if (re != null) {
-            throw re;
-        } else if (intde != null) {
-            throw intde;
+    /**
+     * Validates and registers the specified collection of <code>NodeTypeDef</code>
+     * objects. An <code>InvalidNodeTypeDefException</code> is thrown if the
+     * validation of any of the contained <code>NodeTypeDef</code> objects fails.
+     * <p/>
+     * Note that in the case an exception is thrown no node type will be
+     * registered.
+     *
+     * @param newNTDefs collection of <code>NodeTypeDef</code> objects
+     * @throws InvalidNodeTypeDefException
+     * @throws RepositoryException
+     * @see #registerNodeType
+     */
+    private synchronized void internalRegister(Collection newNTDefs)
+            throws InvalidNodeTypeDefException, RepositoryException {
+
+        // cache of pre-built aggregations of node types
+        EffectiveNodeTypeCache anEntCache = new EffectiveNodeTypeCache(entCache);
+        
+        // map of node type names and node type definitions
+        Map aRegisteredNTDefCache = new HashMap(registeredNTDefs);
+        
+        // temporarily register a clone of the node type definition
+        // and do some checks by the way
+        Iterator ntdNameIterator = newNTDefs.iterator();
+        while (ntdNameIterator.hasNext()) {
+            Object ntdObject = ntdNameIterator.next();
+            // check if the right type is used
+            if (!(ntdObject instanceof NodeTypeDef)) {
+                String msg = "The specified object is not of type "
+                        + NodeTypeDef.class.getName();
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            } else {
+                // check if the ntd is new
+                NodeTypeDef ntd = (NodeTypeDef) ntdObject;
+                QName name = ntd.getName();
+                if (name != null && registeredNTDefs.containsKey(name)) {
+                    String msg = name + " already exists";
+                    log.debug(msg);
+                    throw new InvalidNodeTypeDefException(msg);
+                }
+                // clone the ntd and add it to the cache
+                NodeTypeDef clonedNTD;
+                try {
+                    clonedNTD = (NodeTypeDef) ntd.clone();
+                } catch (CloneNotSupportedException e) {
+                    // should never get here
+                    log.fatal("internal error", e);
+                    throw new InternalError(e.getMessage());
+                }
+                aRegisteredNTDefCache.put(clonedNTD.getName(), clonedNTD);
+            }
+        }
+        Iterator ntdIterator = newNTDefs.iterator();
+        while (ntdIterator.hasNext()) {
+            NodeTypeDef ntd = (NodeTypeDef) ntdIterator.next();
+
+            EffectiveNodeType ent = validateNodeTypeDef(ntd, anEntCache, aRegisteredNTDefCache);
+
+            // store new effective node type instance
+            anEntCache.put(ent);
+        }
+        // as no exception occured at this point, the ntds are valid
+        Iterator validNTDsIterator = newNTDefs.iterator();
+        while (validNTDsIterator.hasNext()) {
+            NodeTypeDef ntd = (NodeTypeDef) validNTDsIterator.next();
+            registeredNTDefs.put(ntd.getName(), ntd);
+            // store property & child node definitions of new node type by id
+            PropDef[] pda = ntd.getPropertyDefs();
+            for (int i = 0; i < pda.length; i++) {
+                propDefs.put(pda[i].getId(), pda[i]);
+            }
+            NodeDef[] nda = ntd.getChildNodeDefs();
+            for (int i = 0; i < nda.length; i++) {
+                nodeDefs.put(nda[i].getId(), nda[i]);
+            }
         }
     }
 
@@ -1232,7 +1312,7 @@
         /**
          * validate new node type definition
          */
-        validateNodeTypeDef(ntd);
+        validateNodeTypeDef(ntd, this.entCache, this.registeredNTDefs);
 
         /**
          * build diff of current and new definition and determine type of change
@@ -1320,7 +1400,7 @@
      *
      * @param nodeTypeName name of node type whose definition should be returned.
      * @return the node type definition of the node type with the given name.
-     * @throws NoSuchNodeTypeException if a node type with the given name 
+     * @throws NoSuchNodeTypeException if a node type with the given name
      *                                 does not exist
      */
     public synchronized NodeTypeDef getNodeTypeDef(QName nodeTypeName)
@@ -1659,268 +1739,5 @@
     protected void checkForReferencesInContent(QName nodeTypeName)
             throws RepositoryException {
         throw new RepositoryException("not yet implemented");
-    }
-
-    //--------------------------------------------------------< inner classes >
-    /**
-     * A <code>WeightedKey</code> uniquely identifies
-     * a combination (i.e. an aggregation) of one or more node types.
-     * The weight is an indicator for the cost involved in building such an
-     * aggregate (e.g. an aggregation of multiple complex node types with deep
-     * inheritance trees is more costly to build/validate than an agreggation
-     * of two very simple node types with just one property definition each).
-     * <p/>
-     * A very simple (and not very accurate) approximation of the weight would
-     * be the number of explicitly aggregated node types (ignoring inheritance
-     * and complexity of each involved node type). A better approximation would
-     * be the number of <b>all</b>, explicitly and implicitly (note that
-     * inheritance is also an aggregation) aggregated node types.
-     * <p/>
-     * The more accurate the weight definition, the more efficient is the
-     * the building of new aggregates.
-     * <p/>
-     * It is important to note that the weight is not part of the key value,
-     * i.e. it is not considered by the <code>hashCode()</code> and
-     * <code>equals(Object)</code> methods. It does however affect the order
-     * of <code>WeightedKey</code> instances. See
-     * <code>{@link #compareTo(Object)}</code> for more information.
-     * <p/>
-     * Let's assume we have an aggregation of node types named "b", "a" and "c".
-     * Its key would be "[a, b, c]" and the weight 3 (using the simple
-     * approximation).
-     */
-    static class WeightedKey implements Comparable {
-        /**
-         * set of node type names, sorted in ascending order
-         */
-        private final TreeSet set;
-        private final int weight;
-
-        /**
-         * @param ntNames
-         */
-        WeightedKey(QName[] ntNames) {
-            this(ntNames, ntNames.length);
-        }
-
-        /**
-         * @param ntNames
-         * @param weight
-         */
-        WeightedKey(QName[] ntNames, int weight) {
-            this.weight = weight;
-
-            set = new TreeSet();
-            for (int i = 0; i < ntNames.length; i++) {
-                // add name to this sorted set
-                set.add(ntNames[i]);
-            }
-        }
-
-        /**
-         * @param ntNames
-         */
-        WeightedKey(Collection ntNames) {
-            this(ntNames, ntNames.size());
-        }
-
-        /**
-         * @param ntNames
-         * @param weight
-         */
-        WeightedKey(Collection ntNames, int weight) {
-            this.weight = weight;
-            set = new TreeSet(ntNames);
-        }
-
-        /**
-         * The key is the string representation of this sorted set
-         * (e.g. the key for a set containing entries "c", "b" and "a" would
-         * be "[a, b, c]").
-         *
-         * @return string representation of this sorted set
-         * @see java.util.AbstractCollection#toString
-         */
-        String getKey() {
-            return set.toString();
-        }
-
-        /**
-         * @return
-         */
-        int getWeight() {
-            return weight;
-        }
-
-        int size() {
-            return set.size();
-        }
-
-        Iterator iterator() {
-            return Collections.unmodifiableSortedSet(set).iterator();
-        }
-
-        Set getSet() {
-            return Collections.unmodifiableSortedSet(set);
-        }
-
-        QName[] toArray() {
-            return (QName[]) set.toArray(new QName[set.size()]);
-        }
-
-        boolean contains(WeightedKey otherKey) {
-            return set.containsAll(otherKey.getSet());
-        }
-
-        WeightedKey subtract(WeightedKey otherKey) {
-            Set tmp = (Set) set.clone();
-            tmp.removeAll(otherKey.getSet());
-            return new WeightedKey(tmp);
-
-        }
-
-        /**
-         * The resulting sort-order is: 1. descending weight, 2. ascending key
-         * (i.e. string representation of this sorted set).
-         *
-         * @param o
-         * @return
-         */
-        public int compareTo(Object o) {
-            WeightedKey other = (WeightedKey) o;
-            if (getWeight() > other.getWeight()) {
-                return -1;
-            } else if (getWeight() < other.getWeight()) {
-                return 1;
-            }
-            return getKey().compareTo(other.getKey());
-        }
-
-        public int hashCode() {
-            int h = 17;
-            // ignore weight
-            Iterator i = set.iterator();
-            while (i.hasNext()) {
-                h *= 37;
-                Object obj = i.next();
-                if (obj != null) {
-                    h += obj.hashCode();
-                }
-            }
-            return h;
-        }
-
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj instanceof WeightedKey) {
-                WeightedKey other = (WeightedKey) obj;
-                // ignore weight
-                return set.equals(other.set);
-            }
-            return false;
-        }
-
-        public String toString() {
-            return set.toString() + " (" + weight + ")";
-        }
-    }
-
-    /**
-     * <code>EfectiveNodeTypeCache</code> ...
-     */
-    private class EffectiveNodeTypeCache {
-        // ordered set of keys
-        final TreeSet sortedKeys;
-        // cache of pre-build aggregations of node types
-        final HashMap aggregates;
-
-        EffectiveNodeTypeCache() {
-            sortedKeys = new TreeSet();
-            aggregates = new HashMap();
-        }
-
-        void put(EffectiveNodeType ent) {
-            // we define the weight as the total number of included node types
-            // (through aggregation and inheritance)
-            int weight = ent.getAllNodeTypes().length;
-            // the effective node type is identified by the list of merged
-            // (i.e. aggregated) node types
-            WeightedKey k = new WeightedKey(ent.getMergedNodeTypes(), weight);
-            aggregates.put(k, ent);
-            sortedKeys.add(k);
-        }
-
-        boolean contains(QName[] ntNames) {
-            return aggregates.containsKey(new WeightedKey(ntNames));
-        }
-
-        boolean contains(WeightedKey key) {
-            return aggregates.containsKey(key);
-        }
-
-        EffectiveNodeType get(QName[] ntNames) {
-            return (EffectiveNodeType) aggregates.get(new WeightedKey(ntNames));
-        }
-
-        EffectiveNodeType get(WeightedKey key) {
-            return (EffectiveNodeType) aggregates.get(key);
-        }
-
-        EffectiveNodeType remove(QName[] ntNames) {
-            return remove(new WeightedKey(ntNames));
-        }
-
-        EffectiveNodeType remove(WeightedKey key) {
-            EffectiveNodeType removed = (EffectiveNodeType) aggregates.remove(key);
-            if (removed != null) {
-                // remove index entry
-
-                // FIXME: can't simply call TreeSet.remove(key) because the entry
-                // in sortedKeys might have a different weight and would thus
-                // not be found
-                Iterator iter = sortedKeys.iterator();
-                while (iter.hasNext()) {
-                    WeightedKey k = (WeightedKey) iter.next();
-                    // WeightedKey.equals(Object) ignores the weight
-                    if (key.equals(k)) {
-                        sortedKeys.remove(k);
-                        break;
-                    }
-                }
-            }
-            return removed;
-        }
-
-        /**
-         * Returns an iterator over the keys. The order of the returned keys is:
-         * <ul>
-         * <li>1. descending weight</li>
-         * <li>2. ascending key (i.e. unique identifier of aggregate)</li>
-         * </ul>
-         *
-         * @see NodeTypeRegistry.WeightedKey#compareTo
-         */
-        Iterator keys() {
-            return sortedKeys.iterator();
-        }
-
-        //---------------------------------------------------------< Dumpable >
-        /**
-         * {@inheritDoc}
-         */
-        public void dump(PrintStream ps) {
-            ps.println("EffectiveNodeTypeCache (" + this + ")");
-            ps.println();
-            ps.println("EffectiveNodeTypes in cache:");
-            ps.println();
-            Iterator iter = sortedKeys.iterator();
-            while (iter.hasNext()) {
-                WeightedKey k = (WeightedKey) iter.next();
-                //EffectiveNodeType ent = (EffectiveNodeType) aggregates.get(k);
-                ps.println(k);
-            }
-        }
     }
 }

Added: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java?rev=373516&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java Mon Jan 30 06:07:41 2006
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.core.nodetype;
+
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeTypeManager;
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * @test
+ * @sources CyclicNodeTypeRegistrationTest
+ * @executeClass org.apache.jackrabbit.test.api.nodetype.CyclicNodeTypeRegistrationTest
+ * @keywords level1
+ */
+public class CyclicNodeTypeRegistrationTest extends AbstractJCRTest {
+    /**
+     * The session we use for the tests
+     */
+    private Session session;
+
+    /**
+     * The node type manager we use for the tests
+     */
+    private NodeTypeManager manager;
+
+    /**
+     * The node type registry we use for the tests
+     */
+    private NodeTypeRegistry ntreg;
+
+    /**
+     * The cyclic dependent node type definitions we use for the tests
+     */
+    private Collection ntDefCollection;
+
+    /**
+     * Sets up the fixture for the test cases.
+     */
+    protected void setUp() throws Exception {
+        //isReadOnly = true;
+        super.setUp();
+
+        session = helper.getReadOnlySession();
+        manager = session.getWorkspace().getNodeTypeManager();
+        
+        // Get the NodeTypeManager from the Workspace.
+        // Note that it must be cast from the generic JCR NodeTypeManager to the
+        // Jackrabbit-specific implementation.
+        NodeTypeManagerImpl ntmgr = (NodeTypeManagerImpl) session.getWorkspace().getNodeTypeManager();
+
+        // Acquire the NodeTypeRegistry
+        ntreg = ntmgr.getNodeTypeRegistry();
+
+
+    }
+
+    /**
+     * Releases the session aquired in {@link #setUp()}.
+     */
+    protected void tearDown() throws Exception {
+        if (session != null) {
+            session.logout();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Tests, if it is possible to register node types with simple
+     * cyclic dependencies.
+     */
+    public void testRegisterCyclicChildNodeTypes() {
+        /**
+         * Constructs node types with the following structure:
+         * [foo]
+         * + myBarInFoo (bar)
+         * 
+         * [bar]
+         * + myFooInBar (foo) 
+         */
+        final NodeTypeDef foo = new NodeTypeDef();
+        foo.setName(new QName("", "foo"));
+        foo.setSupertypes(new QName[]{QName.NT_BASE});
+
+        final NodeTypeDef bar = new NodeTypeDef();
+        bar.setName(new QName("", "bar"));
+        bar.setSupertypes(new QName[]{QName.NT_BASE});
+
+        NodeDefImpl myBarInFoo = new NodeDefImpl();
+        myBarInFoo.setRequiredPrimaryTypes(new QName[]{bar.getName()});
+        myBarInFoo.setName(new QName("", "myBarInFoo"));
+        myBarInFoo.setDeclaringNodeType(foo.getName());
+
+        NodeDefImpl myFooInBar = new NodeDefImpl();
+        myFooInBar.setRequiredPrimaryTypes(new QName[]{foo.getName()});
+        myFooInBar.setName(new QName("", "myFooInBar"));
+        myFooInBar.setDeclaringNodeType(bar.getName());
+
+        foo.setChildNodeDefs(new NodeDefImpl[]{myBarInFoo});
+        bar.setChildNodeDefs(new NodeDefImpl[]{myFooInBar});
+        ntDefCollection = new LinkedList();
+        ntDefCollection.add(foo);
+        ntDefCollection.add(bar);
+
+        try {
+            ntreg.registerNodeTypes(ntDefCollection);
+        } catch (InvalidNodeTypeDefException e) {
+            assertFalse(e.getMessage(), true);
+            e.printStackTrace();
+        } catch (RepositoryException e) {
+            assertFalse(e.getMessage(), true);
+            e.printStackTrace();
+        }
+        boolean allNTsAreRegistered = ntreg.isRegistered(foo.getName()) && ntreg.isRegistered(bar.getName());
+        assertTrue(allNTsAreRegistered);
+
+    }
+
+    /**
+     * A simple check, if a missing node type is found
+     */
+    public void testRegisterSimpleMissingNodeTypes() {
+        /**
+         * Constructs node types with the following structure:
+         * [foo]
+         * + myNTInFoo (I_am_an_invalid_required_primary_type)
+         * 
+         */
+        final NodeTypeDef foo = new NodeTypeDef();
+        foo.setName(new QName("", "foo"));
+        foo.setSupertypes(new QName[]{QName.NT_BASE});
+
+
+        NodeDefImpl myBarInFoo = new NodeDefImpl();
+        myBarInFoo.setRequiredPrimaryTypes(new QName[]{new QName("", "I_am_an_invalid_required_primary_type")});
+        myBarInFoo.setName(new QName("", "myNTInFoo"));
+        myBarInFoo.setDeclaringNodeType(foo.getName());
+
+        foo.setChildNodeDefs(new NodeDefImpl[]{myBarInFoo});
+        ntDefCollection = new LinkedList();
+        ntDefCollection.add(foo);
+
+        try {
+            ntreg.registerNodeTypes(ntDefCollection);
+            assertFalse("Missing node type not found", true);
+        } catch (InvalidNodeTypeDefException e) {
+            assertTrue(true);
+
+        } catch (RepositoryException e) {
+            assertFalse("Wrong Exception thrown on missing node type.", true);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Basically a test of a Graffito use case.
+     */
+    public void testRegisterCyclicChildNodeTypesAndSupertypes() {
+        /**
+         * Constructs node types with the following structure:
+         * [Folder] > CmsObject
+         * + folders (Folder)
+         * + documents (Document)
+         * 
+         * [CmsObject]
+         * + parentFolder (Folder)
+         * 
+         * [Document] > CmsObject
+         * - size (long)
+         * 
+         */
+
+        final NodeTypeDef folder = new NodeTypeDef();
+        folder.setName(new QName("", "Folder"));
+
+        final NodeTypeDef cmsObject = new NodeTypeDef();
+        cmsObject.setName(new QName("", "CmsObject"));
+        cmsObject.setSupertypes(new QName[]{QName.NT_BASE});
+        NodeDefImpl parentFolder = new NodeDefImpl();
+        parentFolder.setRequiredPrimaryTypes(new QName[]{folder.getName()});
+        parentFolder.setName(new QName("", "parentFolder"));
+        parentFolder.setDeclaringNodeType(cmsObject.getName());
+        cmsObject.setChildNodeDefs(new NodeDefImpl[]{parentFolder});
+
+
+        final NodeTypeDef document = new NodeTypeDef();
+        document.setName(new QName("", "Document"));
+        document.setSupertypes(new QName[]{cmsObject.getName()});
+        PropDefImpl sizeProp = new PropDefImpl();
+        sizeProp.setName(new QName("", "size"));
+        sizeProp.setRequiredType(PropertyType.LONG);
+        sizeProp.setDeclaringNodeType(document.getName());
+        document.setPropertyDefs(new PropDef[]{sizeProp});
+
+
+        folder.setSupertypes(new QName[]{cmsObject.getName()});
+
+        NodeDefImpl folders = new NodeDefImpl();
+        folders.setRequiredPrimaryTypes(new QName[]{folder.getName()});
+        folders.setName(new QName("", "folders"));
+        folders.setDeclaringNodeType(folder.getName());
+
+        NodeDefImpl documents = new NodeDefImpl();
+        documents.setRequiredPrimaryTypes(new QName[]{document.getName()});
+        documents.setName(new QName("", "documents"));
+        documents.setDeclaringNodeType(folder.getName());
+
+        folder.setChildNodeDefs(new NodeDefImpl[]{folders, documents});
+        ntDefCollection = new LinkedList();
+        ntDefCollection.add(folder);
+        ntDefCollection.add(document);
+        ntDefCollection.add(cmsObject);
+
+        try {
+            ntreg.registerNodeTypes(ntDefCollection);
+        } catch (InvalidNodeTypeDefException e) {
+            assertFalse(e.getMessage(), true);
+            e.printStackTrace();
+        } catch (RepositoryException e) {
+            assertFalse(e.getMessage(), true);
+            e.printStackTrace();
+        }
+        boolean allNTsAreRegistered = ntreg.isRegistered(folder.getName()) && ntreg.isRegistered(document.getName()) && ntreg.isRegistered(cmsObject.getName());
+        assertTrue(allNTsAreRegistered);
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/nodetype/CyclicNodeTypeRegistrationTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url