jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r421270 [8/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/ commons/src/ commons/src/main/ commons/src/main/java/ commons/src/main/java/org/ commons/src/main/java/org/apache/ commons/src/main/java/org/apache/jackrabbit/ commons/src/main/...
Date Wed, 12 Jul 2006 13:33:27 GMT
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.name;
+
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.NamespaceListener;
+import org.apache.jackrabbit.name.AbstractNamespaceResolver;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.name.PathFormat;
+import org.apache.jackrabbit.name.NameFormat;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import org.apache.commons.collections.map.LRUMap;
+
+import javax.jcr.NamespaceException;
+
+import java.util.Map;
+
+/**
+ * Implements a {@link NamespaceResolver} that caches QName to resolved jcr names
+ * and vice versa. The cache is invalidated when a namespace uri to prefix
+ * mapping is changed.
+ */
+class CachingNamespaceResolver implements NamespaceResolver, NamespaceListener {
+
+    /**
+     * The base namespace resolver.
+     */
+    private final AbstractNamespaceResolver base;
+
+    /**
+     * Maps QName instances to resolved jcr name Strings.
+     */
+    private final Map qnameToJCRName;
+
+    /**
+     * Maps resolved jcr name Strings to QName instances.
+     */
+    private final Map jcrNameToQName;
+
+    /**
+     * Creates a new <code>CachingNamespaceResolver</code>.
+     *
+     * @param base      a base namespace resolver with support for listener
+     *                  registration.
+     * @param cacheSize number of mappings this resolver may cache.
+     */
+    CachingNamespaceResolver(AbstractNamespaceResolver base, int cacheSize) {
+        this.base = base;
+        qnameToJCRName = new LRUMap(cacheSize);
+        jcrNameToQName = new LRUMap(cacheSize);
+        this.base.addListener(this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        return base.getURI(prefix);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        return base.getPrefix(uri);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public synchronized QName getQName(String jcrName)
+            throws IllegalNameException, UnknownPrefixException {
+        QName qName = (QName) jcrNameToQName.get(jcrName);
+        if (qName == null) {
+            qName = NameFormat.parse(jcrName, this);
+            jcrNameToQName.put(jcrName, qName);
+        }
+        return qName;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public synchronized String getJCRName(QName qName)
+            throws NoPrefixDeclaredException {
+        String jcrName = (String) qnameToJCRName.get(qName);
+        if (jcrName == null) {
+            // DIFF JR: moved method from QName to NameFormat
+            jcrName = NameFormat.format(qName, this);
+            qnameToJCRName.put(qName, jcrName);
+        }
+        return jcrName;
+    }
+
+    /**
+     * @inheritDoc
+     * As currently paths are not cached, the call is delegated to
+     * {@link PathFormat#parse(String, NamespaceResolver)}.
+     */
+    public Path getQPath(String jcrPath) throws MalformedPathException {
+        return PathFormat.parse(jcrPath, this);
+    }
+
+    /**
+     * @inheritDoc
+     * As currently paths are not cached, the call is delegated to
+     * {@link PathFormat#format(Path, NamespaceResolver)}.
+     */
+    public String getJCRPath(Path qPath) throws NoPrefixDeclaredException {
+        return PathFormat.format(qPath, this);
+    }
+
+    /**
+     * Disposes this <code>CachingNamespaceResolver</code>.
+     */
+    public void dispose() {
+        base.removeListener(this);
+    }
+
+    //----------------------------------------------------< NamespaceListener >
+    /**
+     * @inheritDoc
+     */
+    public void namespaceAdded(String prefix, String uri) {
+        // since it is a new namespace there's no need to flush the
+        // cached mappings
+    }
+
+    /**
+     * @inheritDoc
+     * Invalidates all cached mappings.
+     */
+    public void namespaceRemapped(String oldPrefix, String newPrefix, String uri) {
+        qnameToJCRName.clear();
+        jcrNameToQName.clear();
+    }
+
+    /**
+     * @inheritDoc
+     * Invalidates all cached mappings.
+     */
+    public void namespaceRemoved(String uri) {
+        qnameToJCRName.clear();
+        jcrNameToQName.clear();
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/CachingNamespaceResolver.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.name;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.AbstractNamespaceResolver;
+import org.apache.jackrabbit.name.NamespaceListener;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.jcr2spi.SessionImpl;
+import org.apache.xerces.util.XMLChar;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Arrays;
+
+/**
+ * Manager for local session namespace mappings. This class is
+ * used by the {@link SessionImpl SessionImpl} class to implement
+ * the local namespace mapping functionality required by the JCR API.
+ * <p>
+ * This class holds a reference to the underlying global and persistent
+ * namespace registry (a {@link NamespaceRegistryImpl NamespaceRegistryImpl}
+ * instance) and keeps track of local namespace mappings added by the session.
+ * <p>
+ * The namespace resolution methods required by the
+ * {@link NamespaceResolver NamespaceResolver} are implemented by first
+ * looking up the local namespace mapping and then backing to the
+ * underlying namespace registry.
+ */
+public class LocalNamespaceMappings extends AbstractNamespaceResolver
+    implements NamespaceListener {
+
+    /** The underlying global and persistent namespace registry. */
+    private final NamespaceRegistryImpl nsReg;
+
+    /** Prefix to URI mappings of local namespaces. */
+    private final HashMap prefixToURI = new HashMap();
+
+    /** URI to prefix mappings of local namespaces. */
+    private final HashMap uriToPrefix = new HashMap();
+
+    /**
+     * Creates a local namespace manager with the given underlying
+     * namespace registry.
+     *
+     * @param nsReg namespace registry
+     */
+    public LocalNamespaceMappings(NamespaceRegistryImpl nsReg) {
+        this.nsReg = nsReg;
+        this.nsReg.addListener(this);
+    }
+
+    /**
+     * Rename a persistently registered namespace URI to the new prefix.
+     *
+     * @param prefix namespace prefix
+     * @param uri namespace URI
+     * @throws NamespaceException
+     * @throws RepositoryException
+     */
+    public void setNamespacePrefix(String prefix, String uri)
+            throws NamespaceException, RepositoryException {
+        if (prefix == null || uri == null) {
+            throw new IllegalArgumentException("prefix/uri can not be null");
+        }
+        if (QName.NS_EMPTY_PREFIX.equals(prefix)
+                || QName.NS_DEFAULT_URI.equals(uri)) {
+            throw new NamespaceException("default namespace is reserved and can not be changed");
+        }
+        // special case: xml namespace
+        if (uri.equals(QName.NS_XML_URI)) {
+            throw new NamespaceException("xml namespace is reserved and can not be changed.");
+        }
+        // special case: prefixes xml*
+        if (prefix.toLowerCase().startsWith(QName.NS_XML_PREFIX)) {
+            throw new NamespaceException("reserved prefix: " + prefix);
+        }
+        // check if the prefix is a valid XML prefix
+        if (!XMLChar.isValidNCName(prefix)) {
+            throw new NamespaceException("invalid prefix: " + prefix);
+        }
+
+        // verify that namespace exists (the following call will
+        // trigger a NamespaceException if it doesn't)
+        nsReg.getPrefix(uri);
+
+        // check new prefix for collision
+        if (Arrays.asList(getPrefixes()).contains(prefix)) {
+            // prefix is already in use
+            if (getURI(prefix).equals(uri)) {
+                // redundant mapping, silently ignore
+                return;
+            }
+            throw new NamespaceException("prefix already in use: " + prefix);
+        }
+
+        // check if namespace is already locally mapped
+        String oldPrefix = (String) uriToPrefix.get(uri);
+        if (oldPrefix != null) {
+            // remove old mapping
+            uriToPrefix.remove(uri);
+            prefixToURI.remove(oldPrefix);
+        }
+
+        // store new mapping
+        prefixToURI.put(prefix, uri);
+        uriToPrefix.put(uri, prefix);
+    }
+
+    /**
+     * Returns all prefixes currently mapped.
+     *
+     * @return an array holding all currently mapped prefixes
+     * @throws RepositoryException if an error occurs
+     */
+    public String[] getPrefixes() throws RepositoryException {
+        if (prefixToURI.isEmpty()) {
+            // shortcut
+            return nsReg.getPrefixes();
+        }
+
+        HashSet prefixes = new HashSet();
+        String[] uris = nsReg.getURIs();
+        for (int i = 0; i < uris.length; i++) {
+            // check local mapping
+            String prefix = (String) uriToPrefix.get(uris[i]);
+            if (prefix == null) {
+                // globally mapped
+                prefix = nsReg.getPrefix(uris[i]);
+            }
+            prefixes.add(prefix);
+        }
+
+        return (String[]) prefixes.toArray(new String[prefixes.size()]);
+    }
+
+    /**
+     * Disposes this <code>LocalNamespaceMappings</code>.
+     */
+    public void dispose() {
+        nsReg.removeListener(this);
+    }
+
+    //--------------------------------------------------< NamespaceResolver >---
+    /**
+     * {@inheritDoc}
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        if (prefixToURI.isEmpty()) {
+            // shortcut
+            return nsReg.getURI(prefix);
+        }
+        // check local mappings
+        String uri = (String) prefixToURI.get(prefix);
+        if (uri != null) {
+            return uri;
+        }
+
+        // check global mappings
+        uri = nsReg.getURI(prefix);
+        if (uri != null) {
+            // make sure global prefix is not hidden because of
+            // locally remapped uri
+            if (!uriToPrefix.containsKey(uri)) {
+                return uri;
+            }
+        }
+
+        throw new NamespaceException(prefix + ": unknown prefix");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        if (prefixToURI.isEmpty()) {
+            // shortcut
+            return nsReg.getPrefix(uri);
+        }
+
+        // check local mappings
+        String prefix = (String) uriToPrefix.get(uri);
+        if (prefix != null) {
+            return prefix;
+        }
+
+        // check global mappings
+        return nsReg.getPrefix(uri);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public QName getQName(String name)
+            throws IllegalNameException, UnknownPrefixException {
+        if (prefixToURI.isEmpty()) {
+            // shortcut
+            return nsReg.getQName(name);
+        }
+        try {
+            // first try registry, this might result in a wrong QName because
+            // of locally overlayed mappings
+            QName candidate = nsReg.getQName(name);
+            // make sure global prefix is not hidden because of
+            // locally remapped uri
+            if (!uriToPrefix.containsKey(candidate.getNamespaceURI())) {
+                return candidate;
+            }
+        } catch (UnknownPrefixException e) {
+            // try using local mappings
+        }
+        return super.getQName(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getJCRName(QName name)
+            throws NoPrefixDeclaredException {
+        if (uriToPrefix.isEmpty()) {
+            // shortcut
+            return nsReg.getJCRName(name);
+        }
+        if (uriToPrefix.containsKey(name.getNamespaceURI())) {
+            // locally remappped
+            return super.getJCRName(name);
+        } else {
+            // use global mapping
+            return nsReg.getJCRName(name);
+        }
+    }
+
+    //--------------------------------------------------< NamespaceListener >---
+    /**
+     * @inheritDoc
+     * This method gets called when a new namespace is registered in
+     * the global NamespaceRegistry. Overridden in order to check for/resolve
+     * collision of new global prefix with existing local prefix.
+     */
+    public void namespaceAdded(String prefix, String uri) {
+        if (prefixToURI.containsKey(prefix)) {
+            // the new global prefix is already in use locally;
+            // need to change it locally by appending underscore(s)
+            // in order to guarantee unambiguous mappings
+            String uniquePrefix = prefix + "_";
+            while (prefixToURI.containsKey(uniquePrefix)) {
+                uniquePrefix += "_";
+            }
+            // add new local mapping
+            prefixToURI.put(uniquePrefix, uri);
+            uriToPrefix.put(uri, uniquePrefix);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * This method gets called when an existing namespace is remapped to a new
+     * prefix in the global NamespaceRegistry. Overridden in order to check
+     * for/resolve collision of new global prefix with existing local prefix.
+     */
+    public void namespaceRemapped(String oldPrefix, String newPrefix, String uri) {
+        if (prefixToURI.containsKey(newPrefix)) {
+            // the new global prefix is already in use locally;
+            // check uri
+            if (uriToPrefix.containsKey(uri)) {
+                // since namespace is already remapped locally to
+                // a different prefix there's no collision
+                return;
+            }
+            // need to change enw prefix locally by appending underscore(s)
+            // in order to guarantee unambiguous mappings
+            String uniquePrefix = newPrefix + "_";
+            while (prefixToURI.containsKey(uniquePrefix)) {
+                uniquePrefix += "_";
+            }
+            // add new local mapping
+            prefixToURI.put(uniquePrefix, uri);
+            uriToPrefix.put(uri, uniquePrefix);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public void namespaceRemoved(String uri) {
+        if (uriToPrefix.containsKey(uri)) {
+            String prefix = (String)uriToPrefix.remove(uri);
+            prefixToURI.remove(prefix);
+        }
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/LocalNamespaceMappings.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.name;
+
+import org.apache.xerces.util.XMLChar;
+import org.apache.jackrabbit.name.AbstractNamespaceResolver;
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.QName;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.NamespaceException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.RepositoryException;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.HashSet;
+
+/**
+ * <code>NamespaceRegistryImpl</code>...
+ */
+public class NamespaceRegistryImpl extends AbstractNamespaceResolver implements NamespaceRegistry {
+
+    private static final HashSet reservedPrefixes = new HashSet();
+    private static final HashSet reservedURIs = new HashSet();
+
+    static {
+        // reserved prefixes
+        reservedPrefixes.add(QName.NS_XML_PREFIX);
+        reservedPrefixes.add(QName.NS_XMLNS_PREFIX);
+        // predefined (e.g. built-in) prefixes
+        reservedPrefixes.add(QName.NS_REP_PREFIX);
+        reservedPrefixes.add(QName.NS_JCR_PREFIX);
+        reservedPrefixes.add(QName.NS_NT_PREFIX);
+        reservedPrefixes.add(QName.NS_MIX_PREFIX);
+        reservedPrefixes.add(QName.NS_SV_PREFIX);
+        // reserved namespace URI's
+        reservedURIs.add(QName.NS_XML_URI);
+        reservedURIs.add(QName.NS_XMLNS_URI);
+        // predefined (e.g. built-in) namespace URI's
+        reservedURIs.add(QName.NS_REP_URI);
+        reservedURIs.add(QName.NS_JCR_URI);
+        reservedURIs.add(QName.NS_NT_URI);
+        reservedURIs.add(QName.NS_MIX_URI);
+        reservedURIs.add(QName.NS_SV_URI);
+    }
+
+    private final HashMap prefixToURI = new HashMap();
+    private final HashMap uriToPrefix = new HashMap();
+
+    private final CachingNamespaceResolver resolver;
+    private final NamespaceStorage storage;
+
+    public NamespaceRegistryImpl(NamespaceStorage storage, Properties nsValues) {
+        super(true); // enable listener support
+        resolver = new CachingNamespaceResolver(this, 1000);
+        this.storage = storage;
+	load(nsValues);
+    }
+
+    private void load(Properties nsValues) {
+        Enumeration prefixes = nsValues.propertyNames();
+        while (prefixes.hasMoreElements()) {
+            String prefix = (String) prefixes.nextElement();
+            if (!prefixToURI.containsKey(prefix)) {
+                String uri = nsValues.getProperty(prefix);
+                prefixToURI.put(prefix, uri);
+                uriToPrefix.put(uri, prefix);
+            }
+        }
+    }
+
+    /**
+     * @see NamespaceRegistry#registerNamespace(String, String)
+     */
+    public void registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, RepositoryException {
+        // TODO: UnsupportedRepositoryOperationException in Level1-Repository
+
+        // perform basic validation checks
+        if (prefix == null || uri == null) {
+            throw new IllegalArgumentException("prefix/uri can not be null");
+        }
+        if (QName.NS_EMPTY_PREFIX.equals(prefix) || QName.NS_DEFAULT_URI.equals(uri)) {
+            throw new NamespaceException("default namespace is reserved and can not be changed");
+        }
+        if (reservedURIs.contains(uri)) {
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri + ": reserved URI");
+        }
+        if (reservedPrefixes.contains(prefix)) {
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri + ": reserved prefix");
+        }
+        // special case: prefixes xml*
+        if (prefix.toLowerCase().startsWith(QName.NS_XML_PREFIX)) {
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri + ": reserved prefix");
+        }
+        // check if the prefix is a valid XML prefix
+        if (!XMLChar.isValidNCName(prefix)) {
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri + ": invalid prefix");
+        }
+
+        // check existing mappings
+        String oldPrefix = (String) uriToPrefix.get(uri);
+        if (prefix.equals(oldPrefix)) {
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri + ": mapping already exists");
+        }
+        if (prefixToURI.containsKey(prefix)) {
+            /**
+             * prevent remapping of existing prefixes because this would in effect
+             * remove the previously assigned namespace;
+             * as we can't guarantee that there are no references to this namespace
+             * (in names of nodes/properties/node types etc.) we simply don't allow it.
+             */
+            throw new NamespaceException("failed to register namespace "
+                + prefix + " -> " + uri
+                + ": remapping existing prefixes is not supported.");
+        }
+
+        // inform storage before mappings are added to maps and propagated to listeners
+        storage.registerNamespace(prefix, uri);
+
+        // remove old prefix mapping
+        if (oldPrefix != null) {
+            prefixToURI.remove(oldPrefix);
+            uriToPrefix.remove(uri);
+        }
+        // add new prefix mapping
+        prefixToURI.put(prefix, uri);
+        uriToPrefix.put(uri, prefix);
+
+        // notify listeners
+        if (oldPrefix != null) {
+            // remapped existing namespace uri to new prefix
+            notifyNamespaceRemapped(oldPrefix, prefix, uri);
+        } else {
+            // added new namespace uri mapped to prefix
+            notifyNamespaceAdded(prefix, uri);
+        }
+    }
+
+    /**
+     * @see NamespaceRegistry#unregisterNamespace(String)
+     */
+    public void unregisterNamespace(String prefix) throws NamespaceException, UnsupportedRepositoryOperationException, RepositoryException {
+        // TODO: UnsupportedRepositoryOperationException in Level1-Repository
+        if (reservedPrefixes.contains(prefix)) {
+            throw new NamespaceException("reserved prefix: " + prefix);
+        }
+        if (!prefixToURI.containsKey(prefix)) {
+            throw new NamespaceException("unknown prefix: " + prefix);
+        }
+
+        // inform storage before mappings are added to maps and propagated to listeners
+        storage.unregisterNamespace(prefixToURI.get(prefix).toString());
+
+        // update caches
+        String uri = prefixToURI.remove(prefix).toString();
+        uriToPrefix.remove(uri);
+
+        // notify listeners
+        notifyNamespaceRemoved(uri);
+    }
+
+    /**
+     * @see javax.jcr.NamespaceRegistry#getPrefixes()
+     */
+    public String[] getPrefixes() throws RepositoryException {
+        return (String[]) prefixToURI.keySet().toArray(new String[prefixToURI.keySet().size()]);
+
+    }
+
+    /**
+     * @see javax.jcr.NamespaceRegistry#getURIs()
+     */
+    public String[] getURIs() throws RepositoryException {
+        return (String[]) uriToPrefix.keySet().toArray(new String[uriToPrefix.keySet().size()]);
+
+    }
+
+    /**
+     * @see javax.jcr.NamespaceRegistry#getURI(String)
+     * @see org.apache.jackrabbit.name.NamespaceResolver#getURI(String)
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        String uri = (String) prefixToURI.get(prefix);
+        if (uri == null) {
+            throw new NamespaceException(prefix + ": is not a registered namespace prefix.");
+        }
+        return uri;
+    }
+
+    /**
+     * @see javax.jcr.NamespaceRegistry#getPrefix(String)
+     * @see org.apache.jackrabbit.name.NamespaceResolver#getPrefix(String)
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        String prefix = (String) uriToPrefix.get(uri);
+        if (prefix == null) {
+            throw new NamespaceException(uri + ": is not a registered namespace uri.");
+        }
+        return prefix;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.name.NamespaceResolver#getQName(String)
+     */
+    public QName getQName(String name)
+            throws IllegalNameException, UnknownPrefixException {
+        return resolver.getQName(name);
+    }
+
+    /**
+     * @see org.apache.jackrabbit.name.NamespaceResolver#getJCRName(QName)
+     */
+    public String getJCRName(QName name) throws NoPrefixDeclaredException {
+        return resolver.getJCRName(name);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceRegistryImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.name;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+
+/**
+ * <code>NamespaceStorage</code>...
+ */
+public interface NamespaceStorage {
+
+    public void	registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException;
+
+    public void unregisterNamespace(String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException;
+
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/name/NamespaceStorage.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,627 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.nodetype;
+
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.value.QValue;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.PropertyType;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+
+import java.util.Stack;
+import java.util.Map;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * <code>DefinitionValidator</code>...
+ */
+// DIFF JR: class added (methods extracted from NodeTypeRegistry)
+class DefinitionValidator {
+
+    private static Logger log = LoggerFactory.getLogger(DefinitionValidator.class);
+
+    private final NodeTypeRegistry ntRegistry;
+    private final NamespaceRegistry nsRegistry;
+
+
+    DefinitionValidator(NodeTypeRegistry ntRegistry, NamespaceRegistry nsRegistry) {
+        this.ntRegistry = ntRegistry;
+        this.nsRegistry = nsRegistry;
+    }
+
+    /**
+     * Validate each QNodeTypeDefinition present in the given collection.
+     *
+     * @param ntDefs
+     * @param validatedDefs
+     * @param checkAutoCreatePropHasDefault flag used to disable checking that auto-created properties
+     * have a default value; this check has to be disabled while validating
+     * built-in node types because there are properties defined in built-in
+     * node types which are auto-created but don't have a fixed default value
+     * that can be exposed in a property definition because it is
+     * system-generated (e.g. jcr:primaryType in nt:base).
+     * // TODO FIXME
+     * @return Map mapping the definition to the resulting effective nodetype
+     * @throws InvalidNodeTypeDefException
+     * @throws RepositoryException
+     */
+    public Map validateNodeTypeDefs(Collection ntDefs, Map validatedDefs, boolean checkAutoCreatePropHasDefault) throws InvalidNodeTypeDefException, RepositoryException {
+        // tmp. map containing names/defs of validated nodetypes
+        Map validDefs = new HashMap(validatedDefs);
+        // map of nodetype definitions and effective nodetypes to be registered
+        Map ntMap = new HashMap();
+        ArrayList list = new ArrayList(ntDefs);
+
+        // iterate over definitions until there are no more definitions with
+        // unresolved (i.e. unregistered) dependencies or an error occurs;
+
+        int count = -1;  // number of validated nt's per iteration
+        while (list.size() > 0 && count != 0) {
+            count = 0;
+            Iterator iterator = list.iterator();
+            while (iterator.hasNext()) {
+                QNodeTypeDefinition ntd = (QNodeTypeDefinition) iterator.next();
+                // check if definition has unresolved dependencies
+                // DIFF JR: cannot compared to 'registered' nodetypes since registr. is performed later on
+                if (validDefs.keySet().containsAll(ntd.getDependencies())) {
+                    EffectiveNodeType ent = validateNodeTypeDef(ntd, validDefs, checkAutoCreatePropHasDefault);
+                    // keep track of validated definitions and eff. nodetypes
+                    if (!validDefs.containsKey(ntd.getQName())) {
+                        validDefs.put(ntd.getQName(), ntd);
+                    }
+                    ntMap.put(ntd, ent);
+                    // 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(((QNodeTypeDefinition) iterator.next()).getQName());
+                msg.append(" ");
+            }
+            log.error(msg.toString());
+            throw new InvalidNodeTypeDefException(msg.toString());
+        }
+        return ntMap;
+    }
+
+    /**
+     *
+     * @param ntDef
+     * @param validatedDefs Map of qualified nodetype names and nodetype definitions
+     * that are known to be valid or are already registered. This map is used to
+     * validated dependencies and check for circular inheritance
+     * @param checkAutoCreatePropHasDefault flag used to disable checking that auto-created properties
+     * have a default value; this check has to be disabled while validating
+     * built-in node types because there are properties defined in built-in
+     * node types which are auto-created but don't have a fixed default value
+     * that can be exposed in a property definition because it is
+     * system-generated (e.g. jcr:primaryType in nt:base).
+     * // TODO FIXME
+     * @return
+     * @throws InvalidNodeTypeDefException
+     * @throws RepositoryException
+     */
+    public EffectiveNodeTypeImpl validateNodeTypeDef(QNodeTypeDefinition ntDef, Map validatedDefs, boolean checkAutoCreatePropHasDefault)
+            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
+         */
+        EffectiveNodeTypeImpl ent = null;
+
+        QName name = ntDef.getQName();
+        if (name == null) {
+            String msg = "no name specified";
+            log.debug(msg);
+            throw new InvalidNodeTypeDefException(msg);
+        }
+        checkNamespace(name);
+
+        // validate supertypes
+        QName[] supertypes = ntDef.getSupertypes();
+        if (supertypes != null && supertypes.length > 0) {
+            for (int i = 0; i < supertypes.length; i++) {
+                checkNamespace(supertypes[i]);
+                /**
+                 * simple check for infinite recursion
+                 * (won't trap recursion on a deeper inheritance level)
+                 */
+                if (name.equals(supertypes[i])) {
+                    String msg = "[" + name + "] invalid supertype: "
+                            + supertypes[i] + " (infinite recursion))";
+                    log.debug(msg);
+                    throw new InvalidNodeTypeDefException(msg);
+                }
+                // DIFF JR: compare to given nt-name set and not to registered nodetypes
+                if (!validatedDefs.containsKey(supertypes[i])) {
+                    String msg = "[" + name + "] invalid supertype: "
+                            + supertypes[i];
+                    log.debug(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, validatedDefs);
+        }
+
+        /**
+         * note that infinite recursion through inheritance is automatically
+         * being checked by the following call to getEffectiveNodeType()
+         * as it's impossible to register a 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 {
+                // DIFF JR: use extra method that does not compare to registered nts
+                EffectiveNodeType est = ntRegistry.getEffectiveNodeType(supertypes, validatedDefs);
+                // make sure that all primary types except nt:base extend from nt:base
+                if (!ntDef.isMixin() && !QName.NT_BASE.equals(ntDef.getQName())
+                        && !est.includesNodeType(QName.NT_BASE)) {
+                    String msg = "[" + name + "] all primary node types except"
+                        + " nt:base itself must be (directly or indirectly) derived from nt:base";
+                    log.debug(msg);
+                    throw new InvalidNodeTypeDefException(msg);
+                }
+            } catch (NodeTypeConflictException ntce) {
+                String msg = "[" + name + "] failed to validate supertypes";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, ntce);
+            } catch (NoSuchNodeTypeException nsnte) {
+                String msg = "[" + name + "] failed to validate supertypes";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, nsnte);
+            }
+        } else {
+            // no supertypes specified: has to be either a mixin type or nt:base
+            if (!ntDef.isMixin() && !QName.NT_BASE.equals(ntDef.getQName())) {
+                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(ntDef.getPrimaryItemName());
+
+        // validate property definitions
+        QPropertyDefinition[] pda = ntDef.getPropertyDefs();
+        for (int i = 0; i < pda.length; i++) {
+            QPropertyDefinition pd = pda[i];
+            /**
+             * sanity check:
+             * make sure declaring node type matches name of node type definition
+             */
+            if (!name.equals(pd.getDeclaringNodeType())) {
+                String msg = "[" + name + "#" + pd.getQName() + "] invalid declaring node type specified";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            checkNamespace(pd.getQName());
+            // check that auto-created properties specify a name
+            if (pd.definesResidual() && pd.isAutoCreated()) {
+                String msg = "[" + name + "#" + pd.getQName() + "] auto-created properties must specify a name";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            // check that auto-created properties specify a type
+            if (pd.getRequiredType() == PropertyType.UNDEFINED && pd.isAutoCreated()) {
+                String msg = "[" + name + "#" + pd.getQName() + "] auto-created properties must specify a type";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            /**
+             * check default values:
+             * make sure type of value is consistent with required property type
+             */
+            // DIFF JACKRABBIT: default internal values are built from the
+            // required type, thus check for match with pd.getRequiredType is redundant
+            QValue[] defVals = getQValues(pd);
+            if (defVals == null || defVals.length == 0) {
+                // no default values specified
+                if (checkAutoCreatePropHasDefault) {
+                    // auto-created properties must have a default value
+                    if (pd.isAutoCreated()) {
+                        String msg = "[" + name + "#" + pd.getQName() + "] auto-created property must have a default value";
+                        log.debug(msg);
+                        throw new InvalidNodeTypeDefException(msg);
+                    }
+                }
+            }
+
+            // check that default values satisfy value constraints
+            ValueConstraint.checkValueConstraints(pd, defVals);
+
+            /**
+             * ReferenceConstraint:
+             * the specified node type must be registered, with one notable
+             * exception: the node type just being registered
+             */
+            String[] constraints = pd.getValueConstraints();
+            if (constraints != null && constraints.length > 0) {
+
+                if (pd.getRequiredType() == PropertyType.REFERENCE) {
+                    for (int j = 0; j < constraints.length; j++) {
+                        QName ntName = QName.valueOf(constraints[j]);
+                        // DIFF JR: compare to given ntd map and not registered nts only
+                        if (!name.equals(ntName) && !validatedDefs.containsKey(ntName)) {
+                            String msg = "[" + name + "#" + pd.getQName()
+                                    + "] invalid REFERENCE value constraint '"
+                                    + ntName + "' (unknown node type)";
+                            log.debug(msg);
+                            throw new InvalidNodeTypeDefException(msg);
+                        }
+                    }
+                }
+            }
+        }
+
+        // validate child-node definitions
+        QNodeDefinition[] cnda = ntDef.getChildNodeDefs();
+        for (int i = 0; i < cnda.length; i++) {
+            QNodeDefinition cnd = cnda[i];
+            /**
+             * sanity check:
+             * make sure declaring node type matches name of node type definition
+             */
+            if (!name.equals(cnd.getDeclaringNodeType())) {
+                String msg = "[" + name + "#" + cnd.getQName()
+                        + "] invalid declaring node type specified";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            checkNamespace(cnd.getQName());
+            // check that auto-created child-nodes specify a name
+            if (cnd.definesResidual() && cnd.isAutoCreated()) {
+                String msg = "[" + name + "#" + cnd.getQName()
+                        + "] auto-created child-nodes must specify a name";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            // check that auto-created child-nodes specify a default primary type
+            if (cnd.getDefaultPrimaryType() == null
+                    && cnd.isAutoCreated()) {
+                String msg = "[" + name + "#" + cnd.getQName()
+                        + "] auto-created child-nodes must specify a default primary type";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg);
+            }
+            // check default primary type
+            QName dpt = cnd.getDefaultPrimaryType();
+            checkNamespace(dpt);
+            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) && !validatedDefs.containsKey(dpt)) {
+                    String msg = "[" + name + "#" + cnd.getQName()
+                            + "] invalid default primary type '" + dpt + "'";
+                    log.debug(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 = ntRegistry.getEffectiveNodeType(new QName[] {dpt}, validatedDefs);
+                    } else {
+                        /**
+                         * the default primary type is identical with the node
+                         * type just being registered; we have to instantiate it
+                         * 'manually'
+                         */
+                        ent = EffectiveNodeTypeImpl.create(ntRegistry, ntDef, validatedDefs);
+                        defaultENT = ent;
+                    }
+                    if (cnd.isAutoCreated()) {
+                        /**
+                         * 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, validatedDefs);
+                    }
+                } catch (NodeTypeConflictException ntce) {
+                    String msg = "[" + name + "#" + cnd.getQName()
+                            + "] failed to validate default primary type";
+                    log.debug(msg);
+                    throw new InvalidNodeTypeDefException(msg, ntce);
+                } catch (NoSuchNodeTypeException nsnte) {
+                    String msg = "[" + name + "#" + cnd.getQName()
+                            + "] failed to validate default primary type";
+                    log.debug(msg);
+                    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];
+                    checkNamespace(rpt);
+                    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) && !validatedDefs.containsKey(rpt)) {
+                        String msg = "[" + name + "#" + cnd.getQName()
+                                + "] invalid required primary type: " + rpt;
+                        log.debug(msg);
+                        throw new InvalidNodeTypeDefException(msg);
+                    }
+                    /**
+                     * check if default primary type satisfies the required
+                     * primary type constraint
+                     */
+                    if (defaultENT != null && !defaultENT.includesNodeType(rpt)) {
+                        String msg = "[" + name + "#" + cnd.getQName()
+                                + "] default primary type does not satisfy required primary type constraint "
+                                + rpt;
+                        log.debug(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) {
+                            ntRegistry.getEffectiveNodeType(new QName[] {rpt}, validatedDefs);
+                        } else {
+                            /**
+                             * the required primary type is identical with the
+                             * node type just being registered; we have to
+                             * instantiate it 'manually'
+                             */
+                            if (ent == null) {
+                                ent = EffectiveNodeTypeImpl.create(ntRegistry, ntDef, validatedDefs);
+                            }
+                        }
+                    } catch (NodeTypeConflictException ntce) {
+                        String msg = "[" + name + "#" + cnd.getQName()
+                                + "] failed to validate required primary type constraint";
+                        log.debug(msg);
+                        throw new InvalidNodeTypeDefException(msg, ntce);
+                    } catch (NoSuchNodeTypeException nsnte) {
+                        String msg = "[" + name + "#" + cnd.getQName()
+                                + "] failed to validate required primary type constraint";
+                        log.debug(msg);
+                        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 = EffectiveNodeTypeImpl.create(ntRegistry, ntDef, validatedDefs);
+            } catch (NodeTypeConflictException ntce) {
+                String msg = "[" + name + "] failed to resolve node type definition";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, ntce);
+            } catch (NoSuchNodeTypeException nsnte) {
+                String msg = "[" + name + "] failed to resolve node type definition";
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, nsnte);
+            }
+        }
+        return ent;
+    }
+
+    /**
+     *
+     * @param supertypes
+     * @param inheritanceChain
+     * @param ntdMap
+     * @throws InvalidNodeTypeDefException
+     * @throws RepositoryException
+     */
+    private void checkForCircularInheritance(QName[] supertypes, Stack inheritanceChain, Map ntdMap)
+        throws InvalidNodeTypeDefException, RepositoryException {
+        for (int i = 0; i < supertypes.length; i++) {
+            QName stName = supertypes[i];
+            int pos = inheritanceChain.lastIndexOf(stName);
+            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(stName);
+                throw new InvalidNodeTypeDefException("circular inheritance detected: " + buf.toString());
+            }
+
+            if (ntdMap.containsKey(stName)) {
+                QName[] sta = ((QNodeTypeDefinition)ntdMap.get(stName)).getSupertypes();
+                if (sta != null && sta.length > 0) {
+                    // check recursively
+                    inheritanceChain.push(stName);
+                    checkForCircularInheritance(sta, inheritanceChain, ntdMap);
+                    inheritanceChain.pop();
+                }
+            } else {
+                throw new InvalidNodeTypeDefException("Unknown supertype: " + stName);
+            }
+        }
+    }
+
+    /**
+     *
+     * @param childNodeENT
+     * @param definingParentNTs
+     * @param ntdMap
+     * @throws InvalidNodeTypeDefException
+     */
+    private void checkForCircularNodeAutoCreation(EffectiveNodeType childNodeENT,
+                                                  Stack definingParentNTs, Map ntdMap)
+        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());
+            }
+        }
+
+        QNodeDefinition[] 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);
+                    EffectiveNodeType ent = ntRegistry.getEffectiveNodeType(new QName[] {dnt}, ntdMap);
+                    checkForCircularNodeAutoCreation(ent, definingParentNTs, ntdMap);
+                    definingParentNTs.pop();
+                }
+            } catch (NoSuchNodeTypeException e) {
+                String msg = definingNT + " defines invalid default node type for child node " + nodeDefs[i].getQName();
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, e);
+            } catch (NodeTypeConflictException e) {
+                String msg = definingNT + " defines invalid default node type for child node " + nodeDefs[i].getQName();
+                log.debug(msg);
+                throw new InvalidNodeTypeDefException(msg, e);
+            }
+        }
+    }
+
+    /**
+     * Utility method for verifying that the namespace of a <code>QName</code>
+     * is registered; a <code>null</code> argument is silently ignored.
+     * @param name name whose namespace is to be checked
+     * @throws RepositoryException if the namespace of the given name is not
+     *                             registered or if an unspecified error occured
+     */
+    private void checkNamespace(QName name) throws RepositoryException {
+        if (name != null) {
+            // make sure namespace uri denotes a registered namespace
+            nsRegistry.getPrefix(name.getNamespaceURI());
+        }
+    }
+
+    private static QValue[] getQValues(QPropertyDefinition propDef) throws RepositoryException {
+        int reqType = propDef.getRequiredType();
+        // if no default values are specified, need to return null.
+        QValue[] ivs = null;
+        if (reqType == PropertyType.BINARY) {
+            InputStream[] dfv = propDef.getDefaultValuesAsStream();
+            if (dfv != null) {
+                ivs = new QValue[dfv.length];
+                for (int i = 0; i < dfv.length; i++) {
+                    try {
+                        ivs[i] = QValue.create(dfv[i]);
+                    } catch (IOException e) {
+                        String msg = "[" + propDef.getQName() + "] error while reading binary default values.";
+                        throw new RepositoryException(msg);
+                    }
+                }
+            }
+        } else {
+            String[] dfv = propDef.getDefaultValues();
+            if (dfv != null) {
+                ivs = new QValue[dfv.length];
+                if (reqType == PropertyType.UNDEFINED) {
+                    reqType = PropertyType.STRING;
+                }
+                for (int i = 0; i < dfv.length; i++) {
+                    ivs[i] = QValue.create(dfv[i], reqType);
+                }
+            }
+        }
+        return ivs;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.nodetype;
+
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+
+/**
+ * <code>EffectiveNodeType</code>...
+ */
+public interface EffectiveNodeType {
+
+    public QName[] getAllNodeTypes();
+
+    public QName[] getInheritedNodeTypes();
+
+    public QName[] getMergedNodeTypes();
+
+    /**
+     * Determines whether this effective node type representation includes
+     * (either through inheritance or aggregation) the given node type.
+     *
+     * @param nodeTypeName name of node type
+     * @return <code>true</code> if the given node type is included, otherwise
+     *         <code>false</code>
+     */
+    public boolean includesNodeType(QName nodeTypeName);
+    
+    public QNodeDefinition[] getAllNodeDefs();
+
+    public QPropertyDefinition[] getAllPropDefs();
+
+    public QNodeDefinition[] getAutoCreateNodeDefs();
+
+    public QPropertyDefinition[] getAutoCreatePropDefs();
+
+    public QPropertyDefinition[] getMandatoryPropDefs();
+
+    public QNodeDefinition[] getMandatoryNodeDefs();
+
+    /**
+     * Returns the applicable child node definition for a child node with the
+     * specified name and node type.
+     *
+     * @param name
+     * @param nodeTypeName
+     * @return
+     * @throws NoSuchNodeTypeException
+     * @throws ConstraintViolationException if no applicable child node definition
+     *                                      could be found
+     */
+    public QNodeDefinition getApplicableNodeDefinition(QName name, QName nodeTypeName)
+            throws NoSuchNodeTypeException, ConstraintViolationException;
+
+    /**
+     * Returns the applicable property definition for a property with the
+     * specified name, type and multiValued characteristic. If there more than
+     * one applicable definitions then the following rules are applied:
+     * <ul>
+     * <li>named definitions are preferred to residual definitions</li>
+     * <li>definitions with specific required type are preferred to definitions
+     * with required type UNDEFINED</li>
+     * </ul>
+     *
+     * @param name
+     * @param type
+     * @param multiValued
+     * @return
+     * @throws ConstraintViolationException if no applicable property definition
+     *                                      could be found
+     */
+    public QPropertyDefinition getApplicablePropertyDefinition(QName name, int type,
+                                            boolean multiValued)
+            throws ConstraintViolationException;
+
+    /**
+     * Returns the applicable property definition for a property with the
+     * specified name and type. The multiValued flag is not taken into account
+     * in the selection algorithm. Other than
+     * <code>{@link #getApplicablePropertyDefinition(QName, int, boolean)}</code>
+     * this method does not take the multiValued flag into account in the
+     * selection algorithm. If there more than one applicable definitions then
+     * the following rules are applied:
+     * <ul>
+     * <li>named definitions are preferred to residual definitions</li>
+     * <li>definitions with specific required type are preferred to definitions
+     * with required type UNDEFINED</li>
+     * <li>single-value definitions are preferred to multiple-value definitions</li>
+     * </ul>
+     *
+     * @param name
+     * @param type
+     * @return
+     * @throws ConstraintViolationException if no applicable property definition
+     *                                      could be found
+     */
+    public QPropertyDefinition getApplicablePropertyDefinition(QName name, int type)
+            throws ConstraintViolationException;
+
+    /**
+     * @param name
+     * @throws ConstraintViolationException
+     */
+    public void checkAddNodeConstraints(QName name)
+            throws ConstraintViolationException;
+
+    /**
+     * @param name
+     * @param nodeTypeName
+     * @throws ConstraintViolationException
+     * @throws NoSuchNodeTypeException
+     */
+    public void checkAddNodeConstraints(QName name, QName nodeTypeName)
+            throws ConstraintViolationException, NoSuchNodeTypeException;
+
+    /**
+     * @param name
+     * @throws ConstraintViolationException
+     */
+    public void checkRemoveItemConstraints(QName name) throws ConstraintViolationException;
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeType.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeTypeCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeTypeCache.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeTypeCache.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeTypeCache.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.nodetype;
+
+import org.apache.jackrabbit.jcr2spi.util.Dumpable;
+import org.apache.jackrabbit.name.QName;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * <code>EffectiveNodeTypeCache</code> ...
+ */
+class EffectiveNodeTypeCache implements Cloneable, Dumpable {
+    /**
+     * ordered set of keys
+     */
+    final TreeSet sortedKeys;
+    /**
+     * cache of pre-built aggregations of node types
+     */
+    final HashMap aggregates;
+
+    EffectiveNodeTypeCache() {
+        sortedKeys = new TreeSet();
+        aggregates = new HashMap();
+    }
+
+    void put(EffectiveNodeType ent) {
+        // we define the weight as the total number of included node types
+        // (through aggregation and inheritance)
+        int weight = ent.getAllNodeTypes().length;
+        // the effective node type is identified by the list of merged
+        // (i.e. aggregated) node types
+        WeightedKey k = new WeightedKey(ent.getMergedNodeTypes(), weight);
+        aggregates.put(k, ent);
+        sortedKeys.add(k);
+    }
+
+    boolean contains(QName[] ntNames) {
+        return aggregates.containsKey(new WeightedKey(ntNames));
+    }
+
+    boolean contains(WeightedKey key) {
+        return aggregates.containsKey(key);
+    }
+
+    EffectiveNodeType get(QName[] ntNames) {
+        return (EffectiveNodeType) aggregates.get(new WeightedKey(ntNames));
+    }
+
+    EffectiveNodeType get(WeightedKey key) {
+        return (EffectiveNodeType) aggregates.get(key);
+    }
+
+    EffectiveNodeType remove(QName[] ntNames) {
+        return remove(new WeightedKey(ntNames));
+    }
+
+    EffectiveNodeType remove(WeightedKey key) {
+        EffectiveNodeType removed = (EffectiveNodeType) aggregates.remove(key);
+        if (removed != null) {
+            // remove index entry
+
+            // FIXME: can't simply call TreeSet.remove(key) because the entry
+            // in sortedKeys might have a different weight and would thus
+            // not be found
+            Iterator iter = sortedKeys.iterator();
+            while (iter.hasNext()) {
+                WeightedKey k = (WeightedKey) iter.next();
+                // WeightedKey.equals(Object) ignores the weight
+                if (key.equals(k)) {
+                    sortedKeys.remove(k);
+                    break;
+                }
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Returns an iterator over the keys. The order of the returned keys is:
+     * <ol>
+     * <li>descending weight</li>
+     * <li>ascending key (i.e. unique identifier of aggregate)</li>
+     * </ol>
+     *
+     * @see WeightedKey#compareTo
+     */
+    Iterator keyIterator() {
+        return sortedKeys.iterator();
+    }
+
+    /**
+     * Returns the set of keys.
+     *
+     * @return the set of keys.
+     */
+    Set keySet() {
+        return Collections.unmodifiableSet(sortedKeys);
+    }
+
+    //-------------------------------------------< java.lang.Object overrides >
+    public Object clone() {
+        EffectiveNodeTypeCache clone = new EffectiveNodeTypeCache();
+        clone.sortedKeys.addAll(sortedKeys);
+        clone.aggregates.putAll(aggregates);
+        return clone;
+    }
+
+    //-------------------------------------------------------------< Dumpable >
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(PrintStream ps) {
+        ps.println("EffectiveNodeTypeCache (" + this + ")");
+        ps.println();
+        ps.println("EffectiveNodeTypes in cache:");
+        ps.println();
+        Iterator iter = sortedKeys.iterator();
+        while (iter.hasNext()) {
+            WeightedKey k = (WeightedKey) iter.next();
+            //EffectiveNodeType ent = (EffectiveNodeType) aggregates.get(k);
+            ps.println(k);
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+    /**
+     * A <code>WeightedKey</code> uniquely identifies
+     * a combination (i.e. an aggregation) of one or more node types.
+     * The weight is an indicator for the cost involved in building such an
+     * aggregate (e.g. an aggregation of multiple complex node types with deep
+     * inheritance trees is more costly to build/validate than an agreggation
+     * of two very simple node types with just one property definition each).
+     * <p/>
+     * A very simple (and not very accurate) approximation of the weight would
+     * be the number of explicitly aggregated node types (ignoring inheritance
+     * and complexity of each involved node type). A better approximation would
+     * be the number of <b>all</b>, explicitly and implicitly (note that
+     * inheritance is also an aggregation) aggregated node types.
+     * <p/>
+     * The more accurate the weight definition, the more efficient is the
+     * the building of new aggregates.
+     * <p/>
+     * It is important to note that the weight is not part of the key value,
+     * i.e. it is not considered by the <code>hashCode()</code> and
+     * <code>equals(Object)</code> methods. It does however affect the order
+     * of <code>WeightedKey</code> instances. See
+     * <code>{@link #compareTo(Object)}</code> for more information.
+     * <p/>
+     * Let's assume we have an aggregation of node types named "b", "a" and "c".
+     * Its key would be "[a, b, c]" and the weight 3 (using the simple
+     * approximation).
+     */
+    static class WeightedKey implements Comparable {
+
+        /**
+         * array of node type names, sorted in ascending order
+         */
+        private final QName[] names;
+
+        /**
+         * the weight of this key
+         */
+        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;
+            names = new QName[ntNames.length];
+            System.arraycopy(ntNames, 0, names, 0, names.length);
+            Arrays.sort(names);
+        }
+
+        /**
+         * @param ntNames
+         */
+        WeightedKey(Collection ntNames) {
+            this(ntNames, ntNames.size());
+        }
+
+        /**
+         * @param ntNames
+         * @param weight
+         */
+        WeightedKey(Collection ntNames, int weight) {
+            this((QName[]) ntNames.toArray(new QName[ntNames.size()]), weight);
+        }
+
+        /**
+         * @return the weight of this key
+         */
+        int getWeight() {
+            return weight;
+        }
+
+        /**
+         * @return the node type names of this key
+         */
+        QName[] getNames() {
+            return names;
+        }
+
+        boolean contains(WeightedKey otherKey) {
+            Set tmp = new HashSet(Arrays.asList(names));
+            for (int i = 0; i < otherKey.names.length; i++) {
+                if (!tmp.contains(otherKey.names[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        WeightedKey subtract(WeightedKey otherKey) {
+            Set tmp = new HashSet(Arrays.asList(names));
+            tmp.removeAll(Arrays.asList(otherKey.names));
+            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 the result of the comparison
+         */
+        public int compareTo(Object o) {
+            WeightedKey other = (WeightedKey) o;
+
+            // compare weights
+            if (weight > other.weight) {
+                return -1;
+            } else if (weight < other.weight) {
+                return 1;
+            }
+
+            // compare arrays of names
+            int len1 = names.length;
+            int len2 = other.names.length;
+            int len = Math.min(len1, len2);
+
+            for (int i = 0; i < len; i++) {
+                QName name1 = names[i];
+                QName name2 = other.names[i];
+                int result = name1.compareTo(name2);
+                if (result != 0) {
+                    return result;
+                }
+            }
+            return len1 - len2;
+        }
+
+        public int hashCode() {
+            int h = 17;
+            // ignore weight
+            for (int i = 0; i < names.length; i++) {
+                h *= 37;
+                h += names[i].hashCode();
+            }
+            return h;
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof WeightedKey) {
+                WeightedKey other = (WeightedKey) obj;
+                // ignore weight
+                return Arrays.equals(names, other.names);
+            }
+            return false;
+        }
+
+        public String toString() {
+            return Arrays.asList(names).toString() + " (" + weight + ")";
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/EffectiveNodeTypeCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

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



Mime
View raw message