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 */
017package org.apache.commons.jxpath.ri;
018
019import java.io.Serializable;
020import java.util.HashMap;
021
022import org.apache.commons.jxpath.Pointer;
023import org.apache.commons.jxpath.ri.model.NodeIterator;
024import org.apache.commons.jxpath.ri.model.NodePointer;
025
026/**
027 * Namespace resolver for {@link JXPathContextReferenceImpl}.
028 *
029 * @author Dmitri Plotnikov
030 * @version $Revision: 918623 $ $Date: 2010-03-03 19:47:47 +0100 (Mi, 03 Mär 2010) $
031 */
032public 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(final 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 = currentPointer.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}