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.camel.builder.xml;
018
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.Iterator;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Set;
025import javax.xml.namespace.NamespaceContext;
026import javax.xml.xpath.XPathFactory;
027
028import org.apache.camel.spi.NamespaceAware;
029import org.apache.camel.util.CastUtils;
030
031/**
032 * An implementation of {@link NamespaceContext} which uses a simple Map where
033 * the keys are the prefixes and the values are the URIs
034 */
035public class DefaultNamespaceContext implements NamespaceContext, NamespaceAware {
036
037    private final Map<String, String> map;
038    private final NamespaceContext parent;
039
040    public DefaultNamespaceContext() {
041        this(XPathFactory.newInstance());
042    }
043
044    public DefaultNamespaceContext(XPathFactory factory) {
045        this.parent = factory.newXPath().getNamespaceContext();
046        this.map = new HashMap<String, String>();
047    }
048
049    public DefaultNamespaceContext(NamespaceContext parent, Map<String, String> map) {
050        this.parent = parent;
051        this.map = map;
052    }
053
054    /**
055     * A helper method to make it easy to create newly populated instances
056     */
057    public DefaultNamespaceContext add(String prefix, String uri) {
058        map.put(prefix, uri);
059        return this;
060    }
061
062    public String getNamespaceURI(String prefix) {
063        String answer = map.get(prefix);
064        if (answer == null && parent != null) {
065            return parent.getNamespaceURI(prefix);
066        }
067        return answer;
068    }
069
070    public String getPrefix(String namespaceURI) {
071        for (Entry<String, String> entry : map.entrySet()) {
072            if (namespaceURI.equals(entry.getValue())) {
073                return entry.getKey();
074            }
075        }
076        if (parent != null) {
077            return parent.getPrefix(namespaceURI);
078        }
079        return null;
080    }
081
082    public Iterator<String> getPrefixes(String namespaceURI) {
083        Set<String> set = new HashSet<String>();
084        for (Entry<String, String> entry : map.entrySet()) {
085            if (namespaceURI.equals(entry.getValue())) {
086                set.add(entry.getKey());
087            }
088        }
089        if (parent != null) {
090            Iterator<String> iter = CastUtils.cast(parent.getPrefixes(namespaceURI));
091            while (iter.hasNext()) {
092                set.add(iter.next());
093            }
094        }
095        return set.iterator();
096    }
097
098    public void setNamespaces(Map<String, String> namespaces) {
099        map.putAll(namespaces);
100    }
101
102    public Map<String, String> getNamespaces() {
103        return map;
104    }
105
106    /**
107     * toString() implementation that outputs the namespace mappings with the following format: "[me: {prefix -> value}, {prefix -> value}], [parent: {prefix -> value}, {prefix -> value}].
108     * Recurses up the parent's chain.
109     */
110    @Override
111    public String toString() {
112        StringBuilder sb = new StringBuilder("[me: ");
113        for (Entry<String, String> nsEntry : map.entrySet()) {
114            sb.append("{" + nsEntry.getKey() + " -> " + nsEntry.getValue() + "},");
115        }
116        if (!map.isEmpty()) {
117            // remove the last comma
118            sb.deleteCharAt(sb.length() - 1);
119        }
120        sb.append("]");
121
122        // Get the parent's namespace mappings
123        if (parent != null) {
124            sb.append(", [parent: ");
125            sb.append(parent.toString());
126            sb.append("]");
127        }
128        return sb.toString();
129    }
130
131}