001: /*
002: * Copyright (C) 2001, 2002 Robert MacGrogan
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: *
019: * $Archive: SourceJammer$
020: * $FileName: XMLUtil.java$
021: * $FileID: 4302$
022: *
023: * Last change:
024: * $AuthorName: Rob MacGrogan$
025: * $Date: 4/23/03 5:24 PM$
026: * $VerID: 8797$
027: * $Comment: Replaced GPL header with LGPL header.$
028: */
029:
030: package org.sourcejammer.xml;
031:
032: import java.awt.font.TransformAttribute;
033: import java.io.*;
034:
035: import javax.xml.parsers.DocumentBuilder;
036: import javax.xml.parsers.DocumentBuilderFactory;
037: import javax.xml.parsers.ParserConfigurationException;
038: import javax.xml.transform.OutputKeys;
039: import javax.xml.transform.Transformer;
040: import javax.xml.transform.TransformerConfigurationException;
041: import javax.xml.transform.TransformerException;
042: import javax.xml.transform.TransformerFactory;
043: import javax.xml.transform.dom.DOMResult;
044: import javax.xml.transform.dom.DOMSource;
045: import javax.xml.transform.stream.StreamResult;
046: import javax.xml.transform.stream.StreamSource;
047:
048: import org.sourcejammer.util.AppConfig;
049: import org.sourcejammer.util.ConfigurationException;
050: import org.sourcejammer.util.ProcessTimer;
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Element;
053: import org.w3c.dom.Node;
054: import org.w3c.dom.NodeList;
055: import org.xml.sax.InputSource;
056: import org.xml.sax.SAXException;
057:
058: /**
059: * Title: $FileName: XMLUtil.java$
060: * @author $AuthorName: Rob MacGrogan$
061: * @version $VerNum: 3$
062: * $KeyWordsOff: $<br><br>
063: *
064: *
065: * Utility class for common functions that are performed with XML.
066: */
067: public class XMLUtil {
068: /**
069: * Default constructor.
070: */
071: public XMLUtil() {
072: }
073:
074: /**
075: * Function take an XML document, serializes it and returns a string buffer. No indention will be done.
076: *
077: * @param doc the XML document to be serialized into a string buffer.
078: *
079: * @exception IOException thrown when there is an exception writing the XML document to the string buffer.
080: *
081: * @return The XML document as a string buffer.
082: */
083: public static StringBuffer xmlDocToStringBuffer(Document doc)
084: throws IOException, TransformerException {
085: return xmlDocToStringBuffer(doc, false);
086: }
087:
088: /**
089: * Function take an XML document, serializes it (with an indented format for viewing) and returns a string buffer.
090: *
091: * @param doc the XML document to be serialized into a string buffer.
092: *
093: * @exception IOException thrown when there is an exception writing the XML document to the string buffer.
094: *
095: * @return The XML document as a string buffer.
096: */
097: public static StringBuffer xmlDocToStringBuffer(Document doc,
098: boolean indent) throws IOException, TransformerException {
099: if (doc != null) {
100: Transformer trans = TransformerFactory.newInstance()
101: .newTransformer();
102: if (indent) {
103: trans.setOutputProperty(OutputKeys.INDENT, "yes");
104: trans.setOutputProperty(
105: "{http://xml.apache.org/xslt}indent-amount",
106: "2");
107: }
108: DOMSource source = new DOMSource(doc);
109: StringWriter writer = new StringWriter();
110: StreamResult result = new StreamResult(writer);
111:
112: trans.transform(source, result);
113:
114: StringBuffer strOutput = writer.getBuffer();
115: return strOutput;
116: }
117: return null;
118: }
119:
120: /**
121: * Function that takes a String and returns a Document object.
122: *
123: * @param xmlDocAsString the XML document as a string.
124: *
125: * @exception IOException thrown when there is an exception reading the XML document from the string.
126: * @exception SAXException thrown when there is an error while parsing the XML document.
127: * @exception ParserConfigurationException thrown when there is an error while parsing the XML document.
128: *
129: * @return An XML Document object built from the string passed in.
130: */
131: public static Document getXMLDoc(String xmlDocAsString)
132: throws IOException, SAXException,
133: ParserConfigurationException {
134: if (xmlDocAsString != null) {
135: return getXMLDoc(new InputSource(new StringReader(
136: xmlDocAsString)));
137: }
138: return null;
139: }
140:
141: /**
142: * Reads in a file and passes it to an XML parser from which it will get and return
143: * a document object.
144: *
145: * @param xmlFile a file object that represents the file to be read in.
146: *
147: * @exception IOException thrown when there is an exception reading the XML document from the file.
148: * @exception SAXException thrown when there is an error while parsing the XML document.
149: * @exception ParserConfigurationException thrown when there is an error while parsing the XML document.
150: *
151: * @return An XML document object built from the file passed in.
152: */
153: public static Document getXMLDoc(File xmlFile) throws IOException,
154: SAXException, ParserConfigurationException {
155: if ((xmlFile != null) && (xmlFile.exists())) {
156: return getXMLDoc(new InputSource(new FileReader(xmlFile)));
157: }
158: return null;
159: }
160:
161: /**
162: * Return a new, empty XML document.
163: */
164: public static Document newDocument() {
165: Document doc = null;
166: try {
167: DocumentBuilder builder = DocumentBuilderFactory
168: .newInstance().newDocumentBuilder();
169: doc = builder.newDocument();
170: } catch (ParserConfigurationException ex) {
171: throw new ConfigurationException(
172: "Can't build new xml doc.", ex);
173: }
174: return doc;
175: }
176:
177: /**
178: * Reads in an input source and passes it to an XML parser from which it will get and return
179: * a document object.
180: *
181: * @param source the input source to be parsed into an XML document.
182: *
183: * @exception IOException thrown when there is an exception reading the XML document from the input source.
184: * @exception SAXException thrown when there is an error while parsing the XML document.
185: * @exception ParserConfigurationException thrown when there is an error while parsing the XML document.
186: *
187: * @return An XML document object built from the file passed in.
188: */
189: public static Document getXMLDoc(InputSource source)
190: throws IOException, ParserConfigurationException,
191: SAXException {
192: //Tried to speed this up with transformers once. This is faster.
193: Document doc = null;
194: if (source != null) {
195: DocumentBuilder bldr = DocumentBuilderFactory.newInstance()
196: .newDocumentBuilder();
197: doc = bldr.parse(source);
198: }
199: return doc;
200: }
201:
202: public static Document oldGetXMLDoc(InputSource source)
203: throws IOException, ParserConfigurationException,
204: SAXException {
205: Document doc = null;
206: if (source != null) {
207: DocumentBuilder bldr = DocumentBuilderFactory.newInstance()
208: .newDocumentBuilder();
209: doc = bldr.parse(source);
210: }
211: return doc;
212: }
213:
214: /**
215: * Convenience method for returning result of getTextFromNode(0) as String.
216: */
217: public static String getStringFromNode(Node node,
218: String concatString) {
219: return getTextFromNode(node, concatString).toString();
220: }
221:
222: /**
223: * Concatenates the text nodes together for the node passed in into a single string.
224: *
225: * @param node the node to extract the text nodes from.
226: * @param concatString the string that will be concatenated between each text node.
227: *
228: * @return A string buffer representing all of the text nodes concatenated together with concatString.
229: * Returns null if the node is null. Returns an empty string of it does not have any text nodes.
230: */
231: public static StringBuffer getTextFromNode(Node node,
232: String concatString) {
233: if (node != null) {
234: StringBuffer result = new StringBuffer();
235: NodeList childNodes = node.getChildNodes();
236: if (childNodes != null) {
237: int len = childNodes.getLength();
238: for (int i = 0; i < len; i++) {
239: if (childNodes.item(i).getNodeType() == Node.TEXT_NODE) {
240: if (result.length() > 0) {
241: result.append(concatString);
242: }
243: result
244: .append(childNodes.item(i)
245: .getNodeValue());
246: }
247: }
248: }
249: return result;
250: }
251: return null;
252: }
253:
254: /**
255: * Call this to get a specific value out of the configuration document.
256: *
257: * @param nodeName the name of the XML node to retrieve.
258: * @param parentElement the parent of the node to find and retrieve its values for.
259: *
260: * @return the value of the XML text node.
261: */
262: public static String getValue(String nodeName, Element parentElement) {
263: if (parentElement != null) {
264: NodeList nodes = parentElement
265: .getElementsByTagName(nodeName);
266: if ((nodes != null) && (nodes.getLength() > 0)) {
267: return getTextFromNode(nodes.item(0), " ").toString();
268: }
269: }
270: return null;
271: }
272:
273: /**
274: * Call this to get a set of values out of the configuration document.
275: *
276: * @param nodeName the name of the XML node(s) to retrieve.
277: * @param parentElement the parent of the node to find and retrieve its values for.
278: *
279: * @return the value of the XML text node(s).
280: */
281: public static String[] getValues(String nodeName,
282: Element parentElement) {
283: if (parentElement != null) {
284: NodeList nodes = parentElement
285: .getElementsByTagName(nodeName);
286: if ((nodes != null) && (nodes.getLength() > 0)) {
287: int len = nodes.getLength();
288: String[] result = new String[len];
289: for (int i = 0; i < len; i++) {
290: result[i] = getTextFromNode(nodes.item(i), " ")
291: .toString();
292: }
293: return result;
294: }
295: }
296: return null;
297: }
298:
299: /**
300: * Turns invalid characters into spaces. Valid characters are defined by the following:
301: *
302: * [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] // any Unicode character, excluding the
303: * | [#xE000-#xFFFD] | [#x10000-#x10FFFF] // surrogate blocks, FFFE, and FFFF.
304: *
305: * Implementation Note: The algorithm for determining what is a valid character
306: * and what is not was copied from org.apache.xerces.framework.XMLDocumentScanner.scanCharRef()
307: *
308: * @param input the string to replace invalid characters with spaces.
309: *
310: * @return a string with all invalid characters replaced by a space each.
311: */
312: public static String replaceInvalidCharWithSpace(String input) {
313: if (input != null) {
314: final String SPACE = " ";
315: char[] inputChars = input.toCharArray();
316: StringBuffer result = new StringBuffer(inputChars.length);
317: for (int i = 0; i < inputChars.length; i++) {
318: char currentChar = inputChars[i];
319: if (currentChar < 0x20) {
320: if (currentChar == 0x09 || currentChar == 0x0A
321: || currentChar == 0x0D) {
322: result.append(currentChar);
323: continue;
324: }
325: } else if (currentChar <= 0xD7FF
326: || (currentChar >= 0xE000 && (currentChar <= 0xFFFD || (currentChar >= 0x10000 && currentChar <= 0x10FFFF)))) {
327: result.append(currentChar);
328: continue;
329: }
330: result.append(SPACE);
331: }
332: return result.toString();
333: }
334: return null;
335: }
336:
337: /**
338: * Return the child element of elm with the name sElementName. Case is
339: * not ignored in comparing the element name to sElementName.
340: *
341: * @param sElementName the name of the element to be returned.
342: * @param elmParent the parent Element in which this method will search for a
343: * child element with sElementName as its name.
344: *
345: * @exception XMLNodeDoesNotExistException if the requested Element does not exist
346: * in the parent Element.
347: *
348: * @return an Element with name sElementName, which is a child of elmParent.
349: */
350: public static Element getChildElement(String sElementName,
351: Element elmParent) throws XMLNodeDoesNotExistException {
352: return getChildElement(sElementName, elmParent, false);
353: }
354:
355: /**
356: * Return the child element of elm with the name sElementName.
357: *
358: * @param sElementName the name of the element to be returned.
359: * @param elmParent the parent Element in which this method will search for a
360: * child element with sElementName as its name.
361: * @param bIgnoreCase if <code>true</code>, case is ignored when comparing
362: * the names of child Elements to sElementName.
363: *
364: * @exception XMLNodeDoesNotExistException if the requested Element does not exist
365: * in the parent Element.
366: *
367: * @return an Element with name sElementName, which is a child of elmParent.
368: */
369: public static Element getChildElement(String sElementName,
370: Element elmParent, boolean bIgnoreCase)
371: throws XMLNodeDoesNotExistException {
372:
373: Element elmReturn = null;
374:
375: NodeList oNodeList = elmParent
376: .getElementsByTagName(sElementName);
377: int iNodeCount = oNodeList.getLength();
378: boolean bElementSearchComplete = false;
379: int iCounter = -1;
380: while (!bElementSearchComplete) {
381: iCounter++;
382: if (iCounter == iNodeCount) {
383: //This is the last Node in the list
384: bElementSearchComplete = true;
385: }
386:
387: try {
388: elmReturn = (Element) oNodeList.item(iCounter);
389: bElementSearchComplete = true;
390: } catch (ClassCastException ex) {
391: //This just means the node is not an Element, so go on to the next one.
392: }
393: }//end while
394:
395: if (elmReturn == null) {
396: throw new XMLNodeDoesNotExistException(
397: "There is no node with name: " + sElementName
398: + " inside the parent Element.");
399: }
400:
401: return elmReturn;
402:
403: }//end getChildElement(String, Element)
404:
405: public static Element addNewChildElement(String name, Element parent) {
406: return addNewChildElement(name, null, parent);
407: }
408:
409: /**
410: * Creates a new Element node with the specified name and value, and
411: * appends it to the parent Element.
412: *
413: * @param name -- name for the new Element.
414: * @param value -- value for the new Element.
415: * @param parent -- parent Element of new Element.
416: *
417: * @return The newly created Element.
418: */
419: public static Element addNewChildElement(String name, String value,
420: Element parent) {
421:
422: Document doc = parent.getOwnerDocument();
423: Element elmNew = doc.createElement(name);
424:
425: if (value != null) {
426: elmNew.appendChild(doc.createTextNode(value));
427: }
428:
429: parent.appendChild(elmNew);
430:
431: return elmNew;
432:
433: }
434:
435: /**
436: * Replace current text node with new text if exists. Otherwise, adds
437: * text to element.
438: */
439: public static void addOrReplaceElementText(Element elm,
440: String newValue) throws XMLNodeDoesNotExistException {
441:
442: NodeList childNodes = elm.getChildNodes();
443: if (childNodes != null) {
444: int len = childNodes.getLength();
445: for (int i = 0; i < len; i++) {
446: if (childNodes.item(i).getNodeType() == Node.TEXT_NODE) {
447: Node ndText = childNodes.item(i);
448: elm.removeChild(ndText);
449: break;
450: }
451: }//end for
452: }
453: Document doc = elm.getOwnerDocument();
454: elm.appendChild(doc.createTextNode(newValue));
455: }
456:
457: /**
458: * Add a root element to a document.
459: */
460: public static Element addRootElement(String name, Document parent) {
461:
462: Element elmNew = parent.createElement(name);
463:
464: parent.appendChild(elmNew);
465:
466: return elmNew;
467:
468: }
469:
470: public static void writeXMLDocToFileSys(Document doc, File file)
471: throws IOException, TransformerException {
472: writeXMLDocToFileSys(doc, file, false);
473: }
474:
475: /**
476: * Writes the xml doc to the specified file.
477: */
478: public static void writeXMLDocToFileSys(Document doc, File file,
479: boolean indent) throws IOException, TransformerException {
480:
481: if (file.exists() && !file.canWrite()) {
482: throw new IOException(
483: "Cannot save xml file. File or directory is read-only.");
484: }
485:
486: Transformer trans = TransformerFactory.newInstance()
487: .newTransformer();
488: trans.setOutputProperty(OutputKeys.ENCODING, AppConfig
489: .getInstance().getXmlEncodingCharset());
490: if (indent) {
491: trans.setOutputProperty(OutputKeys.INDENT, "yes");
492: trans.setOutputProperty(
493: "{http://xml.apache.org/xslt}indent-amount", "2");
494: }
495: DOMSource source = new DOMSource(doc);
496: StreamResult result = new StreamResult(file);
497: trans.transform(source, result);
498: }
499:
500: public static void main(String[] args) {
501: try {
502: String readFile = args[0];
503: String writeFileOld = args[1];
504: String writeFileNew = args[2];
505:
506: File read = new File(readFile);
507: File writeOld = new File(writeFileOld);
508: File writeNew = new File(writeFileNew);
509:
510: ProcessTimer timer = ProcessTimer.getDefaultInstance();
511:
512: timer.beginProcess("read " + readFile);
513: Document doc = getXMLDoc(read);
514: timer.endProcess("read " + readFile);
515:
516: //timer.beginProcess("write old");
517: //oldWrite(doc, writeOld);
518: //timer.endProcess("write old");
519:
520: timer.beginProcess("write new");
521: writeXMLDocToFileSys(doc, writeNew);
522: timer.endProcess("write new");
523:
524: } catch (Throwable thr) {
525: thr.printStackTrace();
526: }
527: }
528:
529: }
|