    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.ri;
    018
    019    import java.io.Serializable;
    020    import java.util.HashMap;
    021
    022    import org.apache.commons.jxpath.Pointer;
    023    import org.apache.commons.jxpath.ri.model.NodeIterator;
    024    import org.apache.commons.jxpath.ri.model.NodePointer;
    025
    026    /**
    027     * Namespace resolver for {@link JXPathContextReferenceImpl}.
    028     *
    029     * @author Dmitri Plotnikov
    030     * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $
    031     */
    032    public class NamespaceResolver implements Cloneable, Serializable {
    033        private static final long serialVersionUID = 1085590057838651311L;
    034
    035        /** Parent NamespaceResolver */
    036        protected final NamespaceResolver parent;
    037        /** namespace map */
    038        protected HashMap namespaceMap = new HashMap();
    039        /** reverse lookup map */
    040        protected HashMap reverseMap = new HashMap();
    041        /** pointer */
    042        protected NodePointer pointer;
    043        private boolean sealed;
    044
    045        /**
    046         * Find the namespace prefix for the specified namespace URI and NodePointer.
    047         * @param pointer location
    048         * @param namespaceURI to check
    049         * @return prefix if found
    050         * @since JXPath 1.3
    051         */
    052        protected static String getPrefix(NodePointer pointer, String namespaceURI) {
    053            NodePointer currentPointer = pointer;
    054            while (currentPointer != null) {
    055                NodeIterator ni = currentPointer.namespaceIterator();
    056                for (int position = 1; ni != null && ni.setPosition(position); position++) {
    057                    NodePointer nsPointer = ni.getNodePointer();
    058                    String uri = nsPointer.getNamespaceURI();
    059                    if (uri.equals(namespaceURI)) {
    060                        String prefix = nsPointer.getName().getName();
    061                        if (!"".equals(prefix)) {
    062                            return prefix;
    063                        }
    064                    }
    065                }
    066                currentPointer = pointer.getParent();
    067            }
    068            return null;
    069        }
    070
    071        /**
    072         * Create a new NamespaceResolver.
    073         */
    074        public NamespaceResolver() {
    075            this(null);
    076        }
    077
    078        /**
    079         * Create a new NamespaceResolver.
    080         * @param parent NamespaceResolver
    081         */
    082        public NamespaceResolver(NamespaceResolver parent) {
    083            this.parent = parent;
    084        }
    085
    086        /**
    087         * Registers a namespace prefix.
    088         *
    089         * @param prefix A namespace prefix
    090         * @param namespaceURI A URI for that prefix
    091         */
    092        public synchronized void registerNamespace(String prefix, String namespaceURI) {
    093            if (isSealed()) {
    094                throw new IllegalStateException(
    095                        "Cannot register namespaces on a sealed NamespaceResolver");
    096            }
    097            namespaceMap.put(prefix, namespaceURI);
    098            reverseMap.put(namespaceURI, prefix);
    099        }
    100
    101        /**
    102         * Register a namespace for the expression context.
    103         * @param pointer the Pointer to set.
    104         */
    105        public void setNamespaceContextPointer(NodePointer pointer) {
    106            this.pointer = pointer;
    107        }
    108
    109        /**
    110         * Get the namespace context pointer.
    111         * @return Pointer
    112         */
    113        public Pointer getNamespaceContextPointer() {
    114            if (pointer == null && parent != null) {
    115                return parent.getNamespaceContextPointer();
    116            }
    117            return pointer;
    118        }
    119
    120        /**
    121         * Given a prefix, returns a registered namespace URI. If the requested
    122         * prefix was not defined explicitly using the registerNamespace method,
    123         * JXPathContext will then check the context node to see if the prefix is
    124         * defined there. See
    125         * {@link #setNamespaceContextPointer(NodePointer) setNamespaceContextPointer}.
    126         *
    127         * @param prefix The namespace prefix to look up
    128         * @return namespace URI or null if the prefix is undefined.
    129         */
    130        public synchronized String getNamespaceURI(String prefix) {
    131            String uri = getExternallyRegisteredNamespaceURI(prefix);
    132            return uri == null && pointer != null ? pointer.getNamespaceURI(prefix)
    133                    : uri;
    134        }
    135
    136        /**
    137         * Given a prefix, returns an externally registered namespace URI.
    138         *
    139         * @param prefix The namespace prefix to look up
    140         * @return namespace URI or null if the prefix is undefined.
    141         * @since JXPath 1.3
    142         */
    143         protected synchronized String getExternallyRegisteredNamespaceURI(
    144                String prefix) {
    145            String uri = (String) namespaceMap.get(prefix);
    146            return uri == null && parent != null ? parent
    147                    .getExternallyRegisteredNamespaceURI(prefix) : uri;
    148        }
    149
    150        /**
    151         * Get the prefix associated with the specifed namespace URI.
    152         * @param namespaceURI the ns URI to check.
    153         * @return String prefix
    154         */
    155        public synchronized String getPrefix(String namespaceURI) {
    156            String prefix = getExternallyRegisteredPrefix(namespaceURI);
    157            return prefix == null && pointer != null ? getPrefix(pointer,
    158                    namespaceURI) : prefix;
    159        }
    160
    161        /**
    162         * Get the nearest prefix found that matches an externally-registered namespace.
    163         * @param namespaceURI the ns URI to check.
    164         * @return String prefix if found.
    165         * @since JXPath 1.3
    166         */
    167        protected synchronized String getExternallyRegisteredPrefix(String namespaceURI) {
    168            String prefix = (String) reverseMap.get(namespaceURI);
    169            return prefix == null && parent != null ? parent
    170                    .getExternallyRegisteredPrefix(namespaceURI) : prefix;
    171        }
    172
    173        /**
    174         * Learn whether this NamespaceResolver has been sealed.
    175         * @return boolean
    176         */
    177        public boolean isSealed() {
    178            return sealed;
    179        }
    180
    181        /**
    182         * Seal this {@link NamespaceResolver}.
    183         */
    184        public void seal() {
    185            sealed = true;
    186            if (parent != null) {
    187                parent.seal();
    188            }
    189        }
    190
    191        public Object clone() {
    192            try {
    193                NamespaceResolver result = (NamespaceResolver) super.clone();
    194                result.sealed = false;
    195                return result;
    196            }
    197            catch (CloneNotSupportedException e) {
    198                // Of course, it's supported.
    199                e.printStackTrace();
    200                return null;
    201            }
    202        }
    203    }