Package org.apache.jackrabbit.oak.jcr.session

Source Code of org.apache.jackrabbit.oak.jcr.session.SessionNamespaces

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.jcr.session;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.jackrabbit.util.XMLChar;

import com.google.common.collect.Maps;

/**
* <code>SessionNamespaces</code> implements namespace handling on the JCR
* Session level. That is, it maintains a map of session local namespace
* re-mappings and takes a snapshot of the namespace registry when initialized
* (see JCR 2.0 specification, section 3.5.1).
*/
class SessionNamespaces {

    /**
     * Local namespace remappings. Prefixes as keys and namespace URIs as values.
     * <p/>
     * This map is only accessed from synchronized methods (see
     * <a href="https://issues.apache.org/jira/browse/JCR-1793">JCR-1793</a>).
     */
    private final Map<String, String> namespaces;

    /**
     * A snapshot of the namespace registry when these SessionNamespaces
     * are first accessed. Key: prefix, value: uri
     */
    private Map<String, String> snapshotPrefixUri;
   
    /**
     * A snapshot of the namespace registry when these SessionNamespaces
     * are first accessed. Key: uri, value: prefix
     */
    private Map<String, String> snapshotUriPrefix;


    private final SessionContext sessionContext;

    SessionNamespaces(@Nonnull SessionContext sessionContext) {
        this.namespaces = Maps.newHashMap();
        this.sessionContext = checkNotNull(sessionContext);
    }

    // The code below was initially copied from JCR Commons AbstractSession, but
    // provides information the "hasRemappings" information

    /**
     * @see Session#setNamespacePrefix(String, String)
     */
    void setNamespacePrefix(String prefix, String uri) throws RepositoryException {
        init();
        if (prefix == null) {
            throw new IllegalArgumentException("Prefix must not be null");
        } else if (uri == null) {
            throw new IllegalArgumentException("Namespace must not be null");
        } else if (prefix.isEmpty()) {
            throw new NamespaceException(
                    "Empty prefix is reserved and can not be remapped");
        } else if (uri.isEmpty()) {
            throw new NamespaceException(
                    "Default namespace is reserved and can not be remapped");
        } else if (prefix.toLowerCase(Locale.ENGLISH).startsWith("xml")) {
            throw new NamespaceException(
                    "XML prefixes are reserved: " + prefix);
        } else if (!XMLChar.isValidNCName(prefix)) {
            throw new NamespaceException(
                    "Prefix is not a valid XML NCName: " + prefix);
        }

        synchronized (namespaces) {
            // Remove existing mapping for the given prefix
            namespaces.remove(prefix);

            // Remove existing mapping(s) for the given URI
            Set<String> prefixes = new HashSet<String>();
            for (Map.Entry<String, String> entry : namespaces.entrySet()) {
                if (entry.getValue().equals(uri)) {
                    prefixes.add(entry.getKey());
                }
            }
            namespaces.keySet().removeAll(prefixes);

            // Add the new mapping
            namespaces.put(prefix, uri);
        }

        if (snapshotPrefixUri.containsKey(prefix)) {
            // make sure we have a prefix in case an existing
            // namespace uri is re-mapped
            getNamespacePrefix(snapshotPrefixUri.get(prefix));
        }
    }

    /**
     * @see Session#getNamespacePrefixes()
     */
    String[] getNamespacePrefixes() throws RepositoryException {
        init();
        synchronized (namespaces) {
            if (namespaces.isEmpty()) {
                Set<String> prefixes = snapshotPrefixUri.keySet();
                return prefixes.toArray(new String[prefixes.size()]);
            }
        }
        Set<String> uris = new HashSet<String>();
        uris.addAll(snapshotPrefixUri.values());
        synchronized (namespaces) {
            // Add namespace uris only visible to session
            uris.addAll(namespaces.values());
        }
        Set<String> prefixes = new HashSet<String>();
        for (String uri : uris) {
            prefixes.add(getNamespacePrefix(uri));
        }
        return prefixes.toArray(new String[prefixes.size()]);
    }

    /**
     * @see Session#getNamespaceURI(String)
     */
    String getNamespaceURI(String prefix) throws RepositoryException {
        init();
        synchronized (namespaces) {
            String uri = namespaces.get(prefix);

            if (uri == null) {
                // Not in local mappings, try snapshot ones
                uri = snapshotPrefixUri.get(prefix);
                if (uri == null) {
                    // Not in snapshot mappings, try the global ones
                    uri = getNamespaceRegistry().getURI(prefix);
                    if (namespaces.containsValue(uri)) {
                        // The global URI is locally mapped to some other prefix,
                        // so there are no mappings for this prefix
                        throw new NamespaceException("Namespace not found: " + prefix);
                    }
                }
            }

            return uri;
        }
    }

    /**
     * @see Session#getNamespacePrefix(String)
     */
    String getNamespacePrefix(String uri) throws RepositoryException {
        init();
        synchronized (namespaces) {
            if (namespaces.size() > 0) {
                for (Map.Entry<String, String> entry : namespaces.entrySet()) {
                    if (entry.getValue().equals(uri)) {
                        return entry.getKey();
                    }
                }
            }

            // try snapshot
            String prefix = snapshotUriPrefix.get(uri);
            if (prefix != null && !namespaces.containsKey(prefix)) {
                return prefix;
            }

            // The following throws an exception if the URI is not found, that's OK
            prefix = getNamespaceRegistry().getPrefix(uri);

            // Generate a new prefix if the global mapping is already taken
            String base = prefix;
            for (int i = 2; namespaces.containsKey(prefix); i++) {
                prefix = base + i;
            }

            if (!base.equals(prefix)) {
                namespaces.put(prefix, uri);
            }
            return prefix;
        }
    }

    /**
     * @return the session local namespaces that were remapped.
     */
    public Map<String, String> getSessionLocalMappings() {
        synchronized (namespaces) {
            if (namespaces.isEmpty()) {
                return Collections.emptyMap();
            }
            return new HashMap<String, String>(namespaces);
        }
    }

    /**
     * Clears the re-mapped namespaces map.
     */
    void clear() {
        synchronized (namespaces) {
            namespaces.clear();
        }
    }

    private NamespaceRegistry getNamespaceRegistry() {
        return sessionContext.getWorkspace().getNamespaceRegistry();
    }

    private void init() throws RepositoryException {
        if (snapshotPrefixUri == null) {
            NamespaceRegistry registry = getNamespaceRegistry();
            Map<String, String> prefixUri = new HashMap<String, String>();
            Map<String, String> uriPrefix = new HashMap<String, String>();
            for (String prefix : registry.getPrefixes()) {
                String uri = registry.getURI(prefix);
                prefixUri.put(prefix, uri);
                uriPrefix.put(uri, prefix);
            }
            snapshotPrefixUri = prefixUri;
            snapshotUriPrefix = uriPrefix;
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.oak.jcr.session.SessionNamespaces

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.