001: /*
002: * regain - A file search engine providing plenty of formats
003: * Copyright (C) 2004 Til Schneider
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: Til Schneider, info@murfman.de
020: *
021: * CVS information:
022: * $RCSfile$
023: * $Source$
024: * $Date: 2005-11-14 09:12:56 +0100 (Mo, 14 Nov 2005) $
025: * $Author: til132 $
026: * $Revision: 178 $
027: */
028: package net.sf.regain;
029:
030: import java.io.File;
031: import java.io.FileInputStream;
032: import java.io.FileOutputStream;
033: import java.io.IOException;
034: import java.io.PrintStream;
035: import java.util.ArrayList;
036: import java.util.StringTokenizer;
037:
038: import javax.xml.parsers.DocumentBuilder;
039: import javax.xml.parsers.DocumentBuilderFactory;
040:
041: import org.w3c.dom.Attr;
042: import org.w3c.dom.Document;
043: import org.w3c.dom.Element;
044: import org.w3c.dom.NamedNodeMap;
045: import org.w3c.dom.Node;
046: import org.w3c.dom.NodeList;
047:
048: /**
049: * Enth�lt Hilfsmethoden f�r die Extraktion von Daten aus dem DOM-Dokument einer
050: * XML-Datei.
051: *
052: * @author Til Schneider, www.murfman.de
053: */
054: public class XmlToolkit {
055:
056: /**
057: * Loads an XML file and returns its content as Document.
058: *
059: * @param xmlFile The XML file to load.
060: * @return The XML document of the file.
061: * @throws RegainException If loading the XML file failed.
062: */
063: public static Document loadXmlDocument(File xmlFile)
064: throws RegainException {
065: DocumentBuilder builder;
066: try {
067: builder = DocumentBuilderFactory.newInstance()
068: .newDocumentBuilder();
069: } catch (Exception exc) {
070: throw new RegainException(
071: "Creating XML document builder failed!", exc);
072: }
073:
074: Document doc;
075: FileInputStream stream = null;
076: try {
077: stream = new FileInputStream(xmlFile);
078: doc = builder.parse(stream);
079: } catch (Exception exc) {
080: throw new RegainException("Parsing XML failed: "
081: + xmlFile.getAbsolutePath(), exc);
082: } finally {
083: if (stream != null) {
084: try {
085: stream.close();
086: } catch (Exception exc) {
087: }
088: }
089: }
090:
091: return doc;
092: }
093:
094: /**
095: * Saves an XML Document to a file.
096: *
097: * @param xmlFile The XML file to save to.
098: * @param doc The XML document to save.
099: * @throws RegainException If saving the XML file failed.
100: */
101: public static void saveXmlDocument(File xmlFile, Document doc)
102: throws RegainException {
103: FileOutputStream stream = null;
104: try {
105: stream = new FileOutputStream(xmlFile);
106: String encoding = "ISO-8859-1";
107: PrintStream out = new PrintStream(stream, true, encoding);
108:
109: out.println("<?xml version=\"1.0\" encoding=\"" + encoding
110: + "\"?>");
111: out.println();
112: out.println("<!DOCTYPE entities [");
113: out.println(" <!ENTITY minus \"-\">");
114: out.println(" <!ENTITY lt \"<\">");
115: out.println(" <!ENTITY gt \">\">");
116: out.println("]>");
117: out.println();
118:
119: Element root = doc.getDocumentElement();
120:
121: printNode(out, "", root);
122:
123: out.close();
124: } catch (Exception exc) {
125: throw new RegainException("Saving XML file failed: "
126: + xmlFile.getAbsolutePath(), exc);
127: } finally {
128: if (stream != null) {
129: try {
130: stream.close();
131: } catch (Exception exc) {
132: }
133: }
134: }
135: }
136:
137: /**
138: * Prints a XML node.
139: *
140: * @param out The PrintStream where to print the node.
141: * @param prefix The prefix to put before every line.
142: * @param node The node to print.
143: * @throws IOException If printing failed.
144: */
145: private static void printNode(PrintStream out, String prefix,
146: Node node) throws IOException {
147: prefix = "";
148:
149: String name = node.getNodeName();
150:
151: boolean isText = name.equals("#text");
152: boolean isComment = name.equals("#comment");
153: if (isText) {
154: // This is a text tag
155: String text = node.getNodeValue();
156: text = RegainToolkit.replace(text, "<", "<");
157: text = RegainToolkit.replace(text, ">", ">");
158: text = RegainToolkit.replace(text, "--", "−−");
159: out.print(text);
160: } else if (isComment) {
161: // This is a comment tag
162: String comment = node.getNodeValue();
163: out.print("<!--" + comment + "-->");
164: } else {
165: // This is a normal tag
166: out.print(prefix + "<" + name);
167: if (node.hasAttributes()) {
168: NamedNodeMap attributes = node.getAttributes();
169: for (int i = 0; i < attributes.getLength(); i++) {
170: Node attrib = attributes.item(i);
171: out.print(" " + attrib.getNodeName() + "=\""
172: + attrib.getNodeValue() + "\"");
173: }
174: }
175:
176: if (!node.hasChildNodes()) {
177: out.print("/>");
178: } else {
179: out.print(">");
180: NodeList childList = node.getChildNodes();
181: String childPrefix = prefix + " ";
182: for (int i = 0; i < childList.getLength(); i++) {
183: printNode(out, childPrefix, childList.item(i));
184: }
185: out.print(prefix + "</" + name + ">");
186: }
187: }
188: }
189:
190: /**
191: * Extrahiert den Text eines Knotens, wandelt ihn in einen boolean und gibt das
192: * Ergebnis zur�ck.
193: *
194: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
195: *
196: * @return Der Text des Knotens als boolean.
197: * @throws RegainException Falls der Knoten keinen Text hat oder falls der
198: * Text nicht <CODE>true</CODE> oder <CODE>false</CODE> ist.
199: */
200: public static boolean getTextAsBoolean(Node node)
201: throws RegainException {
202: String asString = getText(node, true, true);
203: if (asString.equalsIgnoreCase("true")) {
204: return true;
205: } else if (asString.equalsIgnoreCase("false")) {
206: return false;
207: } else {
208: throw new RegainException("Value of node '"
209: + node.getNodeName()
210: + "' must be either 'true' or 'false'!");
211: }
212: }
213:
214: /**
215: * Extrahiert den Text eines Knotens, wandelt ihn in ein int und gibt das
216: * Ergebnis zur�ck.
217: *
218: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
219: *
220: * @return Der Text des Knotens als int.
221: * @throws RegainException Falls der Knoten keinen Text hat oder falls der
222: * Text keine Ganzzahl ist.
223: */
224: public static int getTextAsInt(Node node) throws RegainException {
225: String asString = getText(node, true, true);
226: try {
227: return Integer.parseInt(asString);
228: } catch (NumberFormatException exc) {
229: throw new RegainException("Value of node '"
230: + node.getNodeName() + "' must be an integer: '"
231: + asString + "'", exc);
232: }
233: }
234:
235: /**
236: * Extrahiert den Text eines Knotens, wandelt ihn in ein double und gibt das
237: * Ergebnis zur�ck.
238: *
239: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
240: *
241: * @return Der Text des Knotens als double.
242: * @throws RegainException Falls der Knoten keinen Text hat oder falls der
243: * Text kein Gleitkommawert ist.
244: */
245: public static double getTextAsDouble(Node node)
246: throws RegainException {
247: String asString = getText(node, true, true);
248: try {
249: return Double.parseDouble(asString);
250: } catch (NumberFormatException exc) {
251: throw new RegainException("Value of node '"
252: + node.getNodeName()
253: + "' must be a floating-point number (double): '"
254: + asString + "'", exc);
255: }
256: }
257:
258: /**
259: * Extrahiert den Text eines Knotens, wandelt ihn in ein String-Array um, das
260: * alle durch Leerzeichen getrennte Worte enth�lt.
261: *
262: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
263: * @param mandatory Gibt an, ob eine Exception geworfen werden soll, falls der
264: * Text fehlt.
265: *
266: * @return Der Text des Knotens Wort-Listen-Array.
267: * @throws RegainException Falls der Knoten keinen Text hat.
268: */
269: public static String[] getTextAsWordList(Node node,
270: boolean mandatory) throws RegainException {
271: String asString = getText(node, mandatory);
272:
273: if (asString == null) {
274: return null;
275: } else {
276: StringTokenizer tokenizer = new StringTokenizer(asString);
277: String[] wordList = new String[tokenizer.countTokens()];
278: for (int i = 0; i < wordList.length; i++) {
279: wordList[i] = tokenizer.nextToken();
280: }
281: return wordList;
282: }
283: }
284:
285: /**
286: * Gibt den Text eines Knotens zur�ck und pr�ft, ob er eine g�ltige URL ist.
287: * <p>
288: * Der Text wird als g�ltige URL angesehen, wenn er keinen Backslash enth�lt.
289: *
290: * @param node Der Knoten, dessen Text als URL zur�ckgeben werden soll.
291: *
292: * @return Der Text des Knotens.
293: * @throws RegainException Wenn der Knoten keinen Text hat oder wenn der Text
294: * keine g�ltige URL ist.
295: */
296: public static String getTextAsUrl(Node node) throws RegainException {
297: String asString = getText(node, true);
298:
299: // Check whether the text contains a back slash
300: if (asString.indexOf('\\') != -1) {
301: throw new RegainException(
302: "Text of node '"
303: + node.getNodeName()
304: + "' is not a valid URL. Use normal slashes instead of backslashes: '"
305: + asString + "'");
306: }
307:
308: return asString;
309: }
310:
311: /**
312: * Gets the text of a node.
313: *
314: * @param node The node to get the text from.
315: * @return The text of the node or <code>null</code> if the node has no text.
316: */
317: public static String getText(Node node) {
318: Node textNode = getChild(node, "#text");
319: if (textNode == null) {
320: return null;
321: }
322:
323: return textNode.getNodeValue();
324: }
325:
326: /**
327: * Gibt den Text eines Knotens zur�ck.
328: * <p>
329: * Wenn der Knoten keinen Text hat, dann entscheidet <CODE>mandatory</CODE> dar�ber,
330: * ob eine Exception geworfen (<CODE>mandatory</CODE> ist <CODE>true</CODE>) oder
331: * ob <CODE>null</CODE> zur�ckgegeben werden soll (<CODE>mandatory</CODE> ist
332: * <CODE>false</CODE>)
333: *
334: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
335: * @param mandatory Gibt an, ob eine Exception geworfen werden soll, falls der
336: * Text fehlt.
337: *
338: * @return Der Text des Knotens.
339: * @throws RegainException Wenn der Knoten keinen Text hat und
340: * <CODE>mandatory</CODE> <CODE>true</CODE> ist.
341: */
342: public static String getText(Node node, boolean mandatory)
343: throws RegainException {
344: return getText(node, mandatory, false);
345: }
346:
347: /**
348: * Gibt den Text eines Knotens zur�ck.
349: * <p>
350: * Wenn der Knoten keinen Text hat, dann entscheidet <CODE>mandatory</CODE> dar�ber,
351: * ob eine Exception geworfen (<CODE>mandatory</CODE> ist <CODE>true</CODE>) oder
352: * ob <CODE>null</CODE> zur�ckgegeben werden soll (<CODE>mandatory</CODE> ist
353: * <CODE>false</CODE>)
354: *
355: * @param node Der Knoten, dessen Text zur�ckgeben werden soll.
356: * @param mandatory Gibt an, ob eine Exception geworfen werden soll, falls der
357: * Text fehlt.
358: * @param trimmed Specifies whether the text should be trimmed.
359: *
360: * @return Der Text des Knotens.
361: * @throws RegainException Wenn der Knoten keinen Text hat und
362: * <CODE>mandatory</CODE> <CODE>true</CODE> ist.
363: */
364: public static String getText(Node node, boolean mandatory,
365: boolean trimmed) throws RegainException {
366: String text = getText(node);
367:
368: if (trimmed && (text != null)) {
369: text = text.trim();
370: }
371:
372: if (mandatory && ((text == null) || (text.length() == 0))) {
373: throw new RegainException("Node '" + node.getNodeName()
374: + "' has no text");
375: } else {
376: return text;
377: }
378: }
379:
380: /**
381: * Gets a child node with a certain name.
382: * <p>
383: * If the node has more than one such children, then the first child is
384: * returned.
385: *
386: * @param node The node whichs child should be returned.
387: * @param childNodeName The name of the child node.
388: * @return The child node or <code>null</code> if there is no such child.
389: */
390: public static Node getChild(Node node, String childNodeName) {
391: NodeList list = node.getChildNodes();
392: for (int i = 0; i < list.getLength(); i++) {
393: Node child = list.item(i);
394: if (child.getNodeName().equals(childNodeName)) {
395: return child;
396: }
397: }
398:
399: // No such child found
400: return null;
401: }
402:
403: /**
404: * Gibt den Kindknoten mit einem bestimmten Namen zur�ck.
405: * <p>
406: * Falls der Knoten mehrere Kinder mit diesem Namen hat, so wird das erste Kind
407: * zur�ckgegeben.
408: * <p>
409: * Falls der Knoten kein Kind mit diesem Name hat, dann entscheidet
410: * <CODE>mandatory</CODE> dar�ber, ob eine Exception geworfen
411: * (<CODE>mandatory</CODE> ist <CODE>true</CODE>) oder ob <CODE>null</CODE>
412: * zur�ckgegeben werden soll (<CODE>mandatory</CODE> ist <CODE>false</CODE>)
413: *
414: * @param node Der Knoten, dessen Kind zur�ckgegeben werden soll.
415: * @param childNodeName Der Name des Kindknotens.
416: * @param mandatory Gibt an, ob eine Exception geworfen werden soll, falls der
417: * Knoten kein Kind mit diesem Namen hat.
418: *
419: * @return Der Kindknoten
420: * @throws RegainException Wenn der Knoten kein Kind mit diesem Namen hat und
421: * <CODE>mandatory</CODE> <CODE>true</CODE> ist.
422: */
423: public static Node getChild(Node node, String childNodeName,
424: boolean mandatory) throws RegainException {
425: Node childNode = getChild(node, childNodeName);
426:
427: if (mandatory && (childNode == null)) {
428: throw new RegainException("Node '" + node.getNodeName()
429: + "' must have a child named '" + childNodeName
430: + "'!");
431: } else {
432: return childNode;
433: }
434: }
435:
436: /**
437: * Gibt alle Kindknoten mit einem bestimmten Namen zur�ck.
438: * <p>
439: * Falls der Knoten kein Kind mit diesem Namen hat, so wird ein leeres Array
440: * zur�ckgegeben.
441: *
442: * @param node Der Knoten, dessen Kinder zur�ckgegeben werden soll.
443: * @param childNodeName Der Name der Kindknoten.
444: *
445: * @return Die Kindknoten.
446: */
447: public static Node[] getChildArr(Node node, String childNodeName) {
448: ArrayList list = new ArrayList();
449:
450: NodeList nodeList = node.getChildNodes();
451: for (int i = 0; i < nodeList.getLength(); i++) {
452: Node child = nodeList.item(i);
453: if (child.getNodeName().equals(childNodeName)) {
454: list.add(child);
455: }
456: }
457:
458: Node[] nodeArr = new Node[list.size()];
459: list.toArray(nodeArr);
460:
461: return nodeArr;
462: }
463:
464: /**
465: * Gets a child node from a parent node. If the parent node has no such child
466: * the child of the <code>defaultNode</code> is used.
467: *
468: * @param node The node to get the child from.
469: * @param defaultNode The node to get the child from if <code>node</code> has
470: * no such child.
471: * @param childNodeName The name of the child.
472: * @return The child with the given name or <code>null</code> if both the
473: * <code>node</code> and the <code>defaultNode</code> have no child
474: * with the given name.
475: */
476: public static Node getCascadedChild(Node node, Node defaultNode,
477: String childNodeName) {
478: Node child = XmlToolkit.getChild(node, childNodeName);
479: if (child == null) {
480: // Try to get the cascaded child
481: child = XmlToolkit.getChild(defaultNode, childNodeName);
482: }
483:
484: return child;
485: }
486:
487: /**
488: * Gets a child node from a parent node. If the parent node has no such child
489: * the child of the <code>defaultNode</code> is used.
490: *
491: * @param node The node to get the child from.
492: * @param defaultNode The node to get the child from if <code>node</code> has
493: * no such child.
494: * @param childNodeName The name of the child.
495: * @param mandatory Specifies whether to throw an exception if none of the
496: * nodes have such a child.
497: * @return The child with the given name.
498: * @throws RegainException If both the <code>node</code> and the
499: * <code>defaultNode</code> have no child with the given name and
500: * <code>mandatory</code> is <code>true</code>.
501: */
502: public static Node getCascadedChild(Node node, Node defaultNode,
503: String childNodeName, boolean mandatory)
504: throws RegainException {
505: Node child = getCascadedChild(node, defaultNode, childNodeName);
506: if (mandatory && (child == null)) {
507: throw new RegainException("Node '" + node.getNodeName()
508: + "' or node '" + defaultNode.getNodeName()
509: + "' must have a child named '" + childNodeName
510: + "'!");
511: }
512:
513: return child;
514: }
515:
516: /**
517: * Gets the text of a child node.
518: *
519: * @param node The (parent) node that has the child to get the text from.
520: * @param childNodeName The name of the child node.
521: * @param mandatory Specifies whether an exception should be thrown if the
522: * child has no text.
523: * @return The text of the child node or <code>null</code> if the child has no
524: * text and <code>mandatory</code> is <code>false</code>.
525: * @throws RegainException If the given node has no child with the given name
526: * of if the child node has no text and <code>mandatory</code> is
527: * <code>true</code>.
528: */
529: public static String getChildText(Node node, String childNodeName,
530: boolean mandatory) throws RegainException {
531: Node child = getChild(node, childNodeName, mandatory);
532: if (child == null) {
533: // NOTE: mandatory must be false otherwise getChild() would have thrown
534: // an exception
535: return null;
536: } else {
537: return getText(child, mandatory);
538: }
539: }
540:
541: /**
542: * Gets an attribute value from a node and converts it to a boolean.
543: *
544: * @param node The node to get the attribute value from.
545: * @param attributeName The name of the attribute to get.
546: * @return The value of the attribute or <code>defaultValue</code> if there is
547: * no such attribute.
548: * @throws RegainException If there is no such attribute or if the attribute
549: * value is no boolean.
550: */
551: public static boolean getAttributeAsBoolean(Node node,
552: String attributeName) throws RegainException {
553: String asString = getAttribute(node, attributeName, true);
554:
555: if (asString.equalsIgnoreCase("true")) {
556: return true;
557: } else if (asString.equalsIgnoreCase("false")) {
558: return false;
559: } else {
560: throw new RegainException("Attribute '" + attributeName
561: + "' of node '" + node.getNodeName()
562: + "' must be either 'true' or 'false': '"
563: + asString + "'");
564: }
565: }
566:
567: /**
568: * Gets an attribute value from a node and converts it to a boolean.
569: *
570: * @param node The node to get the attribute value from.
571: * @param attributeName The name of the attribute to get.
572: * @param defaultValue The default value to return if there is no such
573: * attribute.
574: * @return The value of the attribute or <code>defaultValue</code> if there is
575: * no such attribute.
576: * @throws RegainException If the attribute value is no boolean.
577: */
578: public static boolean getAttributeAsBoolean(Node node,
579: String attributeName, boolean defaultValue)
580: throws RegainException {
581: String asString = getAttribute(node, attributeName);
582:
583: if (asString == null) {
584: return defaultValue;
585: } else if (asString.equalsIgnoreCase("true")) {
586: return true;
587: } else if (asString.equalsIgnoreCase("false")) {
588: return false;
589: } else {
590: throw new RegainException("Attribute '" + attributeName
591: + "' of node '" + node.getNodeName()
592: + "' must be either 'true' or 'false': '"
593: + asString + "'");
594: }
595: }
596:
597: /**
598: * Gets an attribute value from a node and converts it to an int.
599: *
600: * @param node The node to get the attribute value from.
601: * @param attributeName The name of the attribute to get.
602: * @return The value of the attribute or <code>defaultValue</code> if there is
603: * no such attribute.
604: * @throws RegainException If there is no such attribute or if the attribute
605: * value is no int.
606: */
607: public static int getAttributeAsInt(Node node, String attributeName)
608: throws RegainException {
609: String asString = getAttribute(node, attributeName, true);
610:
611: try {
612: return Integer.parseInt(asString);
613: } catch (NumberFormatException exc) {
614: throw new RegainException("Attribute '" + attributeName
615: + "' of node '" + node.getNodeName()
616: + "' must be a number: '" + asString + "'");
617: }
618: }
619:
620: /**
621: * Gets an attribute value from a node and converts it to an int.
622: *
623: * @param node The node to get the attribute value from.
624: * @param attributeName The name of the attribute to get.
625: * @param defaultValue The default value to return if there is no such
626: * attribute.
627: * @return The value of the attribute or <code>defaultValue</code> if there is
628: * no such attribute.
629: * @throws RegainException If the attribute value is no int.
630: */
631: public static int getAttributeAsInt(Node node,
632: String attributeName, int defaultValue)
633: throws RegainException {
634: String asString = getAttribute(node, attributeName);
635:
636: if (asString == null) {
637: return defaultValue;
638: } else {
639: try {
640: return Integer.parseInt(asString);
641: } catch (NumberFormatException exc) {
642: throw new RegainException("Attribute '" + attributeName
643: + "' of node '" + node.getNodeName()
644: + "' must be a number: '" + asString + "'");
645: }
646: }
647: }
648:
649: /**
650: * Gets an attribute value from a node.
651: *
652: * @param node The node to get the attribute value from.
653: * @param attributeName The name of the wanted attribute.
654: * @return The attribute value or <code>null</code> if there is no such
655: * attribute.
656: */
657: public static String getAttribute(Node node, String attributeName) {
658: Node attributeNode = node.getAttributes().getNamedItem(
659: attributeName);
660: if (attributeNode == null) {
661: return null;
662: } else {
663: return attributeNode.getNodeValue();
664: }
665: }
666:
667: /**
668: * Gibt den Attributwert eines Knotens zur�ck.
669: *
670: * @param node Der Knoten, dessen Attribut zur�ckgegeben werden soll.
671: * @param attributeName Der Name des Attributs, das zur�ckgegeben werden soll.
672: * @param mandatory Gibt an, ob eine Exception geworfen werden soll, falls der
673: * Knoten kein solches Attribut hat.
674: *
675: * @return Den Attributwert.
676: * @throws RegainException Falls der Knoten kein solches Attribut hat und
677: * <CODE>mandatory</CODE> <CODE>true</CODE> ist.
678: */
679: public static String getAttribute(Node node, String attributeName,
680: boolean mandatory) throws RegainException {
681: String value = getAttribute(node, attributeName);
682: if (value == null) {
683: if (mandatory) {
684: throw new RegainException("Node '" + node.getNodeName()
685: + "' has no attribute '" + attributeName + "'");
686: } else {
687: return null;
688: }
689: } else {
690: return value;
691: }
692: }
693:
694: /**
695: * Sets the text of a node.
696: *
697: * @param doc The document the node comes from.
698: * @param node The node whichs text should be changed.
699: * @param text The text to set.
700: */
701: public static void setText(Document doc, Node node, String text) {
702: Node textNode = getChild(node, "#text");
703:
704: if (textNode == null) {
705: textNode = doc.createTextNode(text);
706: node.appendChild(textNode);
707: } else {
708: textNode.setNodeValue(text);
709: }
710: }
711:
712: /**
713: * Removes all child nodes from a node.
714: *
715: * @param node The node to remove the children from.
716: */
717: public static void removeAllChildren(Node node) {
718: NodeList nodeList = node.getChildNodes();
719: for (int i = nodeList.getLength() - 1; i >= 0; i--) {
720: node.removeChild(nodeList.item(i));
721: }
722: }
723:
724: /**
725: * Removes all child nodes with a certain name.
726: *
727: * @param node The node to remove the children from.
728: * @param childNodeName The name of the children to remove.
729: */
730: public static void removeAllChildren(Node node, String childNodeName) {
731: Node[] childArr = getChildArr(node, childNodeName);
732: for (int i = 0; i < childArr.length; i++) {
733: node.removeChild(childArr[i]);
734: }
735: }
736:
737: /**
738: * Adds a child node to a node.
739: *
740: * @param doc The document the node comes from.
741: * @param node The node were to add the child.
742: * @param childNodeName The name of the child node to add.
743: * @return The added child node.
744: */
745: public static Node addChild(Document doc, Node node,
746: String childNodeName) {
747: Node childNode = doc.createElement(childNodeName);
748: node.appendChild(childNode);
749: return childNode;
750: }
751:
752: /**
753: * Gets a child node or creates it if no such node exists.
754: *
755: * @param doc The document the node comes from.
756: * @param node The node were to get the child from or where to add the child.
757: * @param childNodeName The name of the child node to get or add.
758: * @return The child node.
759: */
760: public static Node getOrAddChild(Document doc, Node node,
761: String childNodeName) {
762: Node child = getChild(node, childNodeName);
763: if (child == null) {
764: child = addChild(doc, node, childNodeName);
765: }
766: return child;
767: }
768:
769: /**
770: * Adds a child node to a node and gives it a text.
771: *
772: * @param doc The document the node comes from.
773: * @param node The node where to add the child.
774: * @param childNodeName The name of the child node to add.
775: * @param text The text to set to the child.
776: * @return The added child node.
777: */
778: public static Node addChildWithText(Document doc, Node node,
779: String childNodeName, String text) {
780: Node childNode = addChild(doc, node, childNodeName);
781: setText(doc, childNode, text);
782: return childNode;
783: }
784:
785: /**
786: * Sets an attribute of a node.
787: *
788: * @param doc The document the node comes from.
789: * @param node The node where to set the attribute.
790: * @param attribName The name of the attribute to set.
791: * @param attribValue The value of the attribute to set.
792: */
793: public static void setAttribute(Document doc, Node node,
794: String attribName, String attribValue) {
795: Attr attr = doc.createAttribute(attribName);
796: attr.setNodeValue(attribValue);
797: node.getAttributes().setNamedItem(attr);
798: }
799:
800: /**
801: * Pretty prints a node.
802: *
803: * @param doc The document the node comes from.
804: * @param node The node that should be pretty printed.
805: */
806: public static void prettyPrint(Document doc, Node node) {
807: // Get the text before the node and extract the indenting
808: Node parent = node.getParentNode();
809:
810: String indenting = "";
811: NodeList siblingList = parent.getChildNodes();
812: for (int i = 1; i < siblingList.getLength(); i++) {
813: Node sibling = siblingList.item(i);
814: if (sibling == node) {
815: Node nodeBefore = siblingList.item(i - 1);
816: // Check whether this is a text node
817: if (nodeBefore.getNodeName().equals("#text")) {
818: // There is text before the node -> Extract the indenting
819: String text = nodeBefore.getNodeValue();
820: int newlinePos = text.lastIndexOf('\n');
821: if (newlinePos != -1) {
822: indenting = text.substring(newlinePos);
823: if (indenting.trim().length() != 0) {
824: // The indenting is no whitespace -> Forget it
825: indenting = "";
826: }
827: }
828: }
829: break;
830: }
831: }
832:
833: // Now pretty print the node
834: prettyPrint(doc, node, indenting);
835: }
836:
837: /**
838: * Pretty prints a node.
839: *
840: * @param doc The document the node comes from.
841: * @param node The node that should be pretty printed.
842: * @param prefix The prefix the node should get.
843: */
844: private static void prettyPrint(Document doc, Node node,
845: String prefix) {
846: String childPrefix = prefix + " ";
847:
848: // Add the indenting to the children
849: NodeList childList = node.getChildNodes();
850: boolean hasChildren = false;
851: for (int i = childList.getLength() - 1; i >= 0; i--) {
852: Node child = childList.item(i);
853: boolean isNormalNode = (!child.getNodeName()
854: .startsWith("#"));
855: if (isNormalNode) {
856: // Add the indenting to this node
857: Node textNode = doc.createTextNode(childPrefix);
858: node.insertBefore(textNode, child);
859:
860: // pretty print the child's children
861: prettyPrint(doc, child, childPrefix);
862:
863: hasChildren = true;
864: }
865: }
866:
867: // Add the indenting to the end tag
868: if (hasChildren) {
869: Node textNode = doc.createTextNode(prefix);
870: node.appendChild(textNode);
871: }
872: }
873:
874: }
|