1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.backsource.utils.xml;
21 import java.io.Reader;
22 import java.io.StringReader;
23 import java.io.StringWriter;
24 import java.io.BufferedReader;
25 import java.io.InputStream;
26 import java.io.File;
27 import java.io.FileReader;
28 import java.io.IOException;
29
30 import javax.xml.parsers.*;
31 import javax.xml.transform.TransformerFactory;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.SourceLocator;
34 import javax.xml.transform.TransformerException;
35 import javax.xml.transform.stream.StreamSource;
36 import javax.xml.transform.stream.StreamResult;
37 import javax.xml.transform.OutputKeys;
38 import javax.xml.transform.dom.DOMSource;
39
40 import org.w3c.dom.Node;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.DocumentType;
43 import org.w3c.dom.DocumentFragment;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.NodeList;
46 import org.xml.sax.SAXException;
47 import org.xml.sax.InputSource;
48 import org.xml.sax.XMLReader;
49 /***
50 * <p>Util to handle stuff relating to documents and nodes.
51 *
52 *
53 * @author <a href="mailto:pra@tim.se">Peter Antman</a>
54 * @version $Revision: 1.1.1.1 $
55 */
56
57 public class DocumentUtil{
58 public DocumentUtil (){
59
60 }
61
62 /***
63 * <p>Get the serialized XML from node.
64 *
65 * @return the serialized XML from node, wich will have content encoding iso-8859-1, it will be indented and have a prolog.
66 */
67 public static String getXml(Node node)throws XmlException {
68 return getXml(node, true, "iso-8859-1", false);
69 }
70
71
72 /***
73 * Return the string XML rep of node.
74 *
75 *
76 * @param node the node to serialize.
77 * @param indent indent output if true.
78 * @param encoding the encoding to use, if null iso-8859-1
79 * @param omitDecl if true the xml prolog is omitted, which may be handly if the XML is to be used as a fragment.
80 */
81 public static String getXml(Node node, boolean indent, String encoding, boolean omitDecl) throws XmlException{
82 try {
83
84 Document d = null;
85 DocumentType dt =null;
86 if ( node instanceof Document) {
87 d = (Document)node;
88 } else {
89 d = node.getOwnerDocument();
90 }
91 if (d != null ) {
92 dt= d.getDoctype();
93 }
94
95
96 TransformerFactory tfactory = TransformerFactory.newInstance();
97 Transformer serializer = tfactory.newTransformer();
98
99 Resolver res = ResolverContext.get();
100 if ( res != null ) {
101 serializer.setURIResolver( res.getURIResolver());
102 }
103
104
105
106 serializer.setOutputProperty(OutputKeys.METHOD, "xml");
107 if ( indent) {
108 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
109 }
110 if ( encoding != null) {
111 serializer.setOutputProperty(OutputKeys.ENCODING, encoding);
112 } else {
113 serializer.setOutputProperty(OutputKeys.ENCODING, "iso-8859-1");
114 }
115 if ( omitDecl) {
116 serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
117 }
118 if ( dt != null) {
119 if ( dt.getPublicId()!= null ) {
120 serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC , dt.getPublicId());
121 }
122 if ( dt.getSystemId() != null) {
123 serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM , dt.getSystemId());
124 }
125
126 }
127
128
129
130 StringWriter wr = new StringWriter();
131 serializer.transform(new DOMSource(node),
132 new StreamResult(wr));
133
134 return wr.toString();
135
136 } catch (TransformerException ex) {
137 String reason = ex.getMessage();
138 SourceLocator loc = ex.getLocator();
139 if (loc != null) {
140 reason = reason + " Error at line=" + loc.getLineNumber() +
141 " column=" + loc.getColumnNumber() +
142 " in systemID=" + loc.getSystemId();
143 }
144 Throwable e = ex.getException();
145 if(e instanceof org.xml.sax.SAXParseException) {
146 org.xml.sax.SAXParseException s = (org.xml.sax.SAXParseException)e;
147 reason = reason + "SAX Exception " + s.getMessage()+
148 " at line=: " +s.getLineNumber() +
149 " column=" +s.getColumnNumber() + " in systemID" +
150 s.getSystemId();
151 }
152 throw new XmlException("Could not serialize XML: " + reason,ex);
153 }
154 }
155
156 /***
157 * Replace oldNode with newNode and its children, importing the new node if needed.
158 */
159 public static void replace(Node oldNode, Node newNode) throws XmlException {
160 Document into = oldNode.getOwnerDocument();
161 Document from = newNode.getOwnerDocument();
162
163
164
165
166 if ( into.getDocumentElement() == oldNode) {
167
168 Node newTop = into.importNode(newNode, true);
169 into.replaceChild(newTop, into.getDocumentElement());
170 return;
171 }
172
173
174 DocumentFragment frag = null;
175 if ( newNode instanceof DocumentFragment) {
176 frag = (DocumentFragment)newNode;
177 } else {
178 frag = getDocumentFragment(newNode);
179 }
180
181 if ( !into.equals(from)) {
182
183 frag = (DocumentFragment)importNode(into,frag);
184 }
185 Node parent = oldNode.getParentNode();
186 parent.replaceChild(frag,oldNode);
187
188 }
189
190 /***
191 * <p>Create a DocumentFragment from the XML given i string.
192 *
193 * <p>The fragment may not be wellformed XML and may contain multiple children without a root node. To only allow a document fragment to returned where all the children are elements turn forceElementTree on. Any whitespace - including new line - will be removed before trying.
194 *@param xml the string possibly containing and XML fragment. The string is trimed before conversion.
195 *@param forceElementTree the fragment is only valid if alla its children are elements, othervise an exception will be raised.
196 * @throws XMLException if the xml string was not possible to convert to a document fragment or if null.
197 *
198 */
199 public static DocumentFragment getDocumentFragment(String xml, boolean forceElementTree) throws XmlException{
200 if ( xml == null) {
201 throw new XmlException("The xml string is not allowed to be null");
202 }
203
204 Element root = getDocumentElement("<list>"+xml.trim()+"</list>");
205 DocumentFragment frag = root.getOwnerDocument().createDocumentFragment();
206 NodeList ch = root.getChildNodes();
207 int l = ch.getLength();
208 for (int i = 0;i<l;i++) {
209 Node n = (Node)ch.item(i);
210
211 if ( forceElementTree && n.getNodeType() != Node.ELEMENT_NODE) {
212
213
214 if ( n.getNodeType() == Node.TEXT_NODE && "".equals(n.getNodeValue().trim())) {
215 ;
216 } else {
217
218 throw new XmlException("Could not create document fragment: Node was not of element type and forceElementTree was turned on");
219 }
220 }
221 Node imported = frag.appendChild(n.cloneNode(false));
222
223 imported = copyInto(n,imported);
224 }
225
226 return frag;
227
228 }
229
230
231 /***
232 * Create a document fragment around the node and its children, which are cloned.
233 */
234 public static DocumentFragment getDocumentFragment(Node node) throws XmlException {
235 if ( node == null) {
236 throw new XmlException("node is not allowed to be null");
237 }
238
239 DocumentFragment frag = node.getOwnerDocument().createDocumentFragment();
240
241 Node root = frag.appendChild(node.cloneNode(false));
242
243 root = copyInto(node,root);
244
245
246
247
248
249
250
251
252
253 return frag;
254
255 }
256
257 /***
258 * Copy the child nodes and its children from fromNode into toNode.
259 */
260 public static Node copyInto(Node fromNode, Node toNode) {
261 for (Node n = fromNode.getFirstChild();
262 n != null;
263 n = n.getNextSibling()) {
264 Node cp = n.cloneNode(false);
265 toNode.appendChild(cp);
266 copyInto(n,cp);
267 }
268 return toNode;
269 }
270
271 /***
272 * Import node into document, make shure deep works (manually).
273 */
274 public static Node importNode(Document document, Node toImport) {
275 if (toImport != null) {
276 Node root = toImport.cloneNode(false);
277
278 root = document.importNode(root, false);
279
280 for (Node n = toImport.getFirstChild();
281 n != null;
282 n = n.getNextSibling()) {
283 root.appendChild(document.importNode(n, true));
284 }
285
286 return root;
287 }
288 return null;
289 }
290
291 /***
292 * Create a new Document from the node, where node becomes the new root node and all its children the rest of the document.
293 */
294 public static Document createDocument(Node node) throws XmlException {
295 Document newDoc = node.
296 getOwnerDocument().
297 getImplementation().
298 createDocument(node.getNamespaceURI(), ((Element)node).getTagName(), null);
299 Node newTop = newDoc.importNode(node, true);
300 newDoc.replaceChild(newTop, newDoc.getDocumentElement());
301 return newDoc;
302 }
303
304
305 /***
306 * Create a DOM from string and return its root element.
307 */
308 public static Element getDocumentElement(String xml) throws XmlException {
309 Document doc = getDocument(xml);
310 return doc.getDocumentElement();
311 }
312
313 /***
314 * Get DOM document from xml string-
315 */
316 public static Document getDocument(String xml) throws XmlException {
317 return getDocument( new BufferedReader( new StringReader(xml) ) );
318 }
319
320 /***
321 * If reading from a file, use this, since it will set the SystemId.
322 */
323 public static Document getDocument(File file) throws XmlException {
324 if ( file == null) {
325 throw new XmlException("File not allowed to be null");
326 }
327 BufferedReader reader;
328 try {
329 reader = new BufferedReader( new FileReader(file));
330 } catch (java.io.FileNotFoundException e) {
331 throw new XmlException("Could not find file: " + e,e);
332 }
333
334 InputSource is = new InputSource( reader );
335 is.setSystemId( file.toString());
336 return getDocument( is);
337 }
338
339 public static Document getDocument(Reader reader) throws XmlException {
340 return getDocument( new InputSource( reader ));
341 }
342
343 public static Document getDocument(InputStream stream) throws XmlException {
344 return getDocument( new InputSource( stream ));
345 }
346
347 /***
348 * FIXME: Here we could fix our own factory/prop stuff to use alternative builders
349 * than system default.
350 */
351 public static Document getDocument(InputSource is) throws XmlException {
352 Document doc = null;
353 try {
354 UtilsConfig conf = UtilsConfig.getContextConfig();
355
356 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
357 if ( conf != null && conf.isNamespaceAware()) {
358 docBuilderFactory.setNamespaceAware(true);
359 }
360
361 DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
362
363 Resolver res = ResolverContext.get();
364 if ( res != null ) {
365 docBuilder.setEntityResolver( res.getEntityResolver());
366 }
367
368 doc = docBuilder.parse(is);
369
370 if ( doc !=null) {
371 return doc;
372 } else {
373
374 throw new XmlException("Could not get document = null");
375 }
376
377
378
379 } catch (SAXException ex) {
380 String reason = ex.getMessage();
381 if(ex instanceof org.xml.sax.SAXParseException) {
382 org.xml.sax.SAXParseException s = (org.xml.sax.SAXParseException)ex;
383 reason = reason + " at line="+s.getLineNumber() + " column=" +
384 s.getColumnNumber() +
385 " in systemID" + s.getSystemId();
386 }
387 throw new XmlException("Could not parse file: " + reason,ex);
388 } catch (ParserConfigurationException ex) {
389 throw new XmlException("Could not setup parser " + ex,ex);
390 } catch (IOException ex) {
391 throw new XmlException("Could not read xml " + ex,ex);
392 }
393
394 }
395
396 public static void main(String[] args){
397
398
399 }
400 }