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.HashMap;
20  import java.util.Map;
21  
22  import javax.xml.parsers.DocumentBuilder;
23  import javax.xml.parsers.DocumentBuilderFactory;
24  import javax.xml.parsers.ParserConfigurationException;
25  
26  import net.sf.xolite.XMLSerializable;
27  import net.sf.xolite.XMLSerializeException;
28  import net.sf.xolite.XMLSerializer;
29  
30  import org.w3c.dom.Document;
31  import org.w3c.dom.Element;
32  import org.w3c.dom.Node;
33  import org.w3c.dom.Text;
34  
35  
36  /**
37   * Serializer transforming a java object to a DOM tree.
38   */
39  public class DomXMLSerializer implements XMLSerializer {
40  
41  
42      private static final String NAMESPACE_DEFINITION_URI = "http://www.w3.org/2000/xmlns/";
43  
44      private DocumentBuilder builder;
45      private Document currentDocument;
46      private Node currentNode;
47      private Map<String, String> prefixMappings;
48      private Map<Object, Object> customObjects;
49  
50  
51      public DomXMLSerializer() {
52          try {
53              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
54              factory.setNamespaceAware(true);
55              builder = factory.newDocumentBuilder();
56          } catch (ParserConfigurationException pce) {
57              throw new IllegalStateException("Cannot create DocumentBuilder: " + pce);
58          }
59          prefixMappings = new HashMap<String, String>();
60      }
61  
62  
63      public DomXMLSerializer(DocumentBuilder docBuilder) {
64          if (docBuilder == null) throw new IllegalArgumentException("docBuilder cannot be null");
65          builder = docBuilder;
66          prefixMappings = new HashMap<String, String>();
67      }
68  
69  
70      public void clearPrefixMapping() {
71          prefixMappings.clear();
72      }
73  
74  
75      public Document serializeToDOM(XMLSerializable src) throws XMLSerializeException {
76          startDocument();
77          src.serialize(this);
78          endDocument();
79          return currentDocument;
80      }
81  
82  
83      public void startDocument() throws XMLSerializeException {
84          Document doc = builder.newDocument();
85          currentDocument = doc;
86          currentNode = doc;
87      }
88  
89  
90      public void endDocument() throws XMLSerializeException {
91          if (currentNode != currentDocument)
92              throw new XMLSerializeException(
93                      "Not all the document nodes are ended (with endElement(..) notification) remaining = " + currentNode);
94          currentNode = null;
95      }
96  
97  
98      public Document getDocument() {
99          return currentDocument;
100     }
101 
102 
103     private String getPrefix(String uri, String localName) throws XMLSerializeException {
104         String prefix;
105         if (uri == null) {
106             prefix = null;
107         } else {
108             prefix = prefixMappings.get(uri);
109             if (prefix == null)
110                 throw new XMLSerializeException("No prefix defined for URI '" + uri + "' of element <" + localName + ">");
111         }
112         return prefix;
113     }
114 
115 
116     private void ensurePrefixMappingDefined(String prefix, String uri, Element baseElement) {
117         if ((prefix != null) && (uri != null)) {
118             Element elem = baseElement;
119             String attrName = (prefix.equals("")) ? "xmlns" : "xmlns:" + prefix;
120             String attrVal;
121             Node parent;
122             boolean prefixNotDefined = true;
123             while (prefixNotDefined) {
124                 if ((elem != baseElement) && prefix.equals(elem.getPrefix()) && uri.equals(elem.getNamespaceURI())) {
125                     prefixNotDefined = false;
126                     break;
127                 } else {
128                     // seach namespace definitions
129                     attrVal = elem.getAttribute(attrName);
130                     if ((attrVal != null) && (attrVal.equals(uri))) {
131                         prefixNotDefined = false;
132                         break;
133                     }
134                 }
135                 parent = elem.getParentNode();
136                 if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)) elem = (Element) parent;
137                 else break;
138             }
139             if (prefixNotDefined) {
140                 baseElement.setAttributeNS(NAMESPACE_DEFINITION_URI, attrName, uri);
141             }
142         }
143     }
144 
145 
146     // ------------------------ XMLSerializer interface implementation -------------------------
147 
148 
149     public void startPrefixMapping(String prefix, String namespaceUri) throws XMLSerializeException {
150         if (!prefixMappings.containsKey(namespaceUri)) prefixMappings.put(namespaceUri, prefix);
151     }
152 
153 
154     public void startElement(String uri, String localName) throws XMLSerializeException {
155         if ((currentDocument == null) || (currentNode == null)) throw new XMLSerializeException("Document not started");
156         String prefix = getPrefix(uri, localName);
157         String qName = ((prefix == null) || (prefix.equals(""))) ? localName : prefix + ":" + localName;
158         Element el = currentDocument.createElementNS(uri, qName);
159         currentNode.appendChild(el);
160         ensurePrefixMappingDefined(prefix, uri, el);
161         currentNode = el;
162     }
163 
164 
165     public void attribute(String name, String value) throws XMLSerializeException {
166         attribute(null, name, value);
167     }
168 
169 
170     public void attribute(String uri, String localName, String value) throws XMLSerializeException {
171         if ((currentDocument == null) || (currentNode == null)) throw new XMLSerializeException("Document not started");
172         if (!(currentNode instanceof Element))
173             throw new XMLSerializeException("You can only add attributes inside an Element");
174         Element current = (Element) currentNode;
175         String prefix = getPrefix(uri, localName);
176         String qName = ((prefix == null) || (prefix.equals(""))) ? localName : prefix + ":" + localName;
177         current.setAttributeNS(uri, qName, value);
178         ensurePrefixMappingDefined(prefix, uri, current);
179     }
180 
181 
182     public void characters(String text) throws XMLSerializeException {
183         if ((currentDocument == null) || (currentNode == null)) throw new XMLSerializeException("Document not started");
184         if (!(currentNode instanceof Element)) throw new XMLSerializeException("You can only add text inside an Element");
185         Text txt = currentDocument.createTextNode(text);
186         currentNode.appendChild(txt);
187     }
188 
189 
190     public void charactersMultiLine(String text) throws XMLSerializeException {
191         // Not implemented here, as formatting is difficult to do inside dom.
192         characters(text);
193     }
194 
195 
196     public void endElement(String uri, String localName) throws XMLSerializeException {
197         if ((currentDocument == null) || (currentNode == null)) throw new XMLSerializeException("Document not started");
198         if ((uri != null) && !uri.equals(currentNode.getNamespaceURI()))
199             throw new XMLSerializeException("Current node namespace URI is not <" + uri + "> but <"
200                     + currentNode.getNamespaceURI() + ">");
201         if (!localName.equals(currentNode.getLocalName()))
202             throw new XMLSerializeException("Current node is not <" + localName + "> but <" + currentNode.getNodeName() + ">");
203         Node parent = currentNode.getParentNode();
204         if (parent == null) throw new XMLSerializeException("Error while colsing node");
205         currentNode = parent;
206     }
207 
208 
209     public void simpleElement(String namespaceUri, String localName, String text) throws XMLSerializeException {
210         startElement(namespaceUri, localName);
211         characters(text);
212         endElement(namespaceUri, localName);
213     }
214 
215 
216     public Object getCustomObject(Object key) {
217         return (customObjects == null) ? null : customObjects.get(key);
218     }
219 
220 
221     public void putCustomObject(Object key, Object value) {
222         if (customObjects == null) customObjects = new HashMap<Object, Object>();
223         customObjects.put(key, value);
224     }
225 
226 
227 }