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    }