View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.commons.schema.utils;
21  
22  import org.apache.ws.commons.schema.constants.Constants;
23  
24  import org.w3c.dom.NamedNodeMap;
25  import org.w3c.dom.Node;
26  
27  import javax.xml.namespace.NamespaceContext;
28  
29  import java.lang.reflect.Method;
30  import java.util.*;
31  
32  /**
33   * Implementation of {@link NamespaceContext}, which is based on a DOM node.
34   */
35  public class NodeNamespaceContext implements NamespacePrefixList {
36      private static final String NODE_NAMSPACE_CONTEXT = NamespacePrefixList.class.getName();
37      private static final Collection XML_NS_PREFIX_COLLECTION = Collections.singletonList(Constants.XML_NS_PREFIX);
38      private static final Collection XMLNS_ATTRIBUTE_COLLECTION = Collections.singletonList(Constants.XMLNS_ATTRIBUTE);
39      
40      static Method getUserData;
41      static Method setUserData;
42      static {
43          try {
44              Class cls = Class.forName("org.w3c.dom.UserDataHandler", false, Node.class.getClassLoader());
45              getUserData = Node.class.getMethod("getUserData", new Class[]{String.class});
46              setUserData = Node.class.getMethod("setUserData", new Class[]{String.class, Object.class, cls});
47          } catch (Throwable e) {
48              getUserData = null;
49              setUserData = null;
50          }
51      }
52      
53      
54      
55      private final Map declarations;
56      private String[] prefixes;
57  
58      /**
59       * Creates a new instance with the given nodes context.
60       */
61      private NodeNamespaceContext(Map decls) {
62          declarations = decls;
63      }
64      
65      public static NodeNamespaceContext getNamespaceContext(Node pNode) {
66          if (getUserData != null) {
67              try {
68                  NodeNamespaceContext ctx = (NodeNamespaceContext)getUserData.invoke(pNode, new Object[] {NODE_NAMSPACE_CONTEXT});
69                  if (ctx == null) {
70                      Map declarations = new HashMap();
71          
72                      Node parentNode = pNode.getParentNode();
73                      if (parentNode != null) {
74                          NodeNamespaceContext parent = 
75                              (NodeNamespaceContext)getUserData.invoke(parentNode, new Object[] {NODE_NAMSPACE_CONTEXT});
76                          if (parent == null) {
77                              parent = getNamespaceContext(parentNode);
78                          }
79                          declarations.putAll(parent.declarations);
80                      }
81                      
82                      NamedNodeMap map = pNode.getAttributes();
83                      if (map != null) {
84                          for (int i = 0; i < map.getLength(); i++) {
85                              Node attr = map.item(i);
86                              final String uri = attr.getNamespaceURI();
87                              if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(uri)) {
88                                  String localName = attr.getLocalName();
89                                  String prefix = Constants.XMLNS_ATTRIBUTE.equals(localName) ? Constants.DEFAULT_NS_PREFIX : localName;
90                                  declarations.put(prefix, attr.getNodeValue());
91                              }
92                          }
93                      }
94                      ctx = new NodeNamespaceContext(declarations);
95                      setUserData.invoke(pNode, new Object[] {NODE_NAMSPACE_CONTEXT, ctx, null});
96                  }
97                  return ctx;
98              } catch (Throwable t) {
99                  //ignore.  DOM level 2 implementation would not have the getUserData stuff.   
100                 //Thus, fall back to the old, slower method.
101             }
102         }
103         
104         final Map declarations = new HashMap();
105         new PrefixCollector(){
106             protected void declare(String pPrefix, String pNamespaceURI) {
107                 declarations.put(pPrefix, pNamespaceURI);
108             }
109         }.searchAllPrefixDeclarations(pNode);
110         return new NodeNamespaceContext(declarations);
111     }
112 
113     public String getNamespaceURI(String pPrefix) {
114         if (pPrefix == null) {
115             throw new IllegalArgumentException("The prefix must not be null.");
116         }
117         if (Constants.XML_NS_PREFIX.equals(pPrefix)) {
118             return Constants.XML_NS_URI;
119         }
120         if (Constants.XMLNS_ATTRIBUTE.equals(pPrefix)) {
121             return Constants.XMLNS_ATTRIBUTE_NS_URI;
122         }
123         final String uri = (String) declarations.get(pPrefix);
124         return uri == null ? Constants.NULL_NS_URI : uri;
125     }
126 
127     public String getPrefix(String pNamespaceURI) {
128         if (pNamespaceURI == null) {
129             throw new IllegalArgumentException("The namespace URI must not be null.");
130         }
131         if (Constants.XML_NS_URI.equals(pNamespaceURI)) {
132             return Constants.XML_NS_PREFIX;
133         }
134         if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(pNamespaceURI)) {
135             return Constants.XMLNS_ATTRIBUTE;
136         }
137         for (Iterator iter = declarations.entrySet().iterator();  iter.hasNext();  ) {
138             Map.Entry entry = (Map.Entry) iter.next();
139             if (pNamespaceURI.equals(entry.getValue())) {
140                 return (String) entry.getKey();
141             }
142         }
143         return null;
144     }
145 
146     public Iterator getPrefixes(String pNamespaceURI) {
147         if (pNamespaceURI == null) {
148             throw new IllegalArgumentException("The namespace URI must not be null.");
149         }
150         if (Constants.XML_NS_URI.equals(pNamespaceURI)) {
151             return XML_NS_PREFIX_COLLECTION.iterator();
152         }
153         if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(pNamespaceURI)) {
154             return XMLNS_ATTRIBUTE_COLLECTION.iterator();
155         }
156         final List list = new ArrayList();
157         for (Iterator iter = declarations.entrySet().iterator();  iter.hasNext();  ) {
158             Map.Entry entry = (Map.Entry) iter.next();
159             if (pNamespaceURI.equals(entry.getValue())) {
160                 list.add(entry.getKey());
161             }
162         }
163         return list.iterator();
164     }
165 
166     public String[] getDeclaredPrefixes() {
167         if (prefixes == null) {
168             Collection keys = declarations.keySet();
169             prefixes = (String[]) keys.toArray(new String[keys.size()]);
170         }
171         return prefixes;
172     }
173 }