Index: src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefReader.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefReader.java (working copy) @@ -201,11 +201,6 @@ doNodeTypeName(ntd); doSuperTypes(ntd); doOptions(ntd); - // add nt:base to supertypes if not mixin and does not define - // any supertype yet. - if (!ntd.isMixin() && ntd.getSupertypes().length == 0) { - ntd.setSupertypes(new QName[]{QName.NT_BASE}); - } doItemDefs(ntd); nodeTypeDefs.add(ntd); } Index: src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefWriter.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefWriter.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/compact/CompactNodeTypeDefWriter.java (working copy) @@ -170,14 +170,12 @@ */ private void writeSupertypes(NodeTypeDef ntd) throws IOException { QName[] sta = ntd.getSupertypes(); - if (sta != null) { - String delim = " > "; - for (int i = 0; i < sta.length; i++) { - if (!sta[i].equals(QName.NT_BASE)) { - out.write(delim); - out.write(resolve(sta[i])); - delim = ", "; - } + String delim = " > "; + for (int i = 0; i < sta.length; i++) { + if (!sta[i].equals(QName.NT_BASE)) { + out.write(delim); + out.write(resolve(sta[i])); + delim = ", "; } } } Index: src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeDef.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeDef.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeDef.java (working copy) @@ -24,6 +24,8 @@ import java.util.HashSet; import java.util.Set; import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; /** * A NodeTypeDef holds the definition of a node type. @@ -31,7 +33,13 @@ public class NodeTypeDef implements Cloneable { private QName name; - private HashSet supertypes; + + /** + * Ordered array of supertype names. Empty if no supertypes have been + * specified. Never null. + */ + private QName[] supertypes; + private boolean mixin; private boolean orderableChildNodes; private QName primaryItemName; @@ -48,7 +56,7 @@ primaryItemName = null; nodeDefs = new HashSet(); propDefs = new HashSet(); - supertypes = new HashSet(); + supertypes = QName.EMPTY_ARRAY; mixin = false; orderableChildNodes = false; } @@ -69,7 +77,7 @@ if (dependencies == null) { dependencies = new HashSet(); // supertypes - dependencies.addAll(supertypes); + dependencies.addAll(Arrays.asList(supertypes)); // child node definitions for (Iterator iter = nodeDefs.iterator(); iter.hasNext();) { NodeDef nd = (NodeDef) iter.next(); @@ -127,8 +135,17 @@ */ public void setSupertypes(QName[] names) { resetDependencies(); - supertypes.clear(); - supertypes.addAll(Arrays.asList(names)); + // Optimize common cases (zero or one supertypes) + if (names.length == 0) { + supertypes = QName.EMPTY_ARRAY; + } else if (names.length == 1) { + supertypes = new QName[] { names[0] }; + } else { + // Sort and remove duplicates + SortedSet types = new TreeSet(); + types.addAll(Arrays.asList(names)); + supertypes = (QName[]) types.toArray(new QName[types.size()]); + } } /** @@ -192,17 +209,22 @@ } /** - * Returns an array containing the names of the supertypes or - * null if not set. + * Returns an array containing the names of the supertypes. If no + * supertypes have been specified, then an empty array is returned + * for mixin types and the nt:base primary type and + * an array containing just nt:base for other primary types. + *

+ * The returned array must not be modified by the application. * - * @return an array listing the names of the supertypes or - * null if not set. + * @return a sorted array of supertype names */ public QName[] getSupertypes() { - if (supertypes.isEmpty()) { - return QName.EMPTY_ARRAY; + if (supertypes.length > 0 + || isMixin() || QName.NT_BASE.equals(getName())) { + return supertypes; + } else { + return new QName[] { QName.NT_BASE }; } - return (QName[]) supertypes.toArray(new QName[supertypes.size()]); } /** @@ -266,7 +288,7 @@ NodeTypeDef clone = new NodeTypeDef(); clone.name = name; clone.primaryItemName = primaryItemName; - clone.supertypes = (HashSet) supertypes.clone(); + clone.supertypes = supertypes; // immutable, thus ok to share clone.mixin = mixin; clone.orderableChildNodes = orderableChildNodes; clone.nodeDefs = (HashSet) nodeDefs.clone(); @@ -282,7 +304,7 @@ NodeTypeDef other = (NodeTypeDef) obj; return (name == null ? other.name == null : name.equals(other.name)) && (primaryItemName == null ? other.primaryItemName == null : primaryItemName.equals(other.primaryItemName)) - && supertypes.equals(other.supertypes) + && Arrays.equals(getSupertypes(), other.getSupertypes()) && mixin == other.mixin && orderableChildNodes == other.orderableChildNodes && propDefs.equals(other.propDefs) Index: src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/EffectiveNodeType.java (working copy) @@ -198,7 +198,7 @@ // resolve supertypes recursively QName[] supertypes = ntd.getSupertypes(); - if (supertypes != null && supertypes.length > 0) { + if (supertypes.length > 0) { ent.internalMerge(NodeTypeRegistry.getEffectiveNodeType(supertypes, entCache, ntdCache), true); } Index: src/main/java/org/apache/jackrabbit/core/nodetype/xml/NodeTypeWriter.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/xml/NodeTypeWriter.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/xml/NodeTypeWriter.java (working copy) @@ -135,7 +135,7 @@ // supertype declarations QName[] supertypes = def.getSupertypes(); - if (supertypes != null && supertypes.length > 0) { + if (supertypes.length > 0) { builder.startElement(Constants.SUPERTYPES_ELEMENT); for (int i = 0; i < supertypes.length; i++) { builder.addContentElement( Index: src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java (revision 418787) +++ src/main/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java (working copy) @@ -1026,7 +1026,7 @@ NodeTypeDef ntd = (NodeTypeDef) ntDefCache.get(nt); QName[] sta = ntd.getSupertypes(); - if (sta != null && sta.length > 0) { + if (sta.length > 0) { // check recursively inheritanceChain.push(nt); checkForCircularInheritance(sta, inheritanceChain, ntDefCache); @@ -1307,7 +1307,7 @@ // validate supertypes QName[] supertypes = ntd.getSupertypes(); - if (supertypes != null && supertypes.length > 0) { + if (supertypes.length > 0) { for (int i = 0; i < supertypes.length; i++) { checkNamespace(supertypes[i], nsReg); /** @@ -1348,7 +1348,7 @@ * build effective (i.e. merged and resolved) node type from supertypes * and check for conflicts */ - if (supertypes != null && supertypes.length > 0) { + if (supertypes.length > 0) { try { EffectiveNodeType est = getEffectiveNodeType(supertypes, entCache, ntdCache); // make sure that all primary types except nt:base extend from nt:base @@ -1368,14 +1368,6 @@ log.debug(msg); throw new InvalidNodeTypeDefException(msg, nsnte); } - } else { - // no supertypes specified: has to be either a mixin type or nt:base - if (!ntd.isMixin() && !QName.NT_BASE.equals(ntd.getName())) { - String msg = "[" + name - + "] all primary node types except nt:base itself must be (directly or indirectly) derived from nt:base"; - log.debug(msg); - throw new InvalidNodeTypeDefException(msg); - } } checkNamespace(ntd.getPrimaryItemName(), nsReg);