001: package com.jat.util.xml;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.io.StringWriter;
006: import java.util.ArrayList;
007: import java.util.Enumeration;
008: import java.util.Vector;
009: import javax.xml.parsers.DocumentBuilder;
010: import javax.xml.parsers.DocumentBuilderFactory;
011: import javax.xml.parsers.ParserConfigurationException;
012: import javax.xml.transform.ErrorListener;
013: import javax.xml.transform.Transformer;
014: import javax.xml.transform.TransformerConfigurationException;
015: import javax.xml.transform.TransformerException;
016: import javax.xml.transform.TransformerFactory;
017: import javax.xml.transform.dom.DOMSource;
018: import javax.xml.transform.stream.StreamResult;
019: import javax.xml.transform.stream.StreamSource;
020:
021: import org.apache.xpath.XPathAPI;
022: import org.apache.xpath.objects.XObject;
023: import org.w3c.dom.Document;
024: import org.w3c.dom.Element;
025: import org.w3c.dom.Node;
026: import org.w3c.dom.NodeList;
027: import org.w3c.dom.ProcessingInstruction;
028: import org.xml.sax.ErrorHandler;
029: import org.xml.sax.InputSource;
030: import org.xml.sax.SAXException;
031: import org.xml.sax.SAXParseException;
032:
033: /**
034: * <p>Title: JAT</p>
035: * <p>Description: This class is an utility to manage XML document in cogiumtion with XMLUtil class
036: * <br>It permits to:<ul>
037: * <li>read, write and to examine XML document</li>
038: * <li>evaluate XPath expression</li>
039: * <li>perform XSLT trasformation</li>
040: * </p>
041: * <p>Copyright: Copyright (c) 2004</p>
042: *
043: * @author stf
044: * @version 1.1
045: * @see com.jat.util.xml.XMLUtil
046: * @see com.jat.util.file.FileUtil
047: */
048:
049: public class XMLDocument implements ErrorHandler, ErrorListener {
050:
051: private Document doc;
052: private boolean coalescing = false;
053:
054: /**
055: * return a XMLDocument istance
056: *
057: * @param is XML input source
058: * @param validate if true the XML will be validated by its own DTD or Schema
059: * @param coalescing if true CDataSection will be repleaced by Text Node
060: * @exception java.io.IOException
061: * @exception javax.xml.parsers.ParserConfigurationException
062: * @exception org.xml.sax.SAXException
063: */
064: protected static XMLDocument getInstance(InputSource is,
065: boolean validate, boolean coalescing) throws IOException,
066: ParserConfigurationException, SAXException {
067: XMLDocument xmlDoc = new XMLDocument(coalescing);
068: DocumentBuilderFactory dbf = DocumentBuilderFactory
069: .newInstance();
070: DocumentBuilder docBuilder = dbf.newDocumentBuilder();
071: if (validate) {
072: // Validate document
073: dbf.setNamespaceAware(true);
074: dbf.setValidating(true);
075: docBuilder.setErrorHandler(xmlDoc);
076: }
077:
078: xmlDoc.setDocument(docBuilder.parse(is));
079: return xmlDoc;
080: }
081:
082: /**
083: * Constructor
084: */
085: public XMLDocument() {
086: this (null, false);
087: }
088:
089: /**
090: * Constructor
091: *
092: * @param coalescing if true CDataSection will be repleaced by Text Node
093: */
094: public XMLDocument(boolean coalescing) {
095: this (null, coalescing);
096: }
097:
098: /**
099: * Constructor
100: *
101: * @param doc DOM object
102: */
103: public XMLDocument(Document doc) {
104: this (doc, false);
105: }
106:
107: /**
108: * Constructor
109: *
110: * @param doc DOM object
111: * @param coalescing if true CDataSection will be repleaced by TextNode
112: */
113: public XMLDocument(Document doc, boolean coalescing) {
114: this .setDocument(doc);
115: this .setCoalescing(coalescing);
116: xpath = new XPathAPI();
117: }
118:
119: /**
120: * Save XML to file
121: *
122: * @param filename The name of the file
123: * @exception java.io.IOException
124: */
125: public void saveToFile(String filename) throws IOException {
126: XMLUtil.saveXML(filename, this .getDocument());
127: }
128:
129: private XPathAPI xpath = null;
130:
131: /**
132: * Evaluate an XPath expression.
133: *
134: * For Example:
135: * the xml:
136: *
137: * <person>
138: * <name>Joe</name>
139: * <tel">+391111</tel>
140: * <city>Rome</city>
141: * </person>
142: *
143: * XPath expression "person/name" will return 1 org.w3c.dom.Node (org.w3c.dom.TextNode) with value 'Joe'
144: *
145: * XPath expression "person" will return 3 org.w3c.dom.Node (org.w3c.dom.Element) with the follow structure:
146: * 1) <name>Joe</name>
147: * 2) <tel">+391111</tel>
148: * 3) <city>Rome</city>
149: *
150: * @param xPath XPath expression to evalate
151: * @return An array list containing Node object(s)
152: * @exception javax.xml.transform.TransformerException
153: */
154: public ArrayList evaluateXPath(String xPath)
155: throws TransformerException {
156: ArrayList result = new ArrayList();
157: XObject xobj = xpath.eval(this .getDocument(), xPath);
158: NodeList nl = xobj.nodelist();
159: if (XMLUtil.DEBUG)
160: System.out.println("XPath length: " + nl.getLength());
161: for (int i = 0; i < nl.getLength(); i++) {
162: Node n = nl.item(i);
163: if (XMLUtil.DEBUG)
164: System.out.println(n);
165: if (nl.getLength() > 1)
166: result.add(n);
167: else if (nl.getLength() == 1) {
168: NodeList childlist = n.getChildNodes();
169: if (childlist.getLength() > 1)
170: result.add(n);
171: else if (childlist.getLength() == 1) {
172: Node child = childlist.item(0);
173: //if (!(child.getNodeType()==Node.TEXT_NODE && child.getNodeValue()!=null && child.getNodeValue().trim().equals(""))) result.add(child);
174: //else
175: result.add(child);
176: }
177: }
178: }
179: return result;
180: }
181:
182: /**
183: * print XPath result into standard output
184: *
185: * @param xpath XPath expression
186: * @exception java.lang.Exception
187: */
188: public void printXPath(String xpath) throws Exception {
189: ArrayList nl = evaluateXPath(xpath);
190: if (nl != null) {
191: System.out.println(nl.size() + " result(s) for XPath '"
192: + xpath + "':");
193: for (int i = 0; i < nl.size(); i++) {
194: Node node = (Node) nl.get(i);
195: System.out.println((i + 1) + ") "
196: + XMLUtil.getXMLString(node));
197: }
198: } else
199: System.out.println("No results for XPath '" + xpath + "':");
200: System.out.println("");
201: }
202:
203: /**
204: * return all stylesheet process instruction of the XML document
205: */
206: public Vector getAllStyleSheetProcessingInstructionNode() {
207: Vector list = new Vector();
208: XMLUtil.getAllNodes(this .getDocument(),
209: Node.PROCESSING_INSTRUCTION_NODE,
210: XMLUtil.STYLESHEET_NODE_NAME, list);
211: return list;
212: }
213:
214: /**
215: * performs a XSLT transform by the stylesheet into the XML document (if exist)
216: * throws a Trasformer Exception if the stylesheet does not exist
217: *
218: * @exception javax.xml.transform.TransformerConfigurationException
219: * @exception javax.xml.transform.TransformerException
220: */
221: public StringWriter transform()
222: throws TransformerConfigurationException,
223: TransformerException {
224: Vector vect = this .getAllStyleSheetProcessingInstructionNode();
225: if (vect == null || vect.size() < 1)
226: throw new TransformerException(
227: "No xml-stylesheet found in document");
228: ProcessingInstruction pi = (ProcessingInstruction) vect
229: .elementAt(0);
230: String ref = "href=\"";
231: String val = pi.getNodeValue();
232: int indexStart = val.indexOf(ref);
233: int indexEnd = val.lastIndexOf("\"");
234: String filename = val.substring(indexStart + ref.length(),
235: indexEnd).trim();
236: System.out.println("stylesheet: " + filename);
237: return this .transform(filename);
238: }
239:
240: /**
241: * performs a XSLT transform by the stylesheet specified in styleFilename
242: *
243: * @param styleFilename the name of the stylesheet file
244: * @exception javax.xml.transform.TransformerConfigurationException
245: * @exception javax.xml.transform.TransformerException
246: */
247: public StringWriter transform(String styleFilename)
248: throws TransformerConfigurationException,
249: TransformerException {
250: StreamSource source = new StreamSource(styleFilename);
251: return this .transform(source);
252: }
253:
254: /**
255: * performs a XSLT transform by the stylesheet specified in styleFile
256: *
257: * @param styleFile the file rapresenting the stylesheet
258: * @exception javax.xml.transform.TransformerConfigurationException
259: * @exception javax.xml.transform.TransformerException
260: */
261: public StringWriter transform(File styleFile)
262: throws TransformerConfigurationException,
263: TransformerException {
264: StreamSource source = new StreamSource(styleFile);
265: return this .transform(source);
266: }
267:
268: /**
269: * performs a XSLT transform by the stylesheet specified in styleSource
270: *
271: * @param styleSource the stream rapresenting the stylesheet
272: * @exception javax.xml.transform.TransformerConfigurationException
273: * @exception javax.xml.transform.TransformerException
274: */
275: public StringWriter transform(StreamSource styleSource)
276: throws TransformerConfigurationException,
277: TransformerException {
278: TransformerFactory tf = TransformerFactory.newInstance();
279: Transformer transformer = tf.newTransformer(styleSource);
280: transformer.setErrorListener(this );
281: DOMSource source = new DOMSource(this .getDocument());
282: StringWriter sw = new StringWriter();
283: StreamResult result = new StreamResult(sw);
284: transformer.transform(source, result);
285: return sw;
286: }
287:
288: /**
289: * if true CDataSection will be repleaced by Text Node
290: *
291: * @param coalescing
292: */
293: protected void setCoalescing(boolean coalescing) {
294: this .coalescing = coalescing;
295: this .convertCDataToText();
296: }
297:
298: /**
299: * return true if CDataSection will be (or has been) repleaced by Text Node
300: */
301: public boolean isCoalescing() {
302: return this .coalescing;
303: }
304:
305: /**
306: * return the String representing the Document in XML format
307: */
308: public String toString() {
309: return XMLUtil.getXMLString(this .getDocument());
310: }
311:
312: /**
313: * Returun all Text node children of the Document
314: */
315:
316: public Vector getAllTextNode() {
317: return XMLUtil.getAllTextNode(this .getDocument());
318: }
319:
320: /**
321: * Returun all Comment node children of the Document
322: */
323: public Vector getAllCommentNode() {
324: return XMLUtil.getAllCommentNode(this .getDocument());
325: }
326:
327: /**
328: * Returun all DataSection node children of the Document
329: */
330: public Vector getAllCDataSectionNode() {
331: return XMLUtil.getAllCDataSectionNode(this .getDocument());
332: }
333:
334: /**
335: * Returun all Attribute node children of the Document
336: */
337: public Vector getAllAttributeNode() {
338: return XMLUtil.getAllAttributeNode(this .getDocument());
339: }
340:
341: /**
342: * Returun all DocumentFragment children node of the Document
343: */
344: public Vector getAllDocumentFragmentNode() {
345: return XMLUtil.getAllDocumentFragmentNode(this .getDocument());
346: }
347:
348: /**
349: * Returun the Element node children of the Document
350: */
351: public Element getElementNode() {
352: return this .getDocument().getDocumentElement();
353: }
354:
355: /**
356: * Returun all Entity node children of the Document
357: */
358: public Vector getAllEntityNode() {
359: return XMLUtil.getAllEntityNode(this .getDocument());
360: }
361:
362: /**
363: * Returun all Notation node children of the Document
364: */
365: public Vector getAllNotationNode() {
366: return XMLUtil.getAllNotationNode(this .getDocument());
367: }
368:
369: /**
370: * Returun all ProcessInstruction children node of the Document
371: */
372: public Vector getAllProcessingInstructionNode() {
373: return XMLUtil.getAllProcessingInstructionNode(this
374: .getDocument());
375: }
376:
377: /**
378: * Returun all Node children of the Document with the type 'type'
379: *
380: * @param type Node type
381: */
382: public Vector getAllNodes(short type) {
383: return XMLUtil.getAllNodes(this .getDocument(), type);
384: }
385:
386: /**
387: * return the XML Document
388: */
389: public Document getDocument() {
390: return this .doc;
391: }
392:
393: /**
394: * set the XML Document
395: *
396: * @param doc
397: */
398: protected void setDocument(Document doc) {
399: this .doc = doc;
400: converted = false;
401: this .convertCDataToText();
402: }
403:
404: private boolean converted = false;
405:
406: /**
407: * convert all CDataSection node into Text node
408: */
409: protected synchronized void convertCDataToText() {
410: if (this .getDocument() == null || !this .isCoalescing()
411: || converted)
412: return;
413: if (XMLUtil.DEBUG)
414: System.out.println("Starting convert CData to Text");
415: Vector vett = XMLUtil
416: .getAllCDataSectionNode(this .getDocument());
417: for (Enumeration e = vett.elements(); e.hasMoreElements();) {
418: Node cdata = (Node) e.nextElement();
419: String val = cdata.getNodeValue();
420: Node parent = cdata.getParentNode();
421: parent.appendChild(this .getDocument().createTextNode(
422: XMLUtil.parseText(val)));
423: parent.removeChild(cdata);
424: }
425: converted = true;
426: }
427:
428: /**
429: * Callback method used by XML parser to set error message
430: * @param exception the error message
431: * @throws SAXException
432: */
433: public void warning(SAXParseException ex) throws SAXException {
434: throw ex;
435: }
436:
437: /**
438: * Callback method used by XML parser to set error message
439: * @param exception the error message
440: * @throws SAXException
441: */
442: public void error(SAXParseException ex) throws SAXException {
443: throw ex;
444: }
445:
446: /**
447: * Callback method used by XML parser to set fatal error message
448: * @param exception the fatal error message
449: * @throws SAXException
450: */
451: public void fatalError(SAXParseException ex) throws SAXException {
452: throw ex;
453: }
454:
455: /**
456: * Callback method used by XML parser to set error message
457: * @param exception the error message
458: * @throws SAXException
459: */
460: public void warning(TransformerException ex)
461: throws TransformerException {
462: throw ex;
463: }
464:
465: /**
466: * Callback method used by XML parser to set error message
467: * @param exception the error message
468: * @throws SAXException
469: */
470: public void error(TransformerException ex)
471: throws TransformerException {
472: throw ex;
473: }
474:
475: /**
476: * Callback method used by XML parser to set fatal error message
477: * @param exception the fatal error message
478: * @throws SAXException
479: */
480: public void fatalError(TransformerException ex)
481: throws TransformerException {
482: throw ex;
483: }
484:
485: }
|