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 2004/12/02 18:28:54 UTC

svn commit: r109509 - /incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java

Author: stefan
Date: Thu Dec  2 09:28:52 2004
New Revision: 109509

URL: http://svn.apache.org/viewcvs?view=rev&rev=109509
Log:
cleaning up nodetyperegistry code
Modified:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java?view=diff&rev=109509&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java&r1=109508&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java&r2=109509
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java	Thu Dec  2 09:28:52 2004
@@ -31,8 +31,8 @@
 import javax.jcr.version.OnParentVersionAction;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PrintStream;
 import java.io.OutputStream;
+import java.io.PrintStream;
 import java.util.*;
 
 /**
@@ -44,43 +44,43 @@
     // some well known node type names
     // nt:unstructured
     public static final QName NT_UNSTRUCTURED =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "unstructured");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "unstructured");
     // nt:base
     public static final QName NT_BASE =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "base");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "base");
     // nt:hierarchyNode
     public static final QName NT_HIERARCHYNODE =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "hierarchyNode");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "hierarchyNode");
     // nt:mimeResource
     public static final QName NT_MIME_RESOURCE =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "mimeResource");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "mimeResource");
     // nt:query
     public static final QName NT_QUERY =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "query");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "query");
     // mix:referenceable
     public static final QName MIX_REFERENCEABLE =
-            new QName(NamespaceRegistryImpl.NS_MIX_URI, "referenceable");
+	    new QName(NamespaceRegistryImpl.NS_MIX_URI, "referenceable");
     // mix:lockable
     public static final QName MIX_LOCKABLE =
-            new QName(NamespaceRegistryImpl.NS_MIX_URI, "lockable");
+	    new QName(NamespaceRegistryImpl.NS_MIX_URI, "lockable");
     // mix:versionable
     public static final QName MIX_VERSIONABLE =
-            new QName(NamespaceRegistryImpl.NS_MIX_URI, "versionable");
+	    new QName(NamespaceRegistryImpl.NS_MIX_URI, "versionable");
     // nt:versionHistory
     public static final QName NT_VERSION_HISTORY =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "versionHistory");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "versionHistory");
     // nt:version
     public static final QName NT_VERSION =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "version");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "version");
     // nt:frozenVersionableChild
     public static final QName NT_FROZEN_VERSIONABLE_CHILD =
-            new QName(NamespaceRegistryImpl.NS_NT_URI, "frozenVersionableChild");
+	    new QName(NamespaceRegistryImpl.NS_NT_URI, "frozenVersionableChild");
     // jcr:primaryType
     public static final QName JCR_PRIMARY_TYPE =
-            new QName(NamespaceRegistryImpl.NS_JCR_URI, "primaryType");
+	    new QName(NamespaceRegistryImpl.NS_JCR_URI, "primaryType");
 
     private static final String BUILTIN_NODETYPES_RESOURCE_PATH =
-            "org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml";
+	    "org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml";
 
     private static final String CUSTOM_NODETYPES_RESOURCE_NAME = "custom_nodetypes.xml";
 
@@ -119,7 +119,7 @@
      * Listeners (soft references)
      */
     private final Map listeners =
-            Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
+	    Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
 
     /**
      * Create a new <code>NodeTypeRegistry</codes>
@@ -130,9 +130,9 @@
      * @throws RepositoryException
      */
     public static NodeTypeRegistry create(NamespaceRegistryImpl nsReg, FileSystem ntStore)
-            throws RepositoryException {
-        NodeTypeRegistry ntMgr = new NodeTypeRegistry(nsReg, ntStore);
-        return ntMgr;
+	    throws RepositoryException {
+	NodeTypeRegistry ntMgr = new NodeTypeRegistry(nsReg, ntStore);
+	return ntMgr;
     }
 
     /**
@@ -143,105 +143,105 @@
      * @throws RepositoryException
      */
     private NodeTypeRegistry(NamespaceRegistryImpl nsReg, FileSystem ntStore)
-            throws RepositoryException {
-        this.nsReg = nsReg;
-        this.ntStore = ntStore;
-        customNodeTypesResource = new FileSystemResource(this.ntStore, CUSTOM_NODETYPES_RESOURCE_NAME);
-        try {
-            // make sure path to resource exists
-            if (!customNodeTypesResource.exists()) {
-                customNodeTypesResource.makeParentDirs();
-            }
-        } catch (FileSystemException fse) {
-            String error = "internal error: invalid resource: " + customNodeTypesResource.getPath();
-            log.error(error, fse);
-            throw new RepositoryException(error, fse);
-        }
-
-        entCache = new EffectiveNodeTypeCache();
-        registeredNTDefs = new HashMap();
-        propDefs = new HashMap();
-        nodeDefs = new HashMap();
-
-        // setup definition of root node
-        rootNodeDef = createRootNodeDef();
-        nodeDefs.put(new NodeDefId(rootNodeDef), rootNodeDef);
-
-        // load and register pre-defined (i.e. built-in) node types
-        builtInNTDefs = new NodeTypeDefStore();
-        InputStream in = null;
-        try {
-            in = getClass().getClassLoader().getResourceAsStream(BUILTIN_NODETYPES_RESOURCE_PATH);
-            builtInNTDefs.load(in);
-            internalRegister(builtInNTDefs.all());
-        } catch (IOException ioe) {
-            String error = "internal error: failed to read built-in node type definitions stored in " + BUILTIN_NODETYPES_RESOURCE_PATH;
-            log.error(error, ioe);
-            throw new RepositoryException(error, ioe);
-        } catch (InvalidNodeTypeDefException intde) {
-            String error = "internal error: invalid built-in node type definition stored in " + BUILTIN_NODETYPES_RESOURCE_PATH;
-            log.error(error, intde);
-            throw new RepositoryException(error, intde);
-        } finally {
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (IOException ioe) {
-                    // ignore
-                }
-            }
-        }
-
-        // load and register custom node types
-        customNTDefs = new NodeTypeDefStore();
-        in = null;
-        try {
-            if (customNodeTypesResource.exists()) {
-                in = customNodeTypesResource.getInputStream();
-            }
-        } catch (FileSystemException fse) {
-            String error = "internal error: failed to access custom node type definitions stored in " + customNodeTypesResource.getPath();
-            log.error(error, fse);
-            throw new RepositoryException(error, fse);
-        }
-        if (in == null) {
-            log.info("no custom node type definitions found");
-        } else {
-            try {
-                customNTDefs.load(in);
-                internalRegister(customNTDefs.all());
-            } catch (IOException ioe) {
-                String error = "internal error: failed to read custom node type definitions stored in " + customNodeTypesResource.getPath();
-                log.error(error, ioe);
-                throw new RepositoryException(error, ioe);
-            } catch (InvalidNodeTypeDefException intde) {
-                String error = "internal error: invalid custom node type definition stored in " + customNodeTypesResource.getPath();
-                log.error(error, intde);
-                throw new RepositoryException(error, intde);
-            } finally {
-                try {
-                    in.close();
-                } catch (IOException ioe) {
-                    // ignore
-                }
-            }
-        }
+	    throws RepositoryException {
+	this.nsReg = nsReg;
+	this.ntStore = ntStore;
+	customNodeTypesResource = new FileSystemResource(this.ntStore, CUSTOM_NODETYPES_RESOURCE_NAME);
+	try {
+	    // make sure path to resource exists
+	    if (!customNodeTypesResource.exists()) {
+		customNodeTypesResource.makeParentDirs();
+	    }
+	} catch (FileSystemException fse) {
+	    String error = "internal error: invalid resource: " + customNodeTypesResource.getPath();
+	    log.error(error, fse);
+	    throw new RepositoryException(error, fse);
+	}
+
+	entCache = new EffectiveNodeTypeCache();
+	registeredNTDefs = new HashMap();
+	propDefs = new HashMap();
+	nodeDefs = new HashMap();
+
+	// setup definition of root node
+	rootNodeDef = createRootNodeDef();
+	nodeDefs.put(new NodeDefId(rootNodeDef), rootNodeDef);
+
+	// load and register pre-defined (i.e. built-in) node types
+	builtInNTDefs = new NodeTypeDefStore();
+	InputStream in = null;
+	try {
+	    in = getClass().getClassLoader().getResourceAsStream(BUILTIN_NODETYPES_RESOURCE_PATH);
+	    builtInNTDefs.load(in);
+	    internalRegister(builtInNTDefs.all());
+	} catch (IOException ioe) {
+	    String error = "internal error: failed to read built-in node type definitions stored in " + BUILTIN_NODETYPES_RESOURCE_PATH;
+	    log.error(error, ioe);
+	    throw new RepositoryException(error, ioe);
+	} catch (InvalidNodeTypeDefException intde) {
+	    String error = "internal error: invalid built-in node type definition stored in " + BUILTIN_NODETYPES_RESOURCE_PATH;
+	    log.error(error, intde);
+	    throw new RepositoryException(error, intde);
+	} finally {
+	    if (in != null) {
+		try {
+		    in.close();
+		} catch (IOException ioe) {
+		    // ignore
+		}
+	    }
+	}
+
+	// load and register custom node types
+	customNTDefs = new NodeTypeDefStore();
+	in = null;
+	try {
+	    if (customNodeTypesResource.exists()) {
+		in = customNodeTypesResource.getInputStream();
+	    }
+	} catch (FileSystemException fse) {
+	    String error = "internal error: failed to access custom node type definitions stored in " + customNodeTypesResource.getPath();
+	    log.error(error, fse);
+	    throw new RepositoryException(error, fse);
+	}
+	if (in == null) {
+	    log.info("no custom node type definitions found");
+	} else {
+	    try {
+		customNTDefs.load(in);
+		internalRegister(customNTDefs.all());
+	    } catch (IOException ioe) {
+		String error = "internal error: failed to read custom node type definitions stored in " + customNodeTypesResource.getPath();
+		log.error(error, ioe);
+		throw new RepositoryException(error, ioe);
+	    } catch (InvalidNodeTypeDefException intde) {
+		String error = "internal error: invalid custom node type definition stored in " + customNodeTypesResource.getPath();
+		log.error(error, intde);
+		throw new RepositoryException(error, intde);
+	    } finally {
+		try {
+		    in.close();
+		} catch (IOException ioe) {
+		    // ignore
+		}
+	    }
+	}
     }
 
     private static ChildNodeDef createRootNodeDef() {
-        ChildNodeDef def = new ChildNodeDef();
+	ChildNodeDef def = new ChildNodeDef();
 
-        // FIXME need a fake declaring node type
-        def.setDeclaringNodeType(new QName(NamespaceRegistryImpl.NS_DEFAULT_URI, ""));
-        def.setRequiredPrimaryTypes(new QName[]{NT_BASE});
-        def.setDefaultPrimaryType(NT_UNSTRUCTURED);
-        def.setMandatory(true);
-        def.setProtected(false);
-        def.setOnParentVersion(OnParentVersionAction.VERSION);
-        def.setPrimaryItem(false);
-        def.setAllowSameNameSibs(false);
-        def.setAutoCreate(true);
-        return def;
+	// FIXME need a fake declaring node type
+	def.setDeclaringNodeType(new QName(NamespaceRegistryImpl.NS_DEFAULT_URI, ""));
+	def.setRequiredPrimaryTypes(new QName[]{NT_BASE});
+	def.setDefaultPrimaryType(NT_UNSTRUCTURED);
+	def.setMandatory(true);
+	def.setProtected(false);
+	def.setOnParentVersion(OnParentVersionAction.VERSION);
+	def.setPrimaryItem(false);
+	def.setAllowSameNameSibs(false);
+	def.setAutoCreate(true);
+	return def;
     }
 
     /**
@@ -258,81 +258,81 @@
      * @see #registerNodeType
      */
     private synchronized void internalRegister(Collection ntDefs)
-            throws InvalidNodeTypeDefException, RepositoryException {
-        ArrayList list = new ArrayList(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;
+	// 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());
-        }
+	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();
-        if (name != null && registeredNTDefs.containsKey(name)) {
-            String msg = name + " already exists";
-            log.error(msg);
-            throw new InvalidNodeTypeDefException(msg);
-        }
-
-        EffectiveNodeType ent = validateNodeTypeDef(ntd);
-
-        // store new effective node type instance
-        entCache.put(ent);
-
-        // register clone of node type definition
-        try {
-            ntd = (NodeTypeDef) ntd.clone();
-        } catch (CloneNotSupportedException e) {
-            // should never get here
-            log.fatal("internal error", e);
-            throw new InternalError(e.getMessage());
-        }
-        registeredNTDefs.put(name, ntd);
-
-        // store poperty & child node definitions of new node type by id
-        PropDef[] pda = ntd.getPropertyDefs();
-        for (int i = 0; i < pda.length; i++) {
-            PropDef def = pda[i];
-            PropDefId id = new PropDefId(def);
-            propDefs.put(id, def);
-        }
-        ChildNodeDef[] nda = ntd.getChildNodeDefs();
-        for (int i = 0; i < nda.length; i++) {
-            ChildNodeDef def = nda[i];
-            NodeDefId id = new NodeDefId(def);
-            nodeDefs.put(id, def);
-        }
+	    throws InvalidNodeTypeDefException, RepositoryException {
+	QName name = ntd.getName();
+	if (name != null && registeredNTDefs.containsKey(name)) {
+	    String msg = name + " already exists";
+	    log.error(msg);
+	    throw new InvalidNodeTypeDefException(msg);
+	}
+
+	EffectiveNodeType ent = validateNodeTypeDef(ntd);
+
+	// store new effective node type instance
+	entCache.put(ent);
+
+	// register clone of node type definition
+	try {
+	    ntd = (NodeTypeDef) ntd.clone();
+	} catch (CloneNotSupportedException e) {
+	    // should never get here
+	    log.fatal("internal error", e);
+	    throw new InternalError(e.getMessage());
+	}
+	registeredNTDefs.put(name, ntd);
+
+	// store poperty & child node definitions of new node type by id
+	PropDef[] pda = ntd.getPropertyDefs();
+	for (int i = 0; i < pda.length; i++) {
+	    PropDef def = pda[i];
+	    PropDefId id = new PropDefId(def);
+	    propDefs.put(id, def);
+	}
+	ChildNodeDef[] nda = ntd.getChildNodeDefs();
+	for (int i = 0; i < nda.length; i++) {
+	    ChildNodeDef def = nda[i];
+	    NodeDefId id = new NodeDefId(def);
+	    nodeDefs.put(id, def);
+	}
 
-        return ent;
+	return ent;
     }
 
     /**
@@ -342,9 +342,9 @@
      *                 of node types
      */
     void addListener(NodeTypeRegistryListener listener) {
-        if (!listeners.containsKey(listener)) {
-            listeners.put(listener, listener);
-        }
+	if (!listeners.containsKey(listener)) {
+	    listeners.put(listener, listener);
+	}
     }
 
     /**
@@ -353,415 +353,415 @@
      * @param listener an existing listener
      */
     void removeListener(NodeTypeRegistryListener listener) {
-        listeners.remove(listener);
+	listeners.remove(listener);
     }
 
     /**
      * Notify the listeners that a node type <code>ntName</code> has been registered.
      */
     private void notifyRegistered(QName ntName) {
-        // copy listeners to array to avoid ConcurrentModificationException
-        NodeTypeRegistryListener[] la = new NodeTypeRegistryListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (NodeTypeRegistryListener) iter.next();
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].nodeTypeRegistered(ntName);
-            }
-        }
+	// copy listeners to array to avoid ConcurrentModificationException
+	NodeTypeRegistryListener[] la = new NodeTypeRegistryListener[listeners.size()];
+	Iterator iter = listeners.values().iterator();
+	int cnt = 0;
+	while (iter.hasNext()) {
+	    la[cnt++] = (NodeTypeRegistryListener) iter.next();
+	}
+	for (int i = 0; i < la.length; i++) {
+	    if (la[i] != null) {
+		la[i].nodeTypeRegistered(ntName);
+	    }
+	}
     }
 
     /**
      * Notify the listeners that a node type <code>ntName</code> has been unregistered.
      */
     private void notifyUnregistered(QName ntName) {
-        // copy listeners to array to avoid ConcurrentModificationException
-        NodeTypeRegistryListener[] la = new NodeTypeRegistryListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (NodeTypeRegistryListener) iter.next();
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].nodeTypeUnregistered(ntName);
-            }
-        }
+	// copy listeners to array to avoid ConcurrentModificationException
+	NodeTypeRegistryListener[] la = new NodeTypeRegistryListener[listeners.size()];
+	Iterator iter = listeners.values().iterator();
+	int cnt = 0;
+	while (iter.hasNext()) {
+	    la[cnt++] = (NodeTypeRegistryListener) iter.next();
+	}
+	for (int i = 0; i < la.length; i++) {
+	    if (la[i] != null) {
+		la[i].nodeTypeUnregistered(ntName);
+	    }
+	}
     }
 
     private EffectiveNodeType validateNodeTypeDef(NodeTypeDef ntd)
-            throws InvalidNodeTypeDefException, RepositoryException {
+	    throws InvalidNodeTypeDefException, RepositoryException {
 
-        /**
-         * the effective (i.e. merged and resolved) node type resulting from
-         * the specified node type definition;
-         * the effective node type will finally be created after the definition
-         * has been verified and checked for conflicts etc.; in some cases it
-         * will be created already at an earlier stage during the validation
-         * of child node definitions
-         */
-        EffectiveNodeType ent = null;
-
-        QName name = ntd.getName();
-        if (name == null) {
-            String msg = "no name specified";
-            log.error(msg);
-            throw new InvalidNodeTypeDefException(msg);
-        }
-
-        if (registeredNTDefs.containsKey(name)) {
-            String msg = name + " already exists";
-            log.error(msg);
-            throw new InvalidNodeTypeDefException(msg);
-        }
-
-        // validate supertypes
-        QName[] supertypes = ntd.getSupertypes();
-        if (supertypes != null && supertypes.length > 0) {
-            for (int i = 0; i < supertypes.length; i++) {
-                /**
-                 * simple check for infinite recursion
-                 * (won't trap recursion on a deeper inheritance level)
-                 */
-                if (name.equals(supertypes[i])) {
-                    String msg = "invalid supertype: " + supertypes[i] + " (infinite recursion))";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-                if (!registeredNTDefs.containsKey(supertypes[i])) {
-                    String msg = "invalid supertype: " + supertypes[i];
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-            }
-
-            /**
-             * check for circularity in inheritance chain
-             * ('a' extends 'b' extends 'a')
-             */
-            Stack inheritanceChain = new Stack();
-            inheritanceChain.push(name);
-            checkForCircularInheritance(supertypes, inheritanceChain);
-        }
-
-        /**
-         * note that infinite recursion through inheritance is automatically
-         * being checked by the following call to getEffectiveNodeType()
-         * as it's impossible to register an node type definition which
-         * references a supertype that isn't registered yet...
-         */
-
-        /**
-         * build effective (i.e. merged and resolved) node type from supertypes
-         * and check for conflicts
-         */
-        if (supertypes != null && supertypes.length > 0) {
-            try {
-                EffectiveNodeType est = buildEffectiveNodeType(supertypes);
-                // make sure that all primary types except nt:base extend from nt:base
-                if (!ntd.isMixin() && !NT_BASE.equals(ntd.getName()) &&
-                        !est.includesNodeType(NT_BASE)) {
-                    String msg = "all primary node types except nt:base itself must be (directly or indirectly) derived from nt:base";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-            } catch (NodeTypeConflictException ntce) {
-                String msg = "failed to validate supertypes";
-                log.error(msg, ntce);
-                throw new InvalidNodeTypeDefException(msg, ntce);
-            } catch (NoSuchNodeTypeException nsnte) {
-                String msg = "failed to validate supertypes";
-                log.error(msg, nsnte);
-                throw new InvalidNodeTypeDefException(msg, nsnte);
-            }
-        } else {
-            // no supertypes specified: has to be either a mixin type or nt:base
-            if (!ntd.isMixin() && !NT_BASE.equals(ntd.getName())) {
-                String msg = "all primary node types except nt:base itself must be (directly or indirectly) derived from nt:base";
-                log.error(msg);
-                throw new InvalidNodeTypeDefException(msg);
-            }
-        }
-
-        ChildItemDef primaryItem = null;
-
-        // validate property definitions
-        PropDef[] pda = ntd.getPropertyDefs();
-        for (int i = 0; i < pda.length; i++) {
-            PropDef pd = pda[i];
-            // check primary item flag
-            if (pd.isPrimaryItem()) {
-                if (pd.definesResidual()) {
-                    String msg = "primary item must specify a name";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-                if (primaryItem != null) {
-                    String msg = "more than one primary item specified";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                } else {
-                    primaryItem = pd;
-                }
-            }
-            // check that auto-created properties specify a name
-            if (pd.definesResidual() && pd.isAutoCreate()) {
-                String msg = "auto-created properties must specify a name";
-                log.error(msg);
-                throw new InvalidNodeTypeDefException(msg);
-            }
-            /**
-             * check default values:
-             * make sure type of value is consistent with required property type
-             */
-            InternalValue[] defVals = pd.getDefaultValues();
-            if (defVals != null && defVals.length != 0) {
-                int reqType = pd.getRequiredType();
-                for (int j = 0; j < defVals.length; j++) {
-                    if (reqType == PropertyType.UNDEFINED) {
-                        reqType = defVals[j].getType();
-                    } else {
-                        if (defVals[j].getType() != reqType) {
-                            String msg = "type of default value(s) is not consistent with required property type";
-                            log.error(msg);
-                            throw new InvalidNodeTypeDefException(msg);
-                        }
-                    }
-                }
-            }
-            /**
-             * todo check that auto-created properties have have at least either default values or system generated values
-             */
-            // check that default values satisfy value constraints
-            ValueConstraint[] constraints = pd.getValueConstraints();
-            if (constraints != null && constraints.length > 0) {
-                if (defVals != null && defVals.length > 0) {
-                    // check value constraints on every value
-                    for (int j = 0; j < defVals.length; j++) {
-                        // constraints are OR-ed together
-                        boolean satisfied = false;
-                        ConstraintViolationException cve = null;
-                        for (int k = 0; k < constraints.length; k++) {
-                            try {
-                                constraints[k].check(defVals[j]);
-                                // at least one constraint is satisfied
-                                satisfied = true;
-                                break;
-                            } catch (ConstraintViolationException e) {
-                                cve = e;
-                                continue;
-                            }
-                        }
-                        if (!satisfied) {
-                            // report last exception we encountered
-                            String msg = "default value of property "
-                                    + (pd.definesResidual() ? "*" : pd.getName().toString())
-                                    + " does not satisfy value constraint";
-                            log.error(msg, cve);
-                            throw new InvalidNodeTypeDefException(msg, cve);
-                        }
-                    }
-                }
-
-                /**
-                 * ReferenceConstraint:
-                 * the specified node type must be registered, with one notable
-                 * exception: the node type just being registered
-                 */
-                if (pd.getRequiredType() == PropertyType.REFERENCE) {
-                    for (int j = 0; j < constraints.length; j++) {
-                        ReferenceConstraint rc = (ReferenceConstraint) constraints[j];
-                        QName ntName = rc.getNodeTypeName();
-                        if (!name.equals(ntName) && !registeredNTDefs.containsKey(ntName)) {
-                            String msg = "invalid REFERENCE value constraint '"
-                                    + ntName + "' (unknown node type) in property definition "
-                                    + (pd.definesResidual() ? "*" : pd.getName().toString());
-                            log.error(msg);
-                            throw new InvalidNodeTypeDefException(msg);
-                        }
-                    }
-                }
-            }
-        }
-
-        // validate child-node definitions
-        ChildNodeDef[] cnda = ntd.getChildNodeDefs();
-        for (int i = 0; i < cnda.length; i++) {
-            ChildNodeDef cnd = cnda[i];
-            // check primary item flag
-            if (cnd.isPrimaryItem()) {
-                if (cnd.definesResidual()) {
-                    String msg = "primary item must specify a name";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-                if (primaryItem != null) {
-                    String msg = "more than one primary item specified";
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                } else {
-                    primaryItem = cnd;
-                }
-            }
-            // check that auto-created child-nodes specify a name
-            if (cnd.definesResidual() && cnd.isAutoCreate()) {
-                String msg = "auto-created child-nodes must specify a name";
-                log.error(msg);
-                throw new InvalidNodeTypeDefException(msg);
-            }
-            // check default primary type
-            QName dpt = cnd.getDefaultPrimaryType();
-            boolean referenceToSelf = false;
-            EffectiveNodeType defaultENT = null;
-            if (dpt != null) {
-                // check if this node type specifies itself as default primary type
-                if (name.equals(dpt)) {
-                    referenceToSelf = true;
-                }
-                /**
-                 * the default primary type must be registered, with one notable
-                 * exception: the node type just being registered
-                 */
-                if (!name.equals(dpt) && !registeredNTDefs.containsKey(dpt)) {
-                    String msg = "invalid default primary type '" + dpt
-                            + "' in childnode definition " + cnd.getName();
-                    log.error(msg);
-                    throw new InvalidNodeTypeDefException(msg);
-                }
-                /**
-                 * build effective (i.e. merged and resolved) node type from
-                 * default primary type and check for conflicts
-                 */
-                try {
-                    if (!referenceToSelf) {
-                        defaultENT = getEffectiveNodeType(dpt);
-                    } 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);
-                        defaultENT = ent;
-                    }
-                    if (cnd.isAutoCreate()) {
-                        /**
-                         * check for circularity through default primary types
-                         * of auto-created child nodes (node type 'a' defines
-                         * auto-created child node with default primary type 'a')
-                         */
-                        Stack definingNTs = new Stack();
-                        definingNTs.push(name);
-                        checkForCircularNodeAutoCreation(defaultENT, definingNTs);
-                    }
-                } catch (NodeTypeConflictException ntce) {
-                    String msg = "failed to validate default primary type";
-                    log.error(msg, ntce);
-                    throw new InvalidNodeTypeDefException(msg, ntce);
-                } catch (NoSuchNodeTypeException nsnte) {
-                    String msg = "failed to validate default primary type";
-                    log.error(msg, nsnte);
-                    throw new InvalidNodeTypeDefException(msg, nsnte);
-                }
-            }
-
-            // check required primary types
-            QName[] reqTypes = cnd.getRequiredPrimaryTypes();
-            if (reqTypes != null && reqTypes.length > 0) {
-                for (int n = 0; n < reqTypes.length; n++) {
-                    QName rpt = reqTypes[n];
-                    referenceToSelf = false;
-                    /**
-                     * check if this node type specifies itself as required
-                     * primary type
-                     */
-                    if (name.equals(rpt)) {
-                        referenceToSelf = true;
-                    }
-                    /**
-                     * the required primary type must be registered, with one
-                     * notable exception: the node type just being registered
-                     */
-                    if (!name.equals(rpt) && !registeredNTDefs.containsKey(rpt)) {
-                        String msg = "invalid required primary type: " + rpt;
-                        log.error(msg);
-                        throw new InvalidNodeTypeDefException(msg);
-                    }
-                    /**
-                     * check if default primary type satisfies the required
-                     * primary type constraint
-                     */
-                    if (defaultENT != null && !defaultENT.includesNodeType(rpt)) {
-                        String msg = "default primary type does not satisfy required primary type constraint " + rpt;
-                        log.error(msg);
-                        throw new InvalidNodeTypeDefException(msg);
-                    }
-                    /**
-                     * build effective (i.e. merged and resolved) node type from
-                     * required primary type constraint and check for conflicts
-                     */
-                    try {
-                        if (!referenceToSelf) {
-                            getEffectiveNodeType(rpt);
-                        } else {
-                            /**
-                             * the required primary type is identical with the
-                             * node type just being registered; we have to
-                             * instantiate it 'manually'
-                             */
-                            if (ent == null) {
-                                ent = EffectiveNodeType.create(this, ntd);
-                            }
-                        }
-                    } catch (NodeTypeConflictException ntce) {
-                        String msg = "failed to validate required primary type constraint";
-                        log.error(msg, ntce);
-                        throw new InvalidNodeTypeDefException(msg, ntce);
-                    } catch (NoSuchNodeTypeException nsnte) {
-                        String msg = "failed to validate required primary type constraint";
-                        log.error(msg, nsnte);
-                        throw new InvalidNodeTypeDefException(msg, nsnte);
-                    }
-                }
-            }
-        }
-
-        /**
-         * now build effective (i.e. merged and resolved) node type from
-         * this node type definition; this will potentially detect more
-         * conflicts or problems
-         */
-        if (ent == null) {
-            try {
-                ent = EffectiveNodeType.create(this, ntd);
-            } catch (NodeTypeConflictException ntce) {
-                String msg = "failed to resolve node type definition";
-                log.error(msg, ntce);
-                throw new InvalidNodeTypeDefException(msg, ntce);
-            } catch (NoSuchNodeTypeException nsnte) {
-                String msg = "failed to resolve node type definition";
-                log.error(msg, nsnte);
-                throw new InvalidNodeTypeDefException(msg, nsnte);
-            }
-        }
-        return ent;
+	/**
+	 * the effective (i.e. merged and resolved) node type resulting from
+	 * the specified node type definition;
+	 * the effective node type will finally be created after the definition
+	 * has been verified and checked for conflicts etc.; in some cases it
+	 * will be created already at an earlier stage during the validation
+	 * of child node definitions
+	 */
+	EffectiveNodeType ent = null;
+
+	QName name = ntd.getName();
+	if (name == null) {
+	    String msg = "no name specified";
+	    log.error(msg);
+	    throw new InvalidNodeTypeDefException(msg);
+	}
+
+	if (registeredNTDefs.containsKey(name)) {
+	    String msg = name + " already exists";
+	    log.error(msg);
+	    throw new InvalidNodeTypeDefException(msg);
+	}
+
+	// validate supertypes
+	QName[] supertypes = ntd.getSupertypes();
+	if (supertypes != null && supertypes.length > 0) {
+	    for (int i = 0; i < supertypes.length; i++) {
+		/**
+		 * simple check for infinite recursion
+		 * (won't trap recursion on a deeper inheritance level)
+		 */
+		if (name.equals(supertypes[i])) {
+		    String msg = "invalid supertype: " + supertypes[i] + " (infinite recursion))";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+		if (!registeredNTDefs.containsKey(supertypes[i])) {
+		    String msg = "invalid supertype: " + supertypes[i];
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+	    }
+
+	    /**
+	     * check for circularity in inheritance chain
+	     * ('a' extends 'b' extends 'a')
+	     */
+	    Stack inheritanceChain = new Stack();
+	    inheritanceChain.push(name);
+	    checkForCircularInheritance(supertypes, inheritanceChain);
+	}
+
+	/**
+	 * note that infinite recursion through inheritance is automatically
+	 * being checked by the following call to getEffectiveNodeType()
+	 * as it's impossible to register an node type definition which
+	 * references a supertype that isn't registered yet...
+	 */
+
+	/**
+	 * build effective (i.e. merged and resolved) node type from supertypes
+	 * and check for conflicts
+	 */
+	if (supertypes != null && supertypes.length > 0) {
+	    try {
+		EffectiveNodeType est = buildEffectiveNodeType(supertypes);
+		// make sure that all primary types except nt:base extend from nt:base
+		if (!ntd.isMixin() && !NT_BASE.equals(ntd.getName()) &&
+			!est.includesNodeType(NT_BASE)) {
+		    String msg = "all primary node types except nt:base itself must be (directly or indirectly) derived from nt:base";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+	    } catch (NodeTypeConflictException ntce) {
+		String msg = "failed to validate supertypes";
+		log.error(msg, ntce);
+		throw new InvalidNodeTypeDefException(msg, ntce);
+	    } catch (NoSuchNodeTypeException nsnte) {
+		String msg = "failed to validate supertypes";
+		log.error(msg, nsnte);
+		throw new InvalidNodeTypeDefException(msg, nsnte);
+	    }
+	} else {
+	    // no supertypes specified: has to be either a mixin type or nt:base
+	    if (!ntd.isMixin() && !NT_BASE.equals(ntd.getName())) {
+		String msg = "all primary node types except nt:base itself must be (directly or indirectly) derived from nt:base";
+		log.error(msg);
+		throw new InvalidNodeTypeDefException(msg);
+	    }
+	}
+
+	ChildItemDef primaryItem = null;
+
+	// validate property definitions
+	PropDef[] pda = ntd.getPropertyDefs();
+	for (int i = 0; i < pda.length; i++) {
+	    PropDef pd = pda[i];
+	    // check primary item flag
+	    if (pd.isPrimaryItem()) {
+		if (pd.definesResidual()) {
+		    String msg = "primary item must specify a name";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+		if (primaryItem != null) {
+		    String msg = "more than one primary item specified";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		} else {
+		    primaryItem = pd;
+		}
+	    }
+	    // check that auto-created properties specify a name
+	    if (pd.definesResidual() && pd.isAutoCreate()) {
+		String msg = "auto-created properties must specify a name";
+		log.error(msg);
+		throw new InvalidNodeTypeDefException(msg);
+	    }
+	    /**
+	     * check default values:
+	     * make sure type of value is consistent with required property type
+	     */
+	    InternalValue[] defVals = pd.getDefaultValues();
+	    if (defVals != null && defVals.length != 0) {
+		int reqType = pd.getRequiredType();
+		for (int j = 0; j < defVals.length; j++) {
+		    if (reqType == PropertyType.UNDEFINED) {
+			reqType = defVals[j].getType();
+		    } else {
+			if (defVals[j].getType() != reqType) {
+			    String msg = "type of default value(s) is not consistent with required property type";
+			    log.error(msg);
+			    throw new InvalidNodeTypeDefException(msg);
+			}
+		    }
+		}
+	    }
+	    /**
+	     * todo check that auto-created properties have have at least either default values or system generated values
+	     */
+	    // check that default values satisfy value constraints
+	    ValueConstraint[] constraints = pd.getValueConstraints();
+	    if (constraints != null && constraints.length > 0) {
+		if (defVals != null && defVals.length > 0) {
+		    // check value constraints on every value
+		    for (int j = 0; j < defVals.length; j++) {
+			// constraints are OR-ed together
+			boolean satisfied = false;
+			ConstraintViolationException cve = null;
+			for (int k = 0; k < constraints.length; k++) {
+			    try {
+				constraints[k].check(defVals[j]);
+				// at least one constraint is satisfied
+				satisfied = true;
+				break;
+			    } catch (ConstraintViolationException e) {
+				cve = e;
+				continue;
+			    }
+			}
+			if (!satisfied) {
+			    // report last exception we encountered
+			    String msg = "default value of property "
+				    + (pd.definesResidual() ? "*" : pd.getName().toString())
+				    + " does not satisfy value constraint";
+			    log.error(msg, cve);
+			    throw new InvalidNodeTypeDefException(msg, cve);
+			}
+		    }
+		}
+
+		/**
+		 * ReferenceConstraint:
+		 * the specified node type must be registered, with one notable
+		 * exception: the node type just being registered
+		 */
+		if (pd.getRequiredType() == PropertyType.REFERENCE) {
+		    for (int j = 0; j < constraints.length; j++) {
+			ReferenceConstraint rc = (ReferenceConstraint) constraints[j];
+			QName ntName = rc.getNodeTypeName();
+			if (!name.equals(ntName) && !registeredNTDefs.containsKey(ntName)) {
+			    String msg = "invalid REFERENCE value constraint '"
+				    + ntName + "' (unknown node type) in property definition "
+				    + (pd.definesResidual() ? "*" : pd.getName().toString());
+			    log.error(msg);
+			    throw new InvalidNodeTypeDefException(msg);
+			}
+		    }
+		}
+	    }
+	}
+
+	// validate child-node definitions
+	ChildNodeDef[] cnda = ntd.getChildNodeDefs();
+	for (int i = 0; i < cnda.length; i++) {
+	    ChildNodeDef cnd = cnda[i];
+	    // check primary item flag
+	    if (cnd.isPrimaryItem()) {
+		if (cnd.definesResidual()) {
+		    String msg = "primary item must specify a name";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+		if (primaryItem != null) {
+		    String msg = "more than one primary item specified";
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		} else {
+		    primaryItem = cnd;
+		}
+	    }
+	    // check that auto-created child-nodes specify a name
+	    if (cnd.definesResidual() && cnd.isAutoCreate()) {
+		String msg = "auto-created child-nodes must specify a name";
+		log.error(msg);
+		throw new InvalidNodeTypeDefException(msg);
+	    }
+	    // check default primary type
+	    QName dpt = cnd.getDefaultPrimaryType();
+	    boolean referenceToSelf = false;
+	    EffectiveNodeType defaultENT = null;
+	    if (dpt != null) {
+		// check if this node type specifies itself as default primary type
+		if (name.equals(dpt)) {
+		    referenceToSelf = true;
+		}
+		/**
+		 * the default primary type must be registered, with one notable
+		 * exception: the node type just being registered
+		 */
+		if (!name.equals(dpt) && !registeredNTDefs.containsKey(dpt)) {
+		    String msg = "invalid default primary type '" + dpt
+			    + "' in childnode definition " + cnd.getName();
+		    log.error(msg);
+		    throw new InvalidNodeTypeDefException(msg);
+		}
+		/**
+		 * build effective (i.e. merged and resolved) node type from
+		 * default primary type and check for conflicts
+		 */
+		try {
+		    if (!referenceToSelf) {
+			defaultENT = getEffectiveNodeType(dpt);
+		    } 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);
+			defaultENT = ent;
+		    }
+		    if (cnd.isAutoCreate()) {
+			/**
+			 * check for circularity through default primary types
+			 * of auto-created child nodes (node type 'a' defines
+			 * auto-created child node with default primary type 'a')
+			 */
+			Stack definingNTs = new Stack();
+			definingNTs.push(name);
+			checkForCircularNodeAutoCreation(defaultENT, definingNTs);
+		    }
+		} catch (NodeTypeConflictException ntce) {
+		    String msg = "failed to validate default primary type";
+		    log.error(msg, ntce);
+		    throw new InvalidNodeTypeDefException(msg, ntce);
+		} catch (NoSuchNodeTypeException nsnte) {
+		    String msg = "failed to validate default primary type";
+		    log.error(msg, nsnte);
+		    throw new InvalidNodeTypeDefException(msg, nsnte);
+		}
+	    }
+
+	    // check required primary types
+	    QName[] reqTypes = cnd.getRequiredPrimaryTypes();
+	    if (reqTypes != null && reqTypes.length > 0) {
+		for (int n = 0; n < reqTypes.length; n++) {
+		    QName rpt = reqTypes[n];
+		    referenceToSelf = false;
+		    /**
+		     * check if this node type specifies itself as required
+		     * primary type
+		     */
+		    if (name.equals(rpt)) {
+			referenceToSelf = true;
+		    }
+		    /**
+		     * the required primary type must be registered, with one
+		     * notable exception: the node type just being registered
+		     */
+		    if (!name.equals(rpt) && !registeredNTDefs.containsKey(rpt)) {
+			String msg = "invalid required primary type: " + rpt;
+			log.error(msg);
+			throw new InvalidNodeTypeDefException(msg);
+		    }
+		    /**
+		     * check if default primary type satisfies the required
+		     * primary type constraint
+		     */
+		    if (defaultENT != null && !defaultENT.includesNodeType(rpt)) {
+			String msg = "default primary type does not satisfy required primary type constraint " + rpt;
+			log.error(msg);
+			throw new InvalidNodeTypeDefException(msg);
+		    }
+		    /**
+		     * build effective (i.e. merged and resolved) node type from
+		     * required primary type constraint and check for conflicts
+		     */
+		    try {
+			if (!referenceToSelf) {
+			    getEffectiveNodeType(rpt);
+			} else {
+			    /**
+			     * the required primary type is identical with the
+			     * node type just being registered; we have to
+			     * instantiate it 'manually'
+			     */
+			    if (ent == null) {
+				ent = EffectiveNodeType.create(this, ntd);
+			    }
+			}
+		    } catch (NodeTypeConflictException ntce) {
+			String msg = "failed to validate required primary type constraint";
+			log.error(msg, ntce);
+			throw new InvalidNodeTypeDefException(msg, ntce);
+		    } catch (NoSuchNodeTypeException nsnte) {
+			String msg = "failed to validate required primary type constraint";
+			log.error(msg, nsnte);
+			throw new InvalidNodeTypeDefException(msg, nsnte);
+		    }
+		}
+	    }
+	}
+
+	/**
+	 * now build effective (i.e. merged and resolved) node type from
+	 * this node type definition; this will potentially detect more
+	 * conflicts or problems
+	 */
+	if (ent == null) {
+	    try {
+		ent = EffectiveNodeType.create(this, ntd);
+	    } catch (NodeTypeConflictException ntce) {
+		String msg = "failed to resolve node type definition";
+		log.error(msg, ntce);
+		throw new InvalidNodeTypeDefException(msg, ntce);
+	    } catch (NoSuchNodeTypeException nsnte) {
+		String msg = "failed to resolve node type definition";
+		log.error(msg, nsnte);
+		throw new InvalidNodeTypeDefException(msg, nsnte);
+	    }
+	}
+	return ent;
     }
 
     /**
      * @return
      */
     synchronized QName[] getRegisteredNodeTypes() {
-        return (QName[]) registeredNTDefs.keySet().toArray(new QName[registeredNTDefs.size()]);
+	return (QName[]) registeredNTDefs.keySet().toArray(new QName[registeredNTDefs.size()]);
     }
 
     /**
      * @return
      */
     public ChildNodeDef getRootNodeDef() {
-        return rootNodeDef;
+	return rootNodeDef;
     }
 
     /**
@@ -769,13 +769,13 @@
      * @return
      */
     public synchronized EffectiveNodeType getEffectiveNodeType(QName ntName)
-            throws NoSuchNodeTypeException {
-        WeightedKey key = new WeightedKey(new QName[]{ntName});
-        if (entCache.contains(key)) {
-            return entCache.get(key);
-        } else {
-            throw new NoSuchNodeTypeException(ntName.toString());
-        }
+	    throws NoSuchNodeTypeException {
+	WeightedKey key = new WeightedKey(new QName[]{ntName});
+	if (entCache.contains(key)) {
+	    return entCache.get(key);
+	} else {
+	    throw new NoSuchNodeTypeException(ntName.toString());
+	}
     }
 
     /**
@@ -783,164 +783,164 @@
      * @return
      */
     public synchronized EffectiveNodeType buildEffectiveNodeType(QName[] ntNames)
-            throws NodeTypeConflictException, NoSuchNodeTypeException {
-        // 1. make sure every single node type is registered
-        for (int i = 0; i < ntNames.length; i++) {
-            if (!registeredNTDefs.containsKey(ntNames[i])) {
-                throw new NoSuchNodeTypeException(ntNames[i].toString());
-            }
-        }
-
-        WeightedKey key = new WeightedKey(ntNames);
-
-        // 2. check if aggregate has already been build
-        if (entCache.contains(key)) {
-            return entCache.get(key);
-        }
-
-        // 3. build aggregate
-        EffectiveNodeType result = null;
-
-        // build list of 'best' existing sub-aggregates
-        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));
-                // subtract the result from the temporary key
-                // (which is 'empty' now)
-                key = key.subtract(key);
-                break;
-            }
-            // walk list of existing aggregates sorted by 'weight' of
-            // aggregate (i.e. the cost of building it)
-            boolean foundSubResult = false;
-            Iterator iter = entCache.keys();
-            while (iter.hasNext()) {
-                WeightedKey k = (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));
-                    // subtract the result from the temporary key
-                    key = key.subtract(k);
-                    foundSubResult = true;
-                    break;
-                }
-            }
-            if (!foundSubResult) {
-                // no matching sub-aggregates found:
-                // build aggregate of remaining node types through iteration
-                QName[] remainder = key.toArray();
-                for (int i = 0; i < remainder.length; i++) {
-                    EffectiveNodeType ent = null;
-                    ent = EffectiveNodeType.create(this, remainder[i]);
-                    // store new effective node type
-                    entCache.put(ent);
-                    if (result == null) {
-                        result = ent;
-                    } else {
-                        result = result.merge(ent);
-                        // store intermediate result (sub-aggregate)
-                        entCache.put(result);
-                    }
-                }
-                // add aggregate of remaining node types to result list
-                tmpResults.add(result);
-                break;
-            }
-        }
-        // merge the sub-aggregates into new effective node type
-        for (int i = 0; i < tmpResults.size(); i++) {
-            if (result == null) {
-                result = (EffectiveNodeType) tmpResults.get(i);
-            } else {
-                result = result.merge((EffectiveNodeType) tmpResults.get(i));
-                // store intermediate result
-                entCache.put(result);
-            }
-        }
-        // we're done
-        return result;
+	    throws NodeTypeConflictException, NoSuchNodeTypeException {
+	// 1. make sure every single node type is registered
+	for (int i = 0; i < ntNames.length; i++) {
+	    if (!registeredNTDefs.containsKey(ntNames[i])) {
+		throw new NoSuchNodeTypeException(ntNames[i].toString());
+	    }
+	}
+
+	WeightedKey key = new WeightedKey(ntNames);
+
+	// 2. check if aggregate has already been build
+	if (entCache.contains(key)) {
+	    return entCache.get(key);
+	}
+
+	// 3. build aggregate
+	EffectiveNodeType result = null;
+
+	// build list of 'best' existing sub-aggregates
+	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));
+		// subtract the result from the temporary key
+		// (which is 'empty' now)
+		key = key.subtract(key);
+		break;
+	    }
+	    // walk list of existing aggregates sorted by 'weight' of
+	    // aggregate (i.e. the cost of building it)
+	    boolean foundSubResult = false;
+	    Iterator iter = entCache.keys();
+	    while (iter.hasNext()) {
+		WeightedKey k = (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));
+		    // subtract the result from the temporary key
+		    key = key.subtract(k);
+		    foundSubResult = true;
+		    break;
+		}
+	    }
+	    if (!foundSubResult) {
+		// no matching sub-aggregates found:
+		// build aggregate of remaining node types through iteration
+		QName[] remainder = key.toArray();
+		for (int i = 0; i < remainder.length; i++) {
+		    EffectiveNodeType ent = null;
+		    ent = EffectiveNodeType.create(this, remainder[i]);
+		    // store new effective node type
+		    entCache.put(ent);
+		    if (result == null) {
+			result = ent;
+		    } else {
+			result = result.merge(ent);
+			// store intermediate result (sub-aggregate)
+			entCache.put(result);
+		    }
+		}
+		// add aggregate of remaining node types to result list
+		tmpResults.add(result);
+		break;
+	    }
+	}
+	// merge the sub-aggregates into new effective node type
+	for (int i = 0; i < tmpResults.size(); i++) {
+	    if (result == null) {
+		result = (EffectiveNodeType) tmpResults.get(i);
+	    } else {
+		result = result.merge((EffectiveNodeType) tmpResults.get(i));
+		// store intermediate result
+		entCache.put(result);
+	    }
+	}
+	// we're done
+	return result;
     }
 
     void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain)
-            throws InvalidNodeTypeDefException, RepositoryException {
-        for (int i = 0; i < supertypes.length; i++) {
-            QName nt = supertypes[i];
-            int pos = inheritanceChain.lastIndexOf(nt);
-            if (pos >= 0) {
-                StringBuffer buf = new StringBuffer();
-                for (int j = 0; j < inheritanceChain.size(); j++) {
-                    if (j == pos) {
-                        buf.append("--> ");
-                    }
-                    buf.append(inheritanceChain.get(j));
-                    buf.append(" extends ");
-                }
-                buf.append("--> ");
-                buf.append(nt);
-                throw new InvalidNodeTypeDefException("circular inheritance detected: " + buf.toString());
-            }
-
-            try {
-                QName[] sta = getNodeTypeDef(nt).getSupertypes();
-                if (sta != null && sta.length > 0) {
-                    // check recursively
-                    inheritanceChain.push(nt);
-                    checkForCircularInheritance(sta, inheritanceChain);
-                    inheritanceChain.pop();
-                }
-            } catch (NoSuchNodeTypeException nsnte) {
-                String msg = "unknown supertype: " + nt;
-                log.error(msg, nsnte);
-                throw new InvalidNodeTypeDefException(msg, nsnte);
-            }
-        }
+	    throws InvalidNodeTypeDefException, RepositoryException {
+	for (int i = 0; i < supertypes.length; i++) {
+	    QName nt = supertypes[i];
+	    int pos = inheritanceChain.lastIndexOf(nt);
+	    if (pos >= 0) {
+		StringBuffer buf = new StringBuffer();
+		for (int j = 0; j < inheritanceChain.size(); j++) {
+		    if (j == pos) {
+			buf.append("--> ");
+		    }
+		    buf.append(inheritanceChain.get(j));
+		    buf.append(" extends ");
+		}
+		buf.append("--> ");
+		buf.append(nt);
+		throw new InvalidNodeTypeDefException("circular inheritance detected: " + buf.toString());
+	    }
+
+	    try {
+		QName[] sta = getNodeTypeDef(nt).getSupertypes();
+		if (sta != null && sta.length > 0) {
+		    // check recursively
+		    inheritanceChain.push(nt);
+		    checkForCircularInheritance(sta, inheritanceChain);
+		    inheritanceChain.pop();
+		}
+	    } catch (NoSuchNodeTypeException nsnte) {
+		String msg = "unknown supertype: " + nt;
+		log.error(msg, nsnte);
+		throw new InvalidNodeTypeDefException(msg, nsnte);
+	    }
+	}
     }
 
     void checkForCircularNodeAutoCreation(EffectiveNodeType childNodeENT, Stack definingParentNTs)
-            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')
-        QName[] childNodeNTs = childNodeENT.getAllNodeTypes();
-        for (int i = 0; i < childNodeNTs.length; i++) {
-            QName nt = childNodeNTs[i];
-            int pos = definingParentNTs.lastIndexOf(nt);
-            if (pos >= 0) {
-                StringBuffer buf = new StringBuffer();
-                for (int j = 0; j < definingParentNTs.size(); j++) {
-                    if (j == pos) {
-                        buf.append("--> ");
-                    }
-                    buf.append("node type ");
-                    buf.append(definingParentNTs.get(j));
-                    buf.append(" defines auto-created child node with default ");
-                }
-                buf.append("--> ");
-                buf.append("node type ");
-                buf.append(nt);
-                throw new InvalidNodeTypeDefException("circular node auto-creation detected: " + buf.toString());
-            }
-        }
-
-        ChildNodeDef[] nodeDefs = childNodeENT.getAutoCreateNodeDefs();
-        for (int i = 0; i < nodeDefs.length; i++) {
-            QName dnt = nodeDefs[i].getDefaultPrimaryType();
-            QName definingNT = nodeDefs[i].getDeclaringNodeType();
-            try {
-                if (dnt != null) {
-                    // check recursively
-                    definingParentNTs.push(definingNT);
-                    checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt), definingParentNTs);
-                    definingParentNTs.pop();
-                }
-            } catch (NoSuchNodeTypeException nsnte) {
-                String msg = definingNT + " defines invalid default node type for child node " + nodeDefs[i].getName();
-                log.error(msg, nsnte);
-                throw new InvalidNodeTypeDefException(msg, nsnte);
-            }
-        }
+	    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')
+	QName[] childNodeNTs = childNodeENT.getAllNodeTypes();
+	for (int i = 0; i < childNodeNTs.length; i++) {
+	    QName nt = childNodeNTs[i];
+	    int pos = definingParentNTs.lastIndexOf(nt);
+	    if (pos >= 0) {
+		StringBuffer buf = new StringBuffer();
+		for (int j = 0; j < definingParentNTs.size(); j++) {
+		    if (j == pos) {
+			buf.append("--> ");
+		    }
+		    buf.append("node type ");
+		    buf.append(definingParentNTs.get(j));
+		    buf.append(" defines auto-created child node with default ");
+		}
+		buf.append("--> ");
+		buf.append("node type ");
+		buf.append(nt);
+		throw new InvalidNodeTypeDefException("circular node auto-creation detected: " + buf.toString());
+	    }
+	}
+
+	ChildNodeDef[] nodeDefs = childNodeENT.getAutoCreateNodeDefs();
+	for (int i = 0; i < nodeDefs.length; i++) {
+	    QName dnt = nodeDefs[i].getDefaultPrimaryType();
+	    QName definingNT = nodeDefs[i].getDeclaringNodeType();
+	    try {
+		if (dnt != null) {
+		    // check recursively
+		    definingParentNTs.push(definingNT);
+		    checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt), definingParentNTs);
+		    definingParentNTs.pop();
+		}
+	    } catch (NoSuchNodeTypeException nsnte) {
+		String msg = definingNT + " defines invalid default node type for child node " + nodeDefs[i].getName();
+		log.error(msg, nsnte);
+		throw new InvalidNodeTypeDefException(msg, nsnte);
+	    }
+	}
     }
 
     /**
@@ -980,37 +980,37 @@
      * @throws RepositoryException
      */
     public synchronized EffectiveNodeType registerNodeType(NodeTypeDef ntd)
-            throws InvalidNodeTypeDefException, RepositoryException {
-        // validate and register new node type definition
-        EffectiveNodeType ent = internalRegister(ntd);
-        // persist new node type definition
-        customNTDefs.add(ntd);
-        OutputStream out = null;
-        try {
-            out = customNodeTypesResource.getOutputStream();
-            customNTDefs.store(out, nsReg);
-        } catch (IOException ioe) {
-            String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-            log.error(error, ioe);
-            throw new RepositoryException(error, ioe);
-        } catch (FileSystemException fse) {
-            String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-            log.error(error, fse);
-            throw new RepositoryException(error, fse);
-        } finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (IOException ioe) {
-                    // ignore
-                }
-            }
-        }
+	    throws InvalidNodeTypeDefException, RepositoryException {
+	// validate and register new node type definition
+	EffectiveNodeType ent = internalRegister(ntd);
+	// persist new node type definition
+	customNTDefs.add(ntd);
+	OutputStream out = null;
+	try {
+	    out = customNodeTypesResource.getOutputStream();
+	    customNTDefs.store(out, nsReg);
+	} catch (IOException ioe) {
+	    String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+	    log.error(error, ioe);
+	    throw new RepositoryException(error, ioe);
+	} catch (FileSystemException fse) {
+	    String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+	    log.error(error, fse);
+	    throw new RepositoryException(error, fse);
+	} finally {
+	    if (out != null) {
+		try {
+		    out.close();
+		} catch (IOException ioe) {
+		    // ignore
+		}
+	    }
+	}
 
-        // notify listeners
-        notifyRegistered(ntd.getName());
+	// notify listeners
+	notifyRegistered(ntd.getName());
 
-        return ent;
+	return ent;
     }
 
     /**
@@ -1029,74 +1029,74 @@
      * @throws RepositoryException
      */
     public synchronized void registerNodeTypes(Collection ntDefs)
-            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);
-        } catch (RepositoryException e) {
-            // store exception so it can be re-thrown later on
-            re = e;
-        } catch (InvalidNodeTypeDefException e) {
-            // 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));
-            }
-            OutputStream out = null;
-            try {
-                out = customNodeTypesResource.getOutputStream();
-                customNTDefs.store(out, nsReg);
-            } catch (IOException ioe) {
-                String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-                log.error(error, ioe);
-                throw new RepositoryException(error, ioe);
-            } catch (FileSystemException fse) {
-                String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-                log.error(error, fse);
-                throw new RepositoryException(error, fse);
-            } finally {
-                if (out != null) {
-                    try {
-                        out.close();
-                    } catch (IOException ioe) {
-                        // ignore
-                    }
-                }
-            }
-
-            // notify listeners
-            for (Iterator iter = newNTNames.iterator(); iter.hasNext(); ) {
-                QName ntName = (QName) iter.next();
-                notifyRegistered(ntName);
-            }
-        }
-
-        // re-throw exception as necessary
-        if (re != null) {
-            throw re;
-        } else if (intde != null) {
-            throw intde;
-        }
+	    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);
+	} catch (RepositoryException e) {
+	    // store exception so it can be re-thrown later on
+	    re = e;
+	} catch (InvalidNodeTypeDefException e) {
+	    // 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));
+	    }
+	    OutputStream out = null;
+	    try {
+		out = customNodeTypesResource.getOutputStream();
+		customNTDefs.store(out, nsReg);
+	    } catch (IOException ioe) {
+		String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+		log.error(error, ioe);
+		throw new RepositoryException(error, ioe);
+	    } catch (FileSystemException fse) {
+		String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+		log.error(error, fse);
+		throw new RepositoryException(error, fse);
+	    } finally {
+		if (out != null) {
+		    try {
+			out.close();
+		    } catch (IOException ioe) {
+			// ignore
+		    }
+		}
+	    }
+
+	    // notify listeners
+	    for (Iterator iter = newNTNames.iterator(); iter.hasNext();) {
+		QName ntName = (QName) iter.next();
+		notifyRegistered(ntName);
+	    }
+	}
+
+	// re-throw exception as necessary
+	if (re != null) {
+	    throw re;
+	} else if (intde != null) {
+	    throw intde;
+	}
     }
 
     /**
@@ -1105,88 +1105,80 @@
      * @throws RepositoryException
      */
     public synchronized void unregisterNodeType(QName name)
-            throws NoSuchNodeTypeException, RepositoryException {
-        if (!registeredNTDefs.containsKey(name)) {
-            throw new NoSuchNodeTypeException(name.toString());
-        }
-        if (builtInNTDefs.contains(name)) {
-            throw new RepositoryException(name.toString() + ": can't unregister built-in node type.");
-        }
-
-        /**
-         * collect names of node types that have dependencies on the given
-         * node type
-         */
-        HashSet dependentNTs = new HashSet();
-        // only custom node types can have dependencies on a custom node type
-        Iterator iter = customNTDefs.all().iterator();
-        while (iter.hasNext()) {
-            NodeTypeDef ntd = (NodeTypeDef) iter.next();
-            if (ntd.getDependencies().contains(name)) {
-                dependentNTs.add(ntd.getName());
-            }
-        }
-
-        /**
-         * todo
-         * check if the given node type (or any node type that has dependencies
-         * on this node type) is currently referenced by nodes in the repository.
-         *
-         * this is absolutely necessary in order to guarantee integrity of
-         * repository content.
-         *
-         * throw exception until this is can be done (using search)
-         */
-        boolean isReferenced = true;
-        if (isReferenced) {
-            throw new RepositoryException("not yet implemented");
-        }
-
-        NodeTypeDef ntd = (NodeTypeDef) registeredNTDefs.get(name);
-        registeredNTDefs.remove(name);
-        // remove effective node type from aggregates cache
-        entCache.remove(new QName[]{name});
-
-        // remove poperty & child node definitions
-        PropDef[] pda = ntd.getPropertyDefs();
-        for (int i = 0; i < pda.length; i++) {
-            PropDefId id = new PropDefId(pda[i]);
-            propDefs.remove(id);
-        }
-        ChildNodeDef[] nda = ntd.getChildNodeDefs();
-        for (int i = 0; i < nda.length; i++) {
-            NodeDefId id = new NodeDefId(nda[i]);
-            nodeDefs.remove(id);
-        }
-
-        // persist removal of node type definition
-        customNTDefs.remove(name);
-        OutputStream out = null;
-        try {
-            out = customNodeTypesResource.getOutputStream();
-            customNTDefs.store(out, nsReg);
-        } catch (IOException ioe) {
-            String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-            log.error(error, ioe);
-            throw new RepositoryException(error, ioe);
-        } catch (FileSystemException fse) {
-            String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
-            log.error(error, fse);
-            throw new RepositoryException(error, fse);
-        } finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (IOException ioe) {
-                    // ignore
-                }
-            }
-        }
+	    throws NoSuchNodeTypeException, RepositoryException {
+	if (!registeredNTDefs.containsKey(name)) {
+	    throw new NoSuchNodeTypeException(name.toString());
+	}
+	if (builtInNTDefs.contains(name)) {
+	    throw new RepositoryException(name.toString() + ": can't unregister built-in node type.");
+	}
+
+	/**
+	 * collect names of node types that have dependencies on the given
+	 * node type
+	 */
+	Set dependentNTs = getDependentNodeTypes(name);
+
+	/**
+	 * todo
+	 * check if the given node type (or any node type that has dependencies
+	 * on this node type) is currently referenced by nodes in the repository.
+	 *
+	 * this is absolutely necessary in order to guarantee integrity of
+	 * repository content.
+	 *
+	 * throw exception until this is can be done (using search)
+	 */
+	boolean isReferenced = true;
+	if (isReferenced) {
+	    throw new RepositoryException("not yet implemented");
+	}
+
+	NodeTypeDef ntd = (NodeTypeDef) registeredNTDefs.get(name);
+	registeredNTDefs.remove(name);
+	// remove effective node type from aggregates cache
+	entCache.remove(new QName[]{name});
+
+	// remove poperty & child node definitions
+	PropDef[] pda = ntd.getPropertyDefs();
+	for (int i = 0; i < pda.length; i++) {
+	    PropDefId id = new PropDefId(pda[i]);
+	    propDefs.remove(id);
+	}
+	ChildNodeDef[] nda = ntd.getChildNodeDefs();
+	for (int i = 0; i < nda.length; i++) {
+	    NodeDefId id = new NodeDefId(nda[i]);
+	    nodeDefs.remove(id);
+	}
+
+	// persist removal of node type definition
+	customNTDefs.remove(name);
+	OutputStream out = null;
+	try {
+	    out = customNodeTypesResource.getOutputStream();
+	    customNTDefs.store(out, nsReg);
+	} catch (IOException ioe) {
+	    String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+	    log.error(error, ioe);
+	    throw new RepositoryException(error, ioe);
+	} catch (FileSystemException fse) {
+	    String error = "internal error: failed to write custom node type definition to " + customNodeTypesResource.getPath();
+	    log.error(error, fse);
+	    throw new RepositoryException(error, fse);
+	} finally {
+	    if (out != null) {
+		try {
+		    out.close();
+		} catch (IOException ioe) {
+		    // ignore
+		}
+	    }
+	}
 
-        // @todo remove also any node types & aggregates which have dependencies on this node type
+	// @todo remove also any node types & aggregates which have dependencies on this node type
 
-        // notify listeners
-        notifyUnregistered(name);
+	// notify listeners
+	notifyUnregistered(name);
     }
 
     /**
@@ -1197,33 +1189,62 @@
      * @throws RepositoryException
      */
     public synchronized EffectiveNodeType reregisterNodeType(NodeTypeDef ntd)
-            throws NoSuchNodeTypeException, InvalidNodeTypeDefException,
-            RepositoryException {
-        QName name = ntd.getName();
-        if (!registeredNTDefs.containsKey(name)) {
-            throw new NoSuchNodeTypeException(name.toString());
-        }
-        if (builtInNTDefs.contains(name)) {
-            throw new RepositoryException(name.toString() + ": can't reregister built-in node type.");
-        }
-
-        /**
-         * todo
-         * - check if this node type (or any node type that has dependencies
-         *   on this node type) is currently referenced by any nodes;
-         *   this is absolutely necessary in order to guarantee integrity of
-         *   repository content
-         * - validate new node type definition
-         * - build diff of old & new node type definition
-         * - check if applying changes to affected nodes would violate existing node type constraints
-         * - re-register node type definition and update caches
-         * - notify listeners on re-registration
-         * - apply and persist changes to affected nodes
-         * - what else?
-         */
-        //unregisterNodeType(name);
-        //return registerNodeType(ntd);
-        throw new RepositoryException("not yet implemented");
+	    throws NoSuchNodeTypeException, InvalidNodeTypeDefException,
+	    RepositoryException {
+	QName name = ntd.getName();
+	if (!registeredNTDefs.containsKey(name)) {
+	    throw new NoSuchNodeTypeException(name.toString());
+	}
+	if (builtInNTDefs.contains(name)) {
+	    throw new RepositoryException(name.toString() + ": can't reregister built-in node type.");
+	}
+
+	/**
+	 * todo
+	 * - check if this node type (or any node type that has dependencies
+	 *   on this node type) is currently referenced by any nodes;
+	 *   this is absolutely necessary in order to guarantee integrity of
+	 *   repository content
+	 * - validate new node type definition
+	 * - build diff of old & new node type definition
+	 * - check if applying changes to affected nodes would violate existing node type constraints
+	 * - re-register node type definition and update caches
+	 * - notify listeners on re-registration
+	 * - apply and persist changes to affected nodes
+	 * - what else?
+	 */
+	//unregisterNodeType(name);
+	//return registerNodeType(ntd);
+	throw new RepositoryException("not yet implemented");
+    }
+
+    /**
+     * Returns the names of those registered node types that have
+     * dependencies on the given node type.
+     *
+     * @param nodeTypeName
+     * @return a set of node type <code>QName</code>s
+     * @throws NoSuchNodeTypeException
+     */
+    public synchronized Set getDependentNodeTypes(QName nodeTypeName)
+	    throws NoSuchNodeTypeException {
+	if (!registeredNTDefs.containsKey(nodeTypeName)) {
+	    throw new NoSuchNodeTypeException(nodeTypeName.toString());
+	}
+
+	/**
+	 * collect names of those node types that have dependencies on the given
+	 * node type
+	 */
+	HashSet names = new HashSet();
+	Iterator iter = registeredNTDefs.keySet().iterator();
+	while (iter.hasNext()) {
+	    NodeTypeDef ntd = (NodeTypeDef) iter.next();
+	    if (ntd.getDependencies().contains(nodeTypeName)) {
+		names.add(ntd.getName());
+	    }
+	}
+	return names;
     }
 
     /**
@@ -1232,18 +1253,18 @@
      * @throws NoSuchNodeTypeException
      */
     public synchronized NodeTypeDef getNodeTypeDef(QName nodeTypeName) throws NoSuchNodeTypeException {
-        if (!registeredNTDefs.containsKey(nodeTypeName)) {
-            throw new NoSuchNodeTypeException(nodeTypeName.toString());
-        }
-        NodeTypeDef def = (NodeTypeDef) registeredNTDefs.get(nodeTypeName);
-        // return clone to make sure nobody messes around with the 'real' definition
-        try {
-            return (NodeTypeDef) def.clone();
-        } catch (CloneNotSupportedException e) {
-            // should never get here
-            log.fatal("internal error", e);
-            throw new InternalError(e.getMessage());
-        }
+	if (!registeredNTDefs.containsKey(nodeTypeName)) {
+	    throw new NoSuchNodeTypeException(nodeTypeName.toString());
+	}
+	NodeTypeDef def = (NodeTypeDef) registeredNTDefs.get(nodeTypeName);
+	// return clone to make sure nobody messes around with the 'real' definition
+	try {
+	    return (NodeTypeDef) def.clone();
+	} catch (CloneNotSupportedException e) {
+	    // should never get here
+	    log.fatal("internal error", e);
+	    throw new InternalError(e.getMessage());
+	}
     }
 
     /**
@@ -1251,7 +1272,7 @@
      * @return
      */
     public synchronized boolean isRegistered(QName nodeTypeName) {
-        return registeredNTDefs.containsKey(nodeTypeName);
+	return registeredNTDefs.containsKey(nodeTypeName);
     }
 
     /**
@@ -1259,18 +1280,18 @@
      * @return
      */
     public ChildNodeDef getNodeDef(NodeDefId id) {
-        ChildNodeDef def = (ChildNodeDef) nodeDefs.get(id);
-        if (def == null) {
-            return null;
-        }
-        // return clone to make sure nobody messes around with the 'real' definition
-        try {
-            return (ChildNodeDef) def.clone();
-        } catch (CloneNotSupportedException e) {
-            // should never get here
-            log.fatal("internal error", e);
-            throw new InternalError(e.getMessage());
-        }
+	ChildNodeDef def = (ChildNodeDef) nodeDefs.get(id);
+	if (def == null) {
+	    return null;
+	}
+	// return clone to make sure nobody messes around with the 'real' definition
+	try {
+	    return (ChildNodeDef) def.clone();
+	} catch (CloneNotSupportedException e) {
+	    // should never get here
+	    log.fatal("internal error", e);
+	    throw new InternalError(e.getMessage());
+	}
     }
 
     /**
@@ -1278,18 +1299,18 @@
      * @return
      */
     public PropDef getPropDef(PropDefId id) {
-        PropDef def = (PropDef) propDefs.get(id);
-        if (def == null) {
-            return null;
-        }
-        // return clone to make sure nobody messes around with the 'real' definition
-        try {
-            return (PropDef) def.clone();
-        } catch (CloneNotSupportedException e) {
-            // should never get here
-            log.fatal("internal error", e);
-            throw new InternalError(e.getMessage());
-        }
+	PropDef def = (PropDef) propDefs.get(id);
+	if (def == null) {
+	    return null;
+	}
+	// return clone to make sure nobody messes around with the 'real' definition
+	try {
+	    return (PropDef) def.clone();
+	} catch (CloneNotSupportedException e) {
+	    // should never get here
+	    log.fatal("internal error", e);
+	    throw new InternalError(e.getMessage());
+	}
     }
 
     //----------------------------------------------------------< diagnostics >
@@ -1300,87 +1321,87 @@
      * @throws RepositoryException
      */
     void dump(PrintStream ps) throws RepositoryException {
-        ps.println("NodeTypeManager (" + this + ")");
-        ps.println();
-        ps.println("Registered NodeTypes:");
-        ps.println();
-        Iterator iter = registeredNTDefs.values().iterator();
-        while (iter.hasNext()) {
-            NodeTypeDef ntd = (NodeTypeDef) iter.next();
-            ps.println(ntd.getName());
-            QName[] supertypes = ntd.getSupertypes();
-            ps.println("\tSupertypes");
-            for (int i = 0; i < supertypes.length; i++) {
-                ps.println("\t\t" + supertypes[i]);
-            }
-            ps.println("\tMixin\t" + ntd.isMixin());
-            ps.println("\tOrderableChildNodes\t" + ntd.hasOrderableChildNodes());
-            PropDef[] pd = ntd.getPropertyDefs();
-            for (int i = 0; i < pd.length; i++) {
-                ps.print("\tPropertyDef");
-                ps.println(" (declared in " + pd[i].getDeclaringNodeType() + ") id=" + new PropDefId(pd[i]));
-                ps.println("\t\tName\t\t" + (pd[i].definesResidual() ? "*" : pd[i].getName().toString()));
-                String type = pd[i].getRequiredType() == 0 ? "null" : PropertyType.nameFromValue(pd[i].getRequiredType());
-                ps.println("\t\tRequiredType\t" + type);
-                ValueConstraint[] vca = pd[i].getValueConstraints();
-                StringBuffer constraints = new StringBuffer();
-                if (vca == null) {
-                    constraints.append("<null>");
-                } else {
-                    for (int n = 0; n < vca.length; n++) {
-                        if (constraints.length() > 0) {
-                            constraints.append(", ");
-                        }
-                        constraints.append(vca[n].getDefinition());
-                    }
-                }
-                ps.println("\t\tValueConstraints\t" + constraints.toString());
-                InternalValue[] defVals = pd[i].getDefaultValues();
-                StringBuffer defaultValues = new StringBuffer();
-                if (defVals == null) {
-                    defaultValues.append("<null>");
-                } else {
-                    for (int n = 0; n < defVals.length; n++) {
-                        if (defaultValues.length() > 0) {
-                            defaultValues.append(", ");
-                        }
-                        defaultValues.append(defVals[n].toString());
-                    }
-                }
-                ps.println("\t\tDefaultValue\t" + defaultValues.toString());
-                ps.println("\t\tAutoCreate\t" + pd[i].isAutoCreate());
-                ps.println("\t\tMandatory\t" + pd[i].isMandatory());
-                ps.println("\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(pd[i].getOnParentVersion()));
-                ps.println("\t\tProtected\t" + pd[i].isProtected());
-                ps.println("\t\tPrimaryItem\t" + pd[i].isPrimaryItem());
-                ps.println("\t\tMultiple\t" + pd[i].isMultiple());
-            }
-            ChildNodeDef[] nd = ntd.getChildNodeDefs();
-            for (int i = 0; i < nd.length; i++) {
-                ps.print("\tNodeDef");
-                ps.println(" (declared in " + nd[i].getDeclaringNodeType() + ") id=" + new NodeDefId(nd[i]));
-                ps.println("\t\tName\t\t" + (nd[i].definesResidual() ? "*" : nd[i].getName().toString()));
-                QName[] reqPrimaryTypes = nd[i].getRequiredPrimaryTypes();
-                if (reqPrimaryTypes != null && reqPrimaryTypes.length > 0) {
-                    for (int n = 0; n < reqPrimaryTypes.length; n++) {
-                        ps.print("\t\tRequiredPrimaryType\t" + reqPrimaryTypes[n]);
-                    }
-                }
-                QName defPrimaryType = nd[i].getDefaultPrimaryType();
-                if (defPrimaryType != null) {
-                    ps.print("\n\t\tDefaultPrimaryType\t" + defPrimaryType);
-                }
-                ps.println("\n\t\tAutoCreate\t" + nd[i].isAutoCreate());
-                ps.println("\t\tMandatory\t" + nd[i].isMandatory());
-                ps.println("\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(nd[i].getOnParentVersion()));
-                ps.println("\t\tProtected\t" + nd[i].isProtected());
-                ps.println("\t\tPrimaryItem\t" + nd[i].isPrimaryItem());
-                ps.println("\t\tAllowSameNameSibs\t" + nd[i].allowSameNameSibs());
-            }
-        }
-        ps.println();
+	ps.println("NodeTypeManager (" + this + ")");
+	ps.println();
+	ps.println("Registered NodeTypes:");
+	ps.println();
+	Iterator iter = registeredNTDefs.values().iterator();
+	while (iter.hasNext()) {
+	    NodeTypeDef ntd = (NodeTypeDef) iter.next();
+	    ps.println(ntd.getName());
+	    QName[] supertypes = ntd.getSupertypes();
+	    ps.println("\tSupertypes");
+	    for (int i = 0; i < supertypes.length; i++) {
+		ps.println("\t\t" + supertypes[i]);
+	    }
+	    ps.println("\tMixin\t" + ntd.isMixin());
+	    ps.println("\tOrderableChildNodes\t" + ntd.hasOrderableChildNodes());
+	    PropDef[] pd = ntd.getPropertyDefs();
+	    for (int i = 0; i < pd.length; i++) {
+		ps.print("\tPropertyDef");
+		ps.println(" (declared in " + pd[i].getDeclaringNodeType() + ") id=" + new PropDefId(pd[i]));
+		ps.println("\t\tName\t\t" + (pd[i].definesResidual() ? "*" : pd[i].getName().toString()));
+		String type = pd[i].getRequiredType() == 0 ? "null" : PropertyType.nameFromValue(pd[i].getRequiredType());
+		ps.println("\t\tRequiredType\t" + type);
+		ValueConstraint[] vca = pd[i].getValueConstraints();
+		StringBuffer constraints = new StringBuffer();
+		if (vca == null) {
+		    constraints.append("<null>");
+		} else {
+		    for (int n = 0; n < vca.length; n++) {
+			if (constraints.length() > 0) {
+			    constraints.append(", ");
+			}
+			constraints.append(vca[n].getDefinition());
+		    }
+		}
+		ps.println("\t\tValueConstraints\t" + constraints.toString());
+		InternalValue[] defVals = pd[i].getDefaultValues();
+		StringBuffer defaultValues = new StringBuffer();
+		if (defVals == null) {
+		    defaultValues.append("<null>");
+		} else {
+		    for (int n = 0; n < defVals.length; n++) {
+			if (defaultValues.length() > 0) {
+			    defaultValues.append(", ");
+			}
+			defaultValues.append(defVals[n].toString());
+		    }
+		}
+		ps.println("\t\tDefaultValue\t" + defaultValues.toString());
+		ps.println("\t\tAutoCreate\t" + pd[i].isAutoCreate());
+		ps.println("\t\tMandatory\t" + pd[i].isMandatory());
+		ps.println("\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(pd[i].getOnParentVersion()));
+		ps.println("\t\tProtected\t" + pd[i].isProtected());
+		ps.println("\t\tPrimaryItem\t" + pd[i].isPrimaryItem());
+		ps.println("\t\tMultiple\t" + pd[i].isMultiple());
+	    }
+	    ChildNodeDef[] nd = ntd.getChildNodeDefs();
+	    for (int i = 0; i < nd.length; i++) {
+		ps.print("\tNodeDef");
+		ps.println(" (declared in " + nd[i].getDeclaringNodeType() + ") id=" + new NodeDefId(nd[i]));
+		ps.println("\t\tName\t\t" + (nd[i].definesResidual() ? "*" : nd[i].getName().toString()));
+		QName[] reqPrimaryTypes = nd[i].getRequiredPrimaryTypes();
+		if (reqPrimaryTypes != null && reqPrimaryTypes.length > 0) {
+		    for (int n = 0; n < reqPrimaryTypes.length; n++) {
+			ps.print("\t\tRequiredPrimaryType\t" + reqPrimaryTypes[n]);
+		    }
+		}
+		QName defPrimaryType = nd[i].getDefaultPrimaryType();
+		if (defPrimaryType != null) {
+		    ps.print("\n\t\tDefaultPrimaryType\t" + defPrimaryType);
+		}
+		ps.println("\n\t\tAutoCreate\t" + nd[i].isAutoCreate());
+		ps.println("\t\tMandatory\t" + nd[i].isMandatory());
+		ps.println("\t\tOnVersion\t" + OnParentVersionAction.nameFromValue(nd[i].getOnParentVersion()));
+		ps.println("\t\tProtected\t" + nd[i].isProtected());
+		ps.println("\t\tPrimaryItem\t" + nd[i].isPrimaryItem());
+		ps.println("\t\tAllowSameNameSibs\t" + nd[i].allowSameNameSibs());
+	    }
+	}
+	ps.println();
 
-        entCache.dump(ps);
+	entCache.dump(ps);
     }
 
     //--------------------------------------------------------< inner classes >
@@ -1412,237 +1433,237 @@
      * 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 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()) {
-                Object obj = i.next();
-                h = 37 * h + (obj != null ? obj.hashCode() : 0);
-            }
-            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 + ")";
-        }
+	/**
+	 * 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 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()) {
+		Object obj = i.next();
+		h = 37 * h + (obj != null ? obj.hashCode() : 0);
+	    }
+	    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();
-        }
-
-        //------------------------------------------------------< diagnostics >
-        /**
-         * Dumps the state of this <code>EffectiveNodeTypeCache</code> instance.
-         *
-         * @param ps
-         * @throws RepositoryException
-         */
-        void dump(PrintStream ps) throws RepositoryException {
-            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);
-            }
-        }
+	// 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();
+	}
+
+	//------------------------------------------------------< diagnostics >
+	/**
+	 * Dumps the state of this <code>EffectiveNodeTypeCache</code> instance.
+	 *
+	 * @param ps
+	 * @throws RepositoryException
+	 */
+	void dump(PrintStream ps) throws RepositoryException {
+	    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);
+	    }
+	}
     }
 }