View Javadoc

1   /*-------------------------------------------------------------------------
2    Copyright 2006 Olivier Berlanger
3   
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7   
8    http://www.apache.org/licenses/LICENSE-2.0
9   
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15   -------------------------------------------------------------------------*/
16  package net.sf.xolite.dom;
17  
18  
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.List;
22  
23  import javax.xml.namespace.NamespaceContext;
24  
25  import net.sf.xolite.XMLParseException;
26  import net.sf.xolite.XMLSerializable;
27  import net.sf.xolite.impl.BaseXMLEventParser;
28  import net.sf.xolite.impl.MapPrefixResolver;
29  import net.sf.xolite.utils.RootHolder;
30  
31  import org.w3c.dom.Attr;
32  import org.w3c.dom.Document;
33  import org.w3c.dom.DocumentType;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.NamedNodeMap;
36  import org.w3c.dom.Node;
37  import org.w3c.dom.NodeList;
38  import org.w3c.dom.Text;
39  
40  
41  /**
42   * A <code>XMLEventParser</code> implementation using a DOM tree as input.
43   */
44  public class DomXMLEventParser extends BaseXMLEventParser {
45  
46      /** Current parsed XML element. */
47      private Element currentElement;
48  
49  
50      /**
51       * Build a <code>DomEventParser</code>.
52       */
53      public DomXMLEventParser() {
54      }
55  
56  
57      /**
58       * 'Parse' the given DOM Document to java objects. <br>
59       * This method will succeed only if a factory knowing the class of the root element is defined.
60       * 
61       * @param src
62       *            source DOM Document.
63       * @throws XMLParseException
64       *             if the source contains an invalid XML document.
65       */
66      public XMLSerializable parse(Document src) throws XMLParseException {
67          RootHolder holder = new RootHolder();
68          parse(src, holder);
69          return holder.getRoot();
70      }
71  
72  
73      /**
74       * 'Parse' the given DOM Document using the given <code>XMLSerializable</code> as root handler.
75       * 
76       * @param src
77       *            source DOM Document.
78       * @param rootHandler
79       *            the root handler.
80       * @throws XMLParseException
81       *             if the source contains an invalid DOM document.
82       */
83      public void parse(Document src, XMLSerializable rootHandler) throws XMLParseException {
84          if (src == null) throw new IllegalArgumentException("InputSource cannot be null");
85          if (rootHandler == null) throw new IllegalArgumentException("Root SaxHandler cannot be null");
86          try {
87              setup(rootHandler);
88              parseImpl(src.getDocumentElement());
89              tearDown();
90          } catch (Exception e) {
91              transformAndThrowException(e);
92          }
93      }
94  
95  
96      /**
97       * Clean state and avoid keeping dangling references to internal objects.
98       */
99      @Override
100     protected void tearDown() {
101         currentElement = null;
102         super.tearDown();
103     }
104 
105 
106     private void parseImpl(Element elem) throws Exception {
107         currentElement = elem;
108         startElementImpl(elem.getNamespaceURI(), elem.getLocalName());
109         NodeList childNodes = elem.getChildNodes();
110         int len = childNodes.getLength();
111         Node child;
112         for (int i = 0; i < len; i++) {
113             child = childNodes.item(i);
114             if (child.getNodeType() == Node.ELEMENT_NODE) {
115                 parseImpl((Element) child);
116             }
117         }
118         currentElement = elem;
119         endElementImpl(elem.getNamespaceURI(), elem.getLocalName());
120     }
121 
122 
123     /**
124      * Get namespace URI corresponding to the given prefix. Note: the mapped namespace set depends on the current parsed node.
125      */
126     @Override
127     public String getNamespaceURI(String prefix) {
128         return DomUtils.getDefinedUri(currentElement, prefix);
129     }
130 
131 
132     /**
133      * Get the first perfix mapped to the given namespace.
134      * 
135      * @see javax.xml.namespace.NamespaceContext#getPrefix(java.lang.String)
136      */
137     @Override
138     public String getPrefix(String namespaceURI) {
139         return DomUtils.getDefinedPerfix(currentElement, namespaceURI);
140     }
141 
142 
143     /**
144      * Get all the prefix mapped to the given namespace.
145      * 
146      * @see javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String)
147      */
148     @Override
149     public Iterator<String> getPrefixes(String namespaceURI) {
150         return DomUtils.getDefinedPerfixes(currentElement, namespaceURI).iterator();
151     }
152 
153 
154     // ------------------------ XMLEventParser interface implementation -----------------------------------------------
155 
156     @Override
157     public NamespaceContext getCurrentDefinedNamespaces() {
158         MapPrefixResolver prefixMapping = new MapPrefixResolver();
159         String prefix;
160         Element elem = currentElement;
161         NamedNodeMap attrs;
162         int len;
163         Attr a;
164         String attrName;
165         int attrNameLen;
166         Node parent;
167         while (elem != null) {
168             // add any namespace definitions
169             attrs = elem.getAttributes();
170             len = (attrs == null) ? 0 : attrs.getLength();
171             for (int i = 0; i < len; i++) {
172                 a = (Attr) attrs.item(i);
173                 attrName = a.getName();
174                 if (attrName.startsWith("xmlns")) {
175                     attrNameLen = attrName.length();
176                     if (attrNameLen == 5) prefix = "";
177                     else if ((attrNameLen > 6) && (attrName.charAt(5) == ':')) prefix = attrName.substring(6);
178                     else prefix = null;
179                     if (prefix != null) prefixMapping.addPrefixMapping(prefix, a.getValue());
180                 }
181             }
182             parent = elem.getParentNode();
183             if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)) elem = (Element) parent;
184             else elem = null;
185         }
186         return prefixMapping;
187     }
188 
189 
190     @Override
191     public XMLSerializable parseElement(String uri, String localName) throws XMLParseException {
192         XMLSerializable child = getFactory().createObject(uri, localName, this);
193         delegateParsingTo(child);
194         return child;
195     }
196 
197 
198     public String getElementText() {
199         if (currentElement == null) throw new IllegalStateException("No characters available");
200         String text = null;
201         NodeList childNodes = currentElement.getChildNodes();
202         int len = childNodes.getLength();
203         Node child;
204         String nodeText;
205         for (int i = 0; i < len; i++) {
206             child = childNodes.item(i);
207             if (child.getNodeType() == Node.TEXT_NODE) {
208                 nodeText = ((Text) child).getData();
209                 if (text == null) text = nodeText;
210                 else text += nodeText;
211             }
212         }
213         return text;
214     }
215 
216 
217     // --------------------------------- Exception management -----------------------------------
218 
219     @Override
220     protected void addLocationInfo(XMLParseException xpe) {
221         if (currentElement != null) {
222             xpe.setSource(getSourceDescription());
223         }
224     }
225 
226 
227     public String getSourceDescription() {
228         StringBuffer sb = new StringBuffer("DOM Document");
229         Document doc = (currentElement == null) ? null : currentElement.getOwnerDocument();
230         DocumentType docType = (doc == null) ? null : doc.getDoctype();
231         if (docType != null) sb.append(docType.getPublicId());
232         return sb.toString();
233     }
234 
235 
236     // ------------------------- Attributes management -------------------------------------------
237 
238     public String getAttributeValue(String attrName) throws XMLParseException {
239         return getAttributeValueNS(null, attrName);
240     }
241 
242 
243     public String getAttributeValueNS(String attrNamespaceURI, String attrName) throws XMLParseException {
244         if (currentElement == null) throw new XMLParseException(
245                 "getAttributeValue(..) can only be called from startElement(..) method");
246         Attr attrib = (attrNamespaceURI == null) ? currentElement.getAttributeNode(attrName) : currentElement.getAttributeNodeNS(
247                 attrNamespaceURI, attrName);
248         return (attrib == null) ? null : attrib.getValue();
249     }
250 
251 
252     public Iterator<String> getAttributeNamespaceIterator() throws XMLParseException {
253         if (currentElement == null) throw new XMLParseException(
254                 "getAttributeNameIterator(..) can only be called from startElement(..) method");
255         List<String> uris = new ArrayList<String>();
256         NamedNodeMap attrMap = currentElement.getAttributes();
257         int len = attrMap.getLength();
258         for (int i = 0; i < len; i++) {
259             Node n = attrMap.item(i);
260             String uri = n.getNamespaceURI();
261             if (!uris.contains(uri)) uris.add(uri);
262         }
263         return uris.iterator();
264     }
265 
266 
267     public Iterator<String> getAttributeNameIterator(String attrNamespaceURI) throws XMLParseException {
268         if (currentElement == null) throw new XMLParseException(
269                 "getAttributeNameIterator(..) can only be called from startElement(..) method");
270         NamedNodeMap attrMap = currentElement.getAttributes();
271         return new AttributeNameIterator(attrNamespaceURI, attrMap);
272     }
273 
274     // -------------------- Inner Attribute Name Iterator implementation -------------------------------------------
275 
276     /**
277      * Iterator on the attribute names of a DOM element.
278      */
279     static class AttributeNameIterator implements Iterator<String> {
280 
281         private String namespaceURI;
282         private NamedNodeMap attrMap;
283         private int nbrAttr;
284         private int index;
285 
286 
287         AttributeNameIterator(String uri, NamedNodeMap attrs) {
288             namespaceURI = uri;
289             attrMap = attrs;
290             index = 0;
291             nbrAttr = (attrMap == null) ? 0 : attrMap.getLength();
292             checkNextAttibuteNamespace();
293         }
294 
295 
296         private void checkNextAttibuteNamespace() {
297             boolean namespaceOK;
298             while (index < nbrAttr) {
299                 Attr attribute = (Attr) attrMap.item(index);
300                 if (namespaceURI == null) namespaceOK = (attribute.getNamespaceURI() == null);
301                 else namespaceOK = namespaceURI.equals(attribute.getNamespaceURI());
302                 if (namespaceOK) break;
303                 else index++;
304             }
305         }
306 
307 
308         public boolean hasNext() {
309             return index < nbrAttr;
310         }
311 
312 
313         public String next() {
314             Attr attribute = (Attr) attrMap.item(index);
315             String name = attribute.getLocalName();
316             index++;
317             checkNextAttibuteNamespace();
318             return name;
319         }
320 
321 
322         public void remove() {
323             throw new UnsupportedOperationException("Cannot remove attribute");
324         }
325 
326     }
327 
328 }