KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > knowgate > dataxslt > XMLDocument


1 /*
2   Copyright (C) 2003 Know Gate S.L. All rights reserved.
3                       C/Oña, 107 1º2 28050 Madrid (Spain)
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8
9   1. Redistributions of source code must retain the above copyright
10      notice, this list of conditions and the following disclaimer.
11
12   2. The end-user documentation included with the redistribution,
13      if any, must include the following acknowledgment:
14      "This product includes software parts from hipergate
15      (http://www.hipergate.org/)."
16      Alternately, this acknowledgment may appear in the software itself,
17      if and wherever such third-party acknowledgments normally appear.
18
19   3. The name hipergate must not be used to endorse or promote products
20      derived from this software without prior written permission.
21      Products derived from this software may not be called hipergate,
22      nor may hipergate appear in their name, without prior written
23      permission.
24
25   This library is distributed in the hope that it will be useful,
26   but WITHOUT ANY WARRANTY; without even the implied warranty of
27   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
28
29   You should have received a copy of hipergate License with this code;
30   if not, visit http://www.hipergate.org or mail to info@hipergate.org
31 */

32
33 package com.knowgate.dataxslt;
34
35 import java.io.IOException JavaDoc;
36 import java.io.File JavaDoc;
37 import java.io.FileReader JavaDoc;
38 import java.io.UnsupportedEncodingException JavaDoc;
39
40 import com.knowgate.debug.DebugFile;
41 import com.knowgate.misc.Gadgets;
42 import com.knowgate.dfs.FileSystem;
43
44 import org.w3c.dom.DOMException JavaDoc;
45
46 /**
47  * Direct character-level manipulations for XML documents.
48  * This class modifies XML document files by directy seeking substrings inside
49  * its text. Given a well-knonw XML file structure is easier and faster to seek
50  * for <nodes> as substrings instead of parsing the whole documents into a
51  * DOM tree.
52  * @author Sergio Montoro Ten
53  * @version 2.2
54  */

55 public class XMLDocument {
56
57   private String JavaDoc sXMLDoc;
58   private String JavaDoc sFilePath;
59   private String JavaDoc sEncoding;
60   private FileSystem oFS;
61
62   public XMLDocument() {
63     oFS = new FileSystem();
64     sEncoding = "UTF-8";
65   }
66
67   // ----------------------------------------------------------
68

69   /**
70    * <p>Create XMLDocument and load an XML file into memory.</p>
71    * No node parsing is done, but file is loaded directly into a String.
72    * @param sFile File Path
73    * @throws IOException
74    * @throws OutOfMemoryError
75    */

76   public XMLDocument(String JavaDoc sFile) throws IOException JavaDoc, OutOfMemoryError JavaDoc {
77     oFS = new FileSystem();
78     sEncoding = "UTF-8";
79     load (sFile);
80   }
81
82   // ----------------------------------------------------------
83

84   /**
85    * Create XMLDocument and load an XML file into memory.
86    * @param sFile File Path
87    * @param sEnc Character Encoding
88    * @throws IOException
89    * @throws OutOfMemoryError
90    */

91   public XMLDocument(String JavaDoc sFile, String JavaDoc sEnc) throws IOException JavaDoc, OutOfMemoryError JavaDoc {
92     oFS = new FileSystem();
93     sEncoding = sEnc;
94     load (sFile);
95   }
96
97   // ----------------------------------------------------------
98

99   public String JavaDoc getCharacterEncoding() {
100     return sEncoding;
101   }
102
103   // ----------------------------------------------------------
104

105   public void setCharacterEncoding(String JavaDoc sEnc) {
106     sEnc = sEncoding;
107   }
108
109   // ----------------------------------------------------------
110

111   /**
112    * Load an XML file into memory.
113    * No node parsing is done, but file is loaded directly into a String.
114    * @param sFile File Path
115    * @param sEnc Character encoding
116    * @throws IOException
117    * @throws OutOfMemoryError
118    */

119   public void load (String JavaDoc sFile, String JavaDoc sEnc) throws IOException JavaDoc,OutOfMemoryError JavaDoc {
120
121     if (DebugFile.trace) {
122       DebugFile.writeln("Begin XMLDocument.load(" + sFile + "," + sEnc + ")");
123       DebugFile.incIdent();
124     }
125
126     sEncoding = sEnc;
127
128     try {
129       sXMLDoc = oFS.readfilestr(sFile, sEncoding);
130     } catch (com.enterprisedt.net.ftp.FTPException neverthrown) { }
131
132     sFilePath = sFile;
133
134     if (DebugFile.trace) {
135       DebugFile.decIdent();
136       DebugFile.writeln("End XMLDocument.load() : " + String.valueOf(sXMLDoc.length()));
137     }
138   } // load
139

140   // ----------------------------------------------------------
141

142   /**
143    * <p>Load an XML file into memory.</p>
144    * No node parsing is done, but file is loaded directly into a String.<br>
145    * Character encoding is UTF-8 by default unless changed by calling setEncoding()
146    * @param sFile File Path
147    * @throws IOException
148    * @throws OutOfMemoryError
149    */

150   public void load (String JavaDoc sFile) throws IOException JavaDoc,OutOfMemoryError JavaDoc {
151
152     if (DebugFile.trace) {
153       DebugFile.writeln("Begin XMLDocument.load(" + sFile + ")");
154       DebugFile.incIdent();
155     }
156
157     try {
158       sXMLDoc = oFS.readfilestr(sFile, sEncoding);
159     } catch (com.enterprisedt.net.ftp.FTPException neverthrown) { }
160
161     sFilePath = sFile;
162
163     if (DebugFile.trace) {
164       DebugFile.decIdent();
165       DebugFile.writeln("End XMLDocument.load() : " + String.valueOf(sXMLDoc.length()));
166     }
167   } // load
168

169   // ----------------------------------------------------------
170

171   /**
172    * Save file to disk.
173    * If it already exists it is overwritten.
174    * @param sFile File Path
175    * @throws IOException
176    */

177   public void save(String JavaDoc sFile) throws IOException JavaDoc {
178
179     if (DebugFile.trace) {
180       DebugFile.writeln("Begin XMLDocument.save(" + sFile + ")");
181       DebugFile.incIdent();
182     }
183
184     oFS.writefilestr (sFile, sXMLDoc, sEncoding);
185
186     if (DebugFile.trace) {
187
188       if (sXMLDoc.length()>0) {
189         if (Character.isISOControl(sXMLDoc.charAt(0)))
190           DebugFile.writeln("Warning: document " + sFile + " starts with ISO control characters");
191         if (Character.isISOControl(sXMLDoc.charAt(sXMLDoc.length()-1)))
192           DebugFile.writeln("Warning: document " + sFile + " ends with ISO control characters");
193       }
194
195       DebugFile.decIdent();
196
197       File JavaDoc oFileW = new File JavaDoc(sFile);
198       long lFileW = oFileW.length();
199
200       DebugFile.writeln("End XMLDocument.save() : " + String.valueOf(lFileW));
201     }
202   } // save
203

204   // ----------------------------------------------------------
205

206   /**
207    * Save file to disk.
208    * Save to same path used for loading the file.
209    * @throws IOException
210    */

211   public void save() throws IOException JavaDoc {
212     save (sFilePath);
213   }
214
215   // ----------------------------------------------------------
216

217   private static boolean isLastSibling (String JavaDoc sXMLDoc, int iFromIndex,
218                                          String JavaDoc sParent, String JavaDoc sSibling) {
219
220     int iEndParent = sXMLDoc.indexOf("</" + sParent, iFromIndex);
221     int iNextSibling = sXMLDoc.indexOf("<" + sSibling, iFromIndex);
222
223     if (iNextSibling==-1)
224       return true;
225     else
226       return iNextSibling>iEndParent;
227   } // isLastSibling
228

229   // ----------------------------------------------------------
230

231   private int seekNode(String JavaDoc sXPath) {
232     int iNodeCount;
233     int iNode;
234     int iAttr;
235     int iLeft;
236     int iRight;
237     String JavaDoc sCurrent;
238     String JavaDoc vNodes[]; // Array de nodos parseados
239
String JavaDoc vAttrs[]; // Array de atributos parseados
240
boolean bAttrs[]; // Guarda indicadores booleanos según se van encontrando los atributos
241
// para saber cual es el que no se encuentra en caso de fallo
242

243     if (DebugFile.trace) {
244       DebugFile.writeln("Begin XMLDocument.seekNode(" + sXPath + ")");
245       DebugFile.incIdent();
246     }
247
248     // Parsear la cadena de búsqueda XPath y colocar el resultado
249
// en dos arrays, uno para los nodos y otro para los atributos.
250
vNodes = Gadgets.split(sXPath, "/");
251     if (null==vNodes) {
252       iNodeCount = 0;
253       vAttrs = null;
254       bAttrs = null;
255     }
256     else {
257       iNodeCount = vNodes.length;
258       vAttrs = new String JavaDoc[iNodeCount];
259       bAttrs = new boolean[iNodeCount];
260       for (int a=0; a<iNodeCount; a++) bAttrs[a] = false;
261     }
262
263     for (int iTok=0; iTok<iNodeCount; iTok++) {
264
265       iLeft = vNodes[iTok].indexOf("[");
266       if (iLeft>0) {
267         iRight = vNodes[iTok].indexOf("]");
268
269         if (iRight==-1)
270           throw new DOMException JavaDoc(DOMException.INVALID_ACCESS_ERR, "missing right bracket");
271
272         // Sacar el contenido dentro de los corchetes
273
vAttrs[iTok] = vNodes[iTok].substring(iLeft+1,iRight);
274         // Eliminar las arrobas
275
vAttrs[iTok] = vAttrs[iTok].replace('@',' ');
276         // Cambiar las comillas simples por dobles
277
vAttrs[iTok] = vAttrs[iTok].replace((char)39,(char)34);
278         // Eliminar los espacios en blanco
279
vAttrs[iTok] = vAttrs[iTok].trim();
280         // Asignar al nodo el valor quitando el contenido de los corchetes
281
vNodes[iTok] = vNodes[iTok].substring(0, iLeft);
282       }
283       else
284         vAttrs[iTok] = "";
285
286       if (DebugFile.trace) DebugFile.writeln("Token " + String.valueOf(iTok) + " : node=" + vNodes[iTok] + ", attr=" + vAttrs[iTok]);
287     } // next (iTok)
288

289     // Buscar el nodo
290
iLeft = 0;
291     iNode = 0;
292
293     while (iNode<iNodeCount) {
294       // Primero recorrer el documento XML para buscar nodos coincidentes
295

296       iLeft = sXMLDoc.indexOf("<" + vNodes[iNode], iLeft);
297       if (iLeft<0) {
298         if (DebugFile.trace) DebugFile.writeln("Node " + vNodes[iNode] + " not found");
299         throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, "Node " + vNodes[iNode] + " not found");
300       } // fi(iLeft<0)
301

302       iRight = sXMLDoc.indexOf(">", iLeft+1);
303       if (iRight<0) {
304         if (DebugFile.trace) DebugFile.writeln("Unclosed Node " + vNodes[iNode] + " missing >");
305         throw new DOMException JavaDoc(DOMException.SYNTAX_ERR, "Unclosed Node " + vNodes[iNode] + " missing >");
306       }
307
308       sCurrent = sXMLDoc.substring(iLeft+1, iLeft+vNodes[iNode].length()+1);
309
310       if (vNodes[iNode].equals(sCurrent)) {
311
312         if (vAttrs[iNode].length()==0) {
313           // No hay atributos, dar por coincidente el primer nodo que aparezca
314
iNode++;
315         }
316
317         // Tratar de forma especial la función position() de XPath
318
else if (vAttrs[iNode].startsWith("position()")) {
319
320           String JavaDoc[] aAttrValue = Gadgets.split2(vAttrs[iNode], '=');
321
322           if (aAttrValue.length<2)
323             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, "position() function can only be declared equal to last() function");
324           else {
325             if (aAttrValue[1].equals("last()")) {
326               if ( isLastSibling (sXMLDoc, iRight, vNodes[iNode-1], sCurrent) ) {
327                 bAttrs[iNode] = true;
328                 iNode++;
329               } // fi (isLastSibling)
330
} // fi (aAttrValue[1])
331
else
332               throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, "position() function can only be declared equal to last() function");
333           }
334         }
335         else {
336           // Mirar si el valor del atributo del nodo actual coincide con el especificado en XPath
337
iAttr = sXMLDoc.indexOf(vAttrs[iNode], iLeft+1);
338           if (iAttr>iLeft && iAttr<iRight) {
339             bAttrs[iNode] = true;
340             iNode++;
341           }
342         }
343       } // fi(substring(<...)==vNode[])
344

345       if (iNode<iNodeCount) iLeft = iRight;
346     } // wend
347

348     if (0==iLeft) {
349       for (int b=0; b<iNodeCount; b++) {
350         if (false == bAttrs[b]) {
351           if (DebugFile.trace) DebugFile.writeln("Attribute " + vAttrs[b] + " of node " + vNodes[b] + " not found");
352           throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, "Attribute " + vAttrs[b] + " of node " + vNodes[b] + " not found");
353         } // fi(bAttrs[b])
354
} // next(b)
355
} // fi(iLeft<0)
356

357     if (DebugFile.trace) {
358       DebugFile.decIdent();
359       DebugFile.writeln("End XMLDocument.seekNode() : " + String.valueOf(iLeft));
360     }
361
362     return iLeft;
363   } // seekNode
364

365   // ----------------------------------------------------------
366

367   /**
368    * Get loaded file as a String
369    */

370   public String JavaDoc toString() {
371     return sXMLDoc;
372   }
373
374   // ----------------------------------------------------------
375

376   /**
377    * Add a piece of XML text after a given node identifier by an XPath expression.
378    * @param sAfterXPath Restricted XPath expression for node after witch the next node is to be placed.
379    * For example : <br>
380    * "pageset/pages/page[@guid="123456789012345678901234567890AB"]/blocks/block[@id="003"]" will add sNode text after <block id="003">...</block> substring.<br>
381    * "pageset/pages/page[position()=last()]" will add sNode text after last <page>...</page> substring.
382    * @param sNode XML Text to be added.
383    * @throws DOMException
384    * DOMException Codes:<br>
385    * <table border=1 cellpadding=4>
386    * <tr><td>NOT_FOUND_ERR</td><td>A node or attribute from the XPath expression was not found</td></tr>
387    * <tr><td>INVALID_ACCESS_ERR</td><td>An attribute expression is invalid</td></tr>
388    * <tr><td>NOT_SUPPORTED_ERR</td><td>position() function was used but last() was not specified as value for it</td></tr>
389    * </table>
390    */

391   public void addNode(String JavaDoc sAfterXPath, String JavaDoc sNode) throws DOMException JavaDoc {
392     String JavaDoc sCloseParent;
393     int iOpenParent;
394     int iCloseParent;
395     int iTailParent;
396     int iAngle;
397     int iSpace;
398     char b;
399
400     if (DebugFile.trace) {
401       DebugFile.writeln("Begin XMLDocument.addNode(" + sAfterXPath + "," + sNode + ")");
402       DebugFile.incIdent();
403     }
404
405     iOpenParent = seekNode(sAfterXPath);
406
407     if (DebugFile.trace) DebugFile.writeln("iOpenParent=" + String.valueOf(iOpenParent));
408
409     iSpace = sXMLDoc.indexOf(" ", iOpenParent);
410     if (-1==iSpace) iSpace = 2147483647;
411     iAngle = sXMLDoc.indexOf(">", iOpenParent);
412     if (-1==iAngle) iSpace = 2147483647;
413
414     sCloseParent = "</" + sXMLDoc.substring(iOpenParent+1, iSpace<iAngle ? iSpace : iAngle) + ">";
415
416     if (DebugFile.trace) DebugFile.writeln("sCloseParent=" + sCloseParent);
417
418     iCloseParent = sXMLDoc.indexOf(sCloseParent, iOpenParent);
419
420     if (DebugFile.trace) DebugFile.writeln("iCloseParent=" + String.valueOf(iCloseParent));
421
422     if (iCloseParent<=0)
423       throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, "Node " + sCloseParent + " not found");
424
425     iSpace = 0;
426     iAngle = iCloseParent - 1;
427     b = sXMLDoc.charAt(iAngle);
428     while (b==' ' || b=='\t') {
429       iCloseParent--;
430       iSpace++;
431       b = sXMLDoc.charAt(--iAngle);
432     } // wend
433

434     iCloseParent = iCloseParent+sCloseParent.length()+iSpace;
435     iTailParent = iCloseParent;
436
437     if (iTailParent<sXMLDoc.length())
438       while (sXMLDoc.charAt(iTailParent)==(char)13 || sXMLDoc.charAt(iTailParent)==(char)10) iTailParent++;
439
440     StringBuffer JavaDoc oXMLDoc = new StringBuffer JavaDoc (iCloseParent+sNode.length()+(sXMLDoc.length()-iTailParent)+4);
441
442     oXMLDoc.append(sXMLDoc.substring(0, iCloseParent));
443     oXMLDoc.append(sNode);
444     oXMLDoc.append('\n');
445
446     if (iTailParent<sXMLDoc.length())
447       oXMLDoc.append(sXMLDoc.substring(iTailParent));
448
449     sXMLDoc = oXMLDoc.toString();
450     oXMLDoc = null;
451
452     if (DebugFile.trace) {
453       DebugFile.decIdent();
454       DebugFile.writeln("End XMLDocument.addNode()");
455     }
456   } // addNode
457

458   // ----------------------------------------------------------
459

460   /**
461    * Add a piece of XML text after a given node and save document.
462    * Document is saved to the same file path where if was loaded.
463    * @param sAfterXPath Restricted XPath expression for node after witch the next node is to be placed.
464    * @param sNode XML Text to be added.
465    * @throws DOMException
466    * @throws IOException
467    */

468   public void addNodeAndSave(String JavaDoc sAfterXPath, String JavaDoc sNode) throws DOMException JavaDoc,IOException JavaDoc {
469     String JavaDoc sCloseParent;
470     int iOpenParent;
471     int iCloseParent;
472     int iTailParent;
473     int iAngle;
474     int iSpace;
475     char b;
476
477     if (DebugFile.trace) {
478       DebugFile.writeln("Begin XMLDocument.addNodeAndSave(" + sAfterXPath + "," + sNode + ")");
479       DebugFile.incIdent();
480     }
481
482     iOpenParent = seekNode(sAfterXPath);
483
484     if (DebugFile.trace) DebugFile.writeln("iOpenParent=" + String.valueOf(iOpenParent));
485
486     iSpace = sXMLDoc.indexOf(" ", iOpenParent);
487     if (-1==iSpace) iSpace = 2147483647;
488     iAngle = sXMLDoc.indexOf(">", iOpenParent);
489     if (-1==iAngle) iSpace = 2147483647;
490
491     sCloseParent = "</" + sXMLDoc.substring(iOpenParent+1,iSpace<iAngle ? iSpace : iAngle) + ">";
492
493     if (DebugFile.trace) DebugFile.writeln("sCloseParent=" + sCloseParent);
494
495     iCloseParent = sXMLDoc.indexOf(sCloseParent, iOpenParent);
496
497     if (DebugFile.trace) DebugFile.writeln("iCloseParent=" + String.valueOf(iCloseParent));
498
499     if (iCloseParent<=0)
500       throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, "Node " + sCloseParent + " not found");
501
502     iSpace = 0;
503     iAngle = iCloseParent - 1;
504     b = sXMLDoc.charAt(iAngle);
505     while (b==' ' || b=='\t') {
506       iCloseParent--;
507       iSpace++;
508       b = sXMLDoc.charAt(--iAngle);
509     } // wend
510

511     iCloseParent = iCloseParent+sCloseParent.length()+iSpace;
512     iTailParent = iCloseParent;
513
514     if (iTailParent<sXMLDoc.length())
515       while (sXMLDoc.charAt(iTailParent)==(char)13 || sXMLDoc.charAt(iTailParent)==(char)10) iTailParent++;
516
517     StringBuffer JavaDoc oXMLDoc = new StringBuffer JavaDoc (iCloseParent+sNode.length()+(sXMLDoc.length()-iTailParent)+4);
518
519     oXMLDoc.append(sXMLDoc.substring(0, iCloseParent));
520     oXMLDoc.append(sNode);
521     oXMLDoc.append('\n');
522
523     if (iTailParent<sXMLDoc.length())
524       oXMLDoc.append(sXMLDoc.substring(iTailParent));
525
526     sXMLDoc = oXMLDoc.toString();
527     oXMLDoc = null;
528
529     if (DebugFile.trace) {
530       if (sXMLDoc.length()>0) {
531         if (Character.isISOControl(sXMLDoc.charAt(0)))
532           DebugFile.writeln("Warning: document " + sFilePath + " starts with ISO control characters");
533         if (Character.isISOControl(sXMLDoc.charAt(sXMLDoc.length()-1)))
534           DebugFile.writeln("Warning: document " + sFilePath + " ends with ISO control characters");
535       }
536     }
537
538     oFS.writefilestr(sFilePath, sXMLDoc, sEncoding);
539
540     if (DebugFile.trace) {
541       DebugFile.decIdent();
542
543       File JavaDoc oFileW = new File JavaDoc(sFilePath);
544       long lFileW = oFileW.length();
545
546       DebugFile.writeln("End XMLDocument.addNodeAndSave() : " + String.valueOf(lFileW));
547     }
548   } // addNodeAndSave
549

550   // ----------------------------------------------------------
551

552   /**
553    * Remove a node.
554    * @param sXPath Restricted XPath expression for node to remove.
555    * For example: "pageset/pages/page[@guid="123456789012345678901234567890AB"]/blocks/block[@id="003"]"
556    * will remove <block id="003">...</block> substring.
557    * @throws DOMException
558    */

559   public void removeNode(String JavaDoc sXPath) throws DOMException JavaDoc {
560     int iStartParent;
561     int iEndParent;
562     int iBracket;
563     String JavaDoc sNodeName;
564     String JavaDoc vNodes[];
565
566     if (DebugFile.trace) {
567       DebugFile.writeln("Begin XMLDocument.removeNode(" + sXPath + ")");
568       DebugFile.incIdent();
569     }
570
571     iStartParent = seekNode(sXPath);
572
573     vNodes = Gadgets.split(sXPath, "/");
574
575     iBracket = vNodes[vNodes.length-1].indexOf("[");
576
577     if (iBracket>0)
578       sNodeName = vNodes[vNodes.length-1].substring(0, iBracket);
579     else
580       sNodeName = vNodes[vNodes.length-1];
581
582     iEndParent = sXMLDoc.indexOf("</" + sNodeName + ">", iStartParent) + sNodeName.length()+3;
583
584     if (iEndParent==0)
585       throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, "Node " + "</" + sNodeName + ">" + " not found");
586
587     // Quitar los espacios por delante del nodo
588
for (char b = sXMLDoc.charAt(iStartParent);
589         (b==' ') && iStartParent>0;
590          b = sXMLDoc.charAt(--iStartParent)) ;
591
592     // Quitar los saltos de línea y retornos de carro por detrás del nodo
593
for (char c = sXMLDoc.charAt(iEndParent);
594         (c=='\r' || c=='\n') && iEndParent<sXMLDoc.length();
595          c = sXMLDoc.charAt(++iEndParent)) ;
596
597     sXMLDoc = sXMLDoc.substring(0, iStartParent) + sXMLDoc.substring(iEndParent);
598
599     if (DebugFile.trace) {
600       DebugFile.decIdent();
601       DebugFile.writeln("End XMLDocument.removeNode()");
602     }
603   } // removeNode
604

605   // ----------------------------------------------------------
606

607   /**
608    * Remove a node and save document.
609    * Document is saved to the same file path where if was loaded.
610    * @param sXPath XPath expression for node to remove.
611    * @throws DOMException
612    * @throws IOException
613    */

614   public void removeNodeAndSave(String JavaDoc sXPath) throws DOMException JavaDoc,IOException JavaDoc {
615
616     if (DebugFile.trace) {
617       DebugFile.writeln("Begin XMLDocument.removeNodeAndSave(" + sXPath + ")");
618       DebugFile.incIdent();
619     }
620
621     removeNode(sXPath);
622
623     oFS.writefilestr(sFilePath, sXMLDoc, sEncoding);
624
625     if (DebugFile.trace) {
626       DebugFile.decIdent();
627       DebugFile.writeln("End XMLDocument.removeNodeAndSave()");
628     }
629   } // removeNodeAndSave
630

631   // ----------------------------------------------------------
632
} // XMLDocument
633
Popular Tags