Source code: diffxml/Patch/Patch.java
1 /*
2 Program to apply a DUL patch
3
4 Copyright (C) 2002 Adrian Mouat
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 Author: Adrian Mouat
21 email: amouat@postmaster.co.uk
22 */
23 package diffxml.Patch;
24
25
26 /*
27 Program to apply DUL patches
28 */
29
30 import diffxml.*;
31
32 import org.apache.xpath.XPathAPI;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NodeList;
37 import org.w3c.dom.traversal.*;
38 import org.apache.xerces.parsers.DOMParser;
39 import org.w3c.dom.NamedNodeMap;
40 import javax.xml.transform.TransformerException;
41 import org.apache.xerces.dom3.Node3;
42 import javax.xml.transform.*;
43 import javax.xml.transform.stream.*;
44 import javax.xml.transform.dom.*;
45 import org.xml.sax.SAXException;
46 import java.io.File;
47
48 public class Patch
49 {
50 static boolean reverse=false;
51
52 //Currently breaks from unix patch, file not implicitly written to
53 //To change this set the boolean dryrun to false
54 //You will then need to use -dry-run to avoid overwriting files
55
56 static boolean dryrun=true;
57 static String file1;
58 static String file2;
59
60 public static void parseArgs(String[] args)
61 {
62 int i=0;
63 char flag;
64 String arg;
65
66 while (i < args.length && args[i].startsWith("-"))
67 {
68 arg = args[i++];
69
70 //Normalize multiple dashes
71 //Don't understand point in differentiating between 1 and 2 dashes
72 //We allow 2 in order to mimic patch util
73
74 if (arg.startsWith("--"))
75 arg=arg.substring(1);
76
77 //"wordy" arguments
78 if (arg.equals("-version"))
79 printVersion();
80 else if (arg.equals("-help"))
81 printHelp();
82 else if (arg.equals("-dry-run"))
83 dryrun=true;
84 else if (arg.equals("-reverse"))
85 reverse=true;
86
87 //(series of) flag arguments
88 else
89 {
90 for (int j = 1; j < arg.length(); j++)
91 {
92 flag = arg.charAt(j);
93 switch (flag)
94 {
95 case 'V':
96 printVersion();
97 break;
98 case 'h':
99 printHelp();
100 case 'd':
101 dryrun=true;
102 case 'R':
103 reverse=true;
104
105 default:
106 System.err.println("Patch: illegal option " + flag);
107 System.exit(2);
108 break;
109 }
110 }
111 }
112 }
113 if ((i+2) != args.length)
114 printUsage();
115
116 file1=args[i];
117 //db.p("file1= " + file1);
118 file2=args[++i];
119 //db.p("file2= " + file2);
120
121 }
122
123 public static void printUsage()
124 {
125 System.err.println("Usage: patch [OPTION]... [ORIGFILE [PATCHFILE]]");
126 System.exit(2);
127 }
128
129 public static void printHelp()
130 {
131 System.out.print("\nUsage: patch [OPTION]... [ORIGFILE [PATCHFILE]]\n\nApply a diffxml file to one of the original XML files.\n\n --version -V Output version number of program.\n --help -h Print summary of options and exit. \n --dry-run -d Print results of applying the changes without modifying any files. \n --reverse -R Assume that the delta file was created with the old and new files swapped.\n\tAttempt to reverse sense of change before applying it, e.g. inserts become deletes.\n\n");
132 System.out.print("\nThis product includes software developed by the Indiana University Extreme! Lab (http://www.extreme.indiana.edu/).\n\n");
133 System.out.print("\nThis product includes software developed by the Apache Software Foundation (http://www.apache.org/).\n\n");
134 System.exit(0);
135 }
136
137 public static void printVersion()
138 {
139 System.out.println("patchxml Version 0.9");
140 System.out.print("\nThis product includes software developed by the Indiana University Extreme! Lab (http://www.extreme.indiana.edu/).\n");
141 System.out.print("\nThis product includes software developed by the Apache Software Foundation (http://www.apache.org/).\n\n");
142 System.exit(0);
143 }
144
145
146 public void apply(Document doc, Document patch)
147 {
148 NodeIterator ni = ((DocumentTraversal) patch).createNodeIterator
149 (patch.getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, false);
150
151 Node op = ni.nextNode();
152 //Check we have a delta
153 if (!op.getNodeName().equals("delta"))
154 {
155 System.err.println("Not a delta document!");
156 return;
157 }
158
159 //Cycle through elements apply ops
160 op = ni.nextNode();
161
162 int instr=0;
163 while (op!=null)
164 {
165 String op_name=op.getNodeName();
166 NamedNodeMap op_attrs=op.getAttributes();
167 instr++;
168 db.p(instr + " " + op);
169 /*
170 if (op_name.equals("update"))
171 {
172 Node node;
173 try {
174 node=XPathAPI.selectSingleNode
175 (doc.getDocumentElement(), op_attrs.getNamedItem("node").getNodeValue());
176
177 //Currently all update does is change the name
178 //This actually breaks the DUL but its a quick hack to get things working
179
180 Element new_node=doc.createElement(op_attrs.getNamedItem("name").getNodeValue());
181
182 }
183 catch (TransformerException e)
184 { System.err.println("Could not resolve XPath for parent for update");}
185 }
186 */
187 if (op_name.equals("insert"))
188 {
189 db.p("Applying insert");
190 /*
191 ============
192 Apply Insert
193 ============
194 */
195 //For all NodeTypes, find parent
196 Node parent;
197 Node ins;
198 try {
199 parent=XPathAPI.selectSingleNode
200 (doc.getDocumentElement(), op_attrs.getNamedItem("parent").getNodeValue());
201 db.p("Insert as child of " + parent.getNodeName());
202
203 //Different ops dependent on NodeType being inserted
204
205
206 int type=new Integer( op_attrs.getNamedItem("nodetype").getNodeValue() ).intValue();
207 //Note setting cn to 0
208 int xpath_cn=0;
209 int dom_cn=0;
210 if (op_attrs.getNamedItem("childno") != null)
211 xpath_cn=new Integer( op_attrs.getNamedItem("childno").getNodeValue() ).intValue();
212
213 //Convert xpath childno to DOM childno
214
215 //Need child nodes in all cases but attr
216 //dom_cn needs to store *first* node eqv to XPath
217 NodeList doc_kids = parent.getChildNodes();
218 if (type!=Node.ATTRIBUTE_NODE)
219 {
220 int index=0;
221 int j;
222 for (j=0;j<doc_kids.getLength();j++)
223 {
224 index++;
225 if (j>0 && doc_kids.item(j).getNodeType()==Node.TEXT_NODE
226 && doc_kids.item(j-1).getNodeType()==Node.TEXT_NODE)
227 index--;
228 if (index==xpath_cn)
229 break;
230 }
231 dom_cn=j;
232
233 }
234
235 //Get value to insert if any
236 String value="";
237 NodeList op_kids = op.getChildNodes();
238
239 if (op_kids.getLength() > 1)
240 System.err.println("Unexpected kiddy winkles in insert operation");
241 else if ( (op_kids.getLength() == 1) && (op_kids.item(0).getNodeType()==Node.TEXT_NODE) )
242 value=op_kids.item(0).getNodeValue();
243
244 int charpos=1;
245 if (op_attrs.getNamedItem("charpos") != null)
246 charpos=new Integer( op_attrs.getNamedItem("charpos").getNodeValue() ).intValue();
247 boolean append=false;
248 switch (type)
249 {
250 case Node.TEXT_NODE:
251 ins=doc.createTextNode(value);
252 db.p("dom_cn=" + dom_cn + " Inserting text: " + value);
253 //careful with cn
254 if (dom_cn==doc_kids.getLength() &&
255 doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
256 {
257 if (charpos<=1)
258 {
259 //Assume we actually mean to append node
260 append=true;
261 }
262 else
263 {
264 //Move back to the start of the text nodes
265 while (dom_cn>0 && doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
266 dom_cn--;
267
268 //move to charpos
269 while ( charpos>doc_kids.item(dom_cn).getNodeValue().length())
270 {
271 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
272 dom_cn++;
273 if (dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
274 {
275 //System.err.println("charpos beyond end of text");
276 append=true;
277 parent.appendChild(ins);
278 break;
279 }
280 }
281 }
282 if (!append)
283 parent.insertBefore(ins,doc_kids.item(dom_cn));
284
285 }
286 else if (doc_kids.getLength()>(dom_cn) )
287 {
288 //We have a node to insert before
289 //Do the charpos thingy.
290 if (doc_kids.item(dom_cn).getNodeType()==Node.TEXT_NODE)
291 {
292 //dom_cn is first text node
293 //move to charpos
294 while ( charpos>doc_kids.item(dom_cn).getNodeValue().length())
295 {
296 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
297 dom_cn++;
298 if (dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
299 {
300 System.err.println("charpos beyond end of text");
301 append=true;
302 parent.appendChild(ins);
303 break;
304 }
305 }
306 //charpos in current node.
307 //Won't consider splitting nodes at mo as unneccesary
308 }
309 if ( charpos!=1)
310 System.err.println("charpos seems to be out");
311 if (!append)
312 parent.insertBefore(ins,doc_kids.item(dom_cn));
313 }
314 else
315 parent.appendChild(ins);
316 break;
317 case Node.ELEMENT_NODE:
318 String tag=op_attrs.getNamedItem("name").getNodeValue();
319
320 ins=doc.createElement(tag);
321 if (dom_cn==doc_kids.getLength() &&
322 doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
323 {
324 if (charpos<=1)
325 {
326 //Assume we actually mean to append node
327 append=true;
328 }
329 else
330 {
331 //Move back to the start of the text nodes
332 while (dom_cn>0 && doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
333 dom_cn--;
334
335 //move to charpos
336 while ( charpos>doc_kids.item(dom_cn).getNodeValue().length())
337 {
338 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
339 dom_cn++;
340 if (dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
341 {
342 //System.err.println("charpos beyond end of text");
343 append=true;
344 parent.appendChild(ins);
345 break;
346 }
347 }
348 }
349 if (!append)
350 parent.insertBefore(ins,doc_kids.item(dom_cn));
351
352 }
353 else if (doc_kids.getLength()>(dom_cn))
354 {
355 //We have a node to insert before
356 //Do the charpos thingy.
357 if (doc_kids.item(dom_cn).getNodeType()==Node.TEXT_NODE)
358 {
359 //dom_cn is first text node
360 //move to charpos
361 while ( charpos>doc_kids.item(dom_cn).getNodeValue().length())
362 {
363 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
364 dom_cn++;
365 if ( dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
366 {
367 //System.err.println("charpos beyond end of text");
368 append=true;
369 parent.appendChild(ins);
370 break;
371 }
372 }
373 //charpos in current node.
374 //Won't consider splitting nodes at mo as unneccesary
375 }
376 if (!append)
377 parent.insertBefore(ins,doc_kids.item(dom_cn));
378 }
379 else
380 parent.appendChild(ins);
381 break;
382 case Node.COMMENT_NODE:
383 ins=doc.createComment(value);
384 if (dom_cn==doc_kids.getLength() &&
385 doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
386 {
387 if (charpos<=1)
388 {
389 //Assume we actually mean to append node
390 append=true;
391 }
392 else
393 {
394 //Move back to the start of the text nodes
395 while (dom_cn>0 && doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
396 dom_cn--;
397
398 //move to charpos
399 while ( dom_cn==doc_kids.getLength() || charpos>doc_kids.item(dom_cn).getNodeValue().length())
400 {
401 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
402 dom_cn++;
403 if (doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
404 {
405 //System.err.println("charpos beyond end of text");
406 append=true;
407 parent.appendChild(ins);
408 break;
409 }
410 }
411 }
412 if (!append)
413 parent.insertBefore(ins,doc_kids.item(dom_cn));
414
415 }
416 else if (doc_kids.getLength()>(dom_cn) )
417 {
418 //We have a node to insert before
419 //Do the charpos thingy.
420 if (doc_kids.item(dom_cn).getNodeType()==Node.TEXT_NODE)
421 {
422 //dom_cn is first text node
423 //move to charpos
424 while ( charpos>doc_kids.item(dom_cn).getNodeValue().length())
425 {
426 charpos=charpos-doc_kids.item(dom_cn).getNodeValue().length();
427 dom_cn++;
428 if (dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
429 {
430 //System.err.println("charpos beyond end of text");
431 append=true;
432 parent.appendChild(ins);
433 break;
434 }
435 }
436 //charpos in current node.
437 //Won't consider splitting nodes at mo as unneccesary
438 }
439 if (!append)
440 parent.insertBefore(ins,doc_kids.item(dom_cn));
441 }
442 else
443 parent.appendChild(ins);
444 break;
445 case Node.ATTRIBUTE_NODE:
446 String name=op_attrs.getNamedItem("name").getNodeValue();
447 ( (Element) parent).setAttribute(name, value);
448 break;
449 default:
450 System.err.println("Unknown NodeType " + type);
451 return;
452 }
453
454
455 } catch (TransformerException e)
456 { System.err.println("Could not resolve XPath for parent for insert");}
457
458 }
459 else if (op_name.equals("delete"))
460 {
461 /*=============
462 Applying Delete
463 =============*/
464 db.p("Applying delete");
465 try {
466 //According to API returns *first* match, so should be first text node if text node matched
467 Node3 del_node=(Node3) XPathAPI.selectSingleNode
468 (doc.getDocumentElement(), op_attrs.getNamedItem("node").getNodeValue());
469
470 if (del_node==null)
471 {
472 System.err.println("Could not find node to delete " + op_attrs.getNamedItem("node").getNodeValue());
473 break;
474 }
475 int charpos=1;
476 if (op_attrs.getNamedItem("charpos") != null)
477 charpos=new Integer( op_attrs.getNamedItem("charpos").getNodeValue() ).intValue();
478
479 //Currently only consider deleting nodes, not part of nodes
480 if (del_node.getNodeType()==Node.TEXT_NODE)
481 {
482 //Get del_node as item of parents child nodelist
483 NodeList del_nodelist=del_node.getParentNode().getChildNodes();
484 int i=0;
485 while ( !del_node.isSameNode(del_nodelist.item(i)) )
486 i++;
487
488 /*
489 while (charpos>0)
490 {
491 charpos=charpos-del_nodelist.item(i).getNodeValue().length();
492 i++;
493 }
494 */
495 while (charpos>del_nodelist.item(i).getNodeValue().length())
496 {
497 charpos=charpos-del_nodelist.item(i).getNodeValue().length();
498 i++;
499 }
500 del_node=(Node3) del_nodelist.item(i);
501 }
502 db.p("Deleting node " + del_node.getNodeName() + " " + del_node.getNodeValue() );
503 if (op_attrs.getNamedItem("length") != null)
504 {
505 db.p("Supposed length " + op_attrs.getNamedItem("length").getNodeValue());
506 db.p("Actual length " + del_node.getNodeValue().length());
507 }
508
509 del_node.getParentNode().removeChild(del_node);
510
511 } catch (TransformerException e)
512 { System.err.println("Could not resolve XPath for node to be deleted");}
513
514 }
515 else if (op_name.equals("move"))
516 {
517 db.p("Applying move");
518 /*=========
519 Apply Move
520 =========*/
521 db.p("Node: " + op_attrs.getNamedItem("node").getNodeValue() + " Parent" + op_attrs.getNamedItem("parent").getNodeValue());
522 db.p("childno" + op_attrs.getNamedItem("childno").getNodeValue());
523 if (op_attrs.getNamedItem("length") != null)
524 db.p("length " + op_attrs.getNamedItem("length").getNodeValue());
525
526 //First find the node
527 try {
528 Node3 mov_node=(Node3) XPathAPI.selectSingleNode
529 (doc.getDocumentElement(), op_attrs.getNamedItem("node").getNodeValue());
530
531 if (mov_node==null)
532 System.err.println("Could not find node to move " + op_attrs.getNamedItem("node").getNodeValue());
533
534 int old_charpos=1;
535 if (op_attrs.getNamedItem("old_charpos") != null)
536 old_charpos=new Integer( op_attrs.getNamedItem("old_charpos").getNodeValue() ).intValue();
537 db.p("old_charpos= " +old_charpos);
538
539
540 //Currently only consider deleting nodes, not part of nodes
541 if (mov_node.getNodeType()==Node.TEXT_NODE)
542 {
543 //Get del_node as item of parents child nodelist
544 NodeList mov_nodelist=mov_node.getParentNode().getChildNodes();
545 int i=0;
546 while ( !mov_node.isSameNode(mov_nodelist.item(i)) )
547 i++;
548
549 db.p("i=" +i + " max=" + mov_nodelist.getLength());
550 db.p("length matched node=" + mov_node.getNodeValue().length());
551 if (op_attrs.getNamedItem("length") != null)
552 db.p("Supposed length=" + op_attrs.getNamedItem("length").getNodeValue().length());
553 if (i>0 && mov_nodelist.item(i-1).getNodeType()==Node.TEXT_NODE)
554 System.err.println("Failed to find leftmost text node for match");
555
556 while (old_charpos>1)
557 {
558 db.p("old_charpos="+old_charpos);
559 db.p("length="+mov_nodelist.item(i).getNodeValue().length());
560 old_charpos=old_charpos-mov_nodelist.item(i).getNodeValue().length();
561 i++;
562 /*
563 if (i==mov_nodelist.getLength())
564 i--;
565 */
566 if (i==mov_nodelist.getLength() || mov_nodelist.item(i).getNodeType()!=Node.TEXT_NODE)
567 break;
568 //Probably want to check not greater than 1 b4 break
569 }
570
571 if (i==mov_nodelist.getLength())
572 {
573 System.err.println("Something looks wrong in move");
574 i--;
575 }
576
577 if (op_attrs.getNamedItem("length") != null)
578 db.p("Supposed length=" + op_attrs.getNamedItem("length").getNodeValue().length());
579
580 mov_node=(Node3) mov_nodelist.item(i);
581 if (mov_node==null)
582 System.err.println("old_charpos past end of text node");
583
584 if (op_attrs.getNamedItem("length")!=null)
585 if (mov_node.getNodeValue().length()!=op_attrs.getNamedItem("length").getNodeValue().length())
586 {
587 System.err.println("!!!!!!!!!\nMoving Wrong Text Node\n!!!!!!!!!");
588 System.err.println("i=" + i);
589 System.err.println("Actual length" + mov_node.getNodeValue().length());
590 }
591 }
592
593 //Find position to move to
594 //Get parent
595 Node parent=XPathAPI.selectSingleNode
596 (doc.getDocumentElement(), op_attrs.getNamedItem("parent").getNodeValue());
597
598 //Get XPath childno
599 int xpath_cn=0;
600 if (op_attrs.getNamedItem("childno") != null)
601 xpath_cn=new Integer( op_attrs.getNamedItem("childno").getNodeValue() ).intValue();
602
603 //Convert to DOM childno
604
605 NodeList doc_kids = parent.getChildNodes();
606 int index=0;
607 int dom_cn=0;
608 int j;
609 for (j=0;j<doc_kids.getLength();j++)
610 {
611 index++;
612 if (j>0 && doc_kids.item(j).getNodeType()==Node.TEXT_NODE
613 && doc_kids.item(j-1).getNodeType()==Node.TEXT_NODE)
614 index--;
615 if (index==xpath_cn)
616 break;
617 }
618 dom_cn=j;
619
620 //Get new charpos
621 int new_charpos=1;
622 if (op_attrs.getNamedItem("new_charpos") != null)
623 new_charpos=new Integer( op_attrs.getNamedItem("new_charpos").getNodeValue() ).intValue();
624 db.p("new_charpos" + new_charpos);
625
626 //Perform insert
627 //Remove should happen automagically
628 //Need to check children are moved properly
629 boolean append=false;
630 if (dom_cn==doc_kids.getLength() && doc_kids.getLength()!=0 &&
631 doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
632 {
633 if (new_charpos<=1)
634 {
635 //Assume we actually mean to append node
636 append=true;
637 }
638 else
639 {
640 //Move back to the start of the text nodes
641 while (dom_cn>0 && doc_kids.item(dom_cn-1).getNodeType()==Node.TEXT_NODE)
642 dom_cn--;
643
644 //move to charpos
645 while ( new_charpos>doc_kids.item(dom_cn).getNodeValue().length())
646 {
647 new_charpos=new_charpos-doc_kids.item(dom_cn).getNodeValue().length();
648 dom_cn++;
649 if (dom_cn==doc_kids.getLength() || doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
650 {
651 //System.err.println("charpos beyond end of text");
652 append=true;
653 parent.appendChild(mov_node);
654 break;
655 }
656 }
657 }
658 if (!append)
659 parent.insertBefore(mov_node,doc_kids.item(dom_cn));
660
661 }
662 else if (doc_kids.getLength()>(dom_cn) )
663 {
664 //We have a node to insert before
665 //Do the charpos thingy.
666 if (doc_kids.item(dom_cn).getNodeType()==Node.TEXT_NODE)
667 {
668 //dom_cn is first text node
669 //move to charpos
670 while ( new_charpos>doc_kids.item(dom_cn).getNodeValue().length())
671 {
672 new_charpos=new_charpos-doc_kids.item(dom_cn).getNodeValue().length();
673 dom_cn++;
674 if (doc_kids.item(dom_cn).getNodeType()!=Node.TEXT_NODE)
675 {
676 //System.err.println("charpos beyond end of text");
677 append=true;
678 if (new_charpos != 1)
679 System.err.println("Don't think charpos should be that....");
680 //Want to append the node
681 parent.appendChild(mov_node);
682 break;
683 }
684 }
685 //charpos in current node.
686 //Won't consider splitting nodes at mo as unneccesary
687 }
688 if (!append)
689 parent.insertBefore(mov_node,doc_kids.item(dom_cn));
690 }
691 else
692 parent.appendChild(mov_node);
693 } catch (TransformerException e)
694 { System.err.println("Could not resolve XPath for node to be deleted");}
695 }
696 else
697 {
698 System.err.println("Do not recognise element: " + op_name);
699 System.exit(2);
700 }
701 if (db.on)
702 {
703 try {
704 Transformer serializer = TransformerFactory.newInstance().newTransformer();
705 serializer.transform(new DOMSource(doc), new StreamResult(System.out));
706 System.out.println();
707 } catch (javax.xml.transform.TransformerException e) {}
708 }
709 op=ni.nextNode();
710 }
711
712 }
713 public static void main(String[] args)
714 {
715
716 parseArgs(args);
717 //Check files exist
718 File test=new File(file1);
719 if (!test.exists())
720 {
721 System.err.println("Could not find file: " +file1);
722 System.exit(2);
723 }
724 test=new File(file2);
725 if (!test.exists())
726 {
727 System.err.println("Could not find file: " +file2);
728 System.exit(2);
729 }
730
731 //db.on=true;
732 try
733 {
734 DOMParser parser1 = new DOMParser();
735 DOMParser parser2 = new DOMParser();
736
737
738 try {
739 parser1.setFeature("http://xml.org/sax/features/external-general-entities",
740 false);
741 parser2.setFeature("http://xml.org/sax/features/external-general-entities",
742 false);
743 parser1.setFeature("http://xml.org/sax/features/external-parameter-entities",
744 false);
745 parser2.setFeature("http://xml.org/sax/features/external-parameter-entities",
746 false);
747 parser1.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
748 false);
749 parser2.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
750 false);
751 //parser1.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
752 // false);
753 parser2.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
754 false);
755 parser1.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes",
756 false);
757 parser2.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes",
758 false);
759 }
760 catch (SAXException e) {
761 System.err.println("could not set parser feature");}
762
763 parser1.parse(file1);
764 parser2.parse(file2);
765 Document doc=parser1.getDocument();
766 Document patch=parser2.getDocument();
767 doc.normalize();
768 patch.normalize();
769 if (reverse)
770 patch=Reverse.go(patch);
771 Patch p = new Patch();
772 p.apply(doc, patch);
773 //Write doc out to file again
774 Transformer serializer = TransformerFactory.newInstance().newTransformer();
775 if (dryrun)
776 serializer.transform(new DOMSource(doc), new StreamResult(System.out));
777 else
778 {
779 File f1=new File(file1);
780 serializer.transform(new DOMSource(doc), new StreamResult(f1));
781 }
782 //db.on=true;
783 db.p("Patch Doc");
784 if (db.on==true)
785 serializer.transform(new DOMSource(patch), new StreamResult(System.out));
786 System.out.println();
787 //db.on=false;
788 }
789 catch (Exception e)
790 {
791 e.printStackTrace();
792 }
793
794 }
795 }