1 /************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: XmlDiff.java,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 import java
.io
.FileWriter
;
32 import java
.io
.InputStream
;
33 import java
.io
.FileInputStream
;
34 import java
.io
.BufferedInputStream
;
35 import java
.io
.BufferedWriter
;
36 import java
.io
.IOException
;
37 import java
.io
.FileNotFoundException
;
38 import java
.io
.OutputStream
;
39 import java
.io
.OutputStreamWriter
;
40 import java
.io
.Writer
;
41 import java
.io
.PrintWriter
;
42 import java
.util
.Vector
;
43 import java
.util
.Properties
;
45 import javax
.xml
.parsers
.DocumentBuilderFactory
;
46 import javax
.xml
.parsers
.DocumentBuilder
;
47 import javax
.xml
.parsers
.ParserConfigurationException
;
49 import org
.w3c
.dom
.Node
;
50 import org
.w3c
.dom
.Document
;
51 import org
.w3c
.dom
.NodeList
;
52 import org
.xml
.sax
.SAXException
;
53 import org
.xml
.sax
.SAXParseException
;
55 * This class will diff 2 Xml files.
60 public final class XmlDiff
{
62 private static final String PROPSFILE
= "XmlDiff.properties";
63 private static final String FILE1
= "XmlDiff.file1";
64 private static final String FILE2
= "XmlDiff.file2";
65 private static final String OUTPUT
= "XmlDiff.output";
66 private static final String IGNORE_TAGS
= "XmlDiff.tags";
68 private Properties props_
= null;
69 private static PrintWriter writer_
= null;
70 private String
[] tags_
= null;
71 private String file1_
= null;
72 private String file2_
= null;
75 * Constructor. Load the properties file.
78 public XmlDiff() throws IOException
{
80 Class c
= this.getClass();
81 InputStream is
= c
.getResourceAsStream(PROPSFILE
);
82 BufferedInputStream bis
= new BufferedInputStream(is
);
83 props_
= new Properties();
87 String file1
= props_
.getProperty(FILE1
, "");
88 String file2
= props_
.getProperty(FILE2
, "");
89 String tagsString
= props_
.getProperty(IGNORE_TAGS
, "");
90 String output
= props_
.getProperty("debug.output", "System.out");
92 tags_
= parseTags(tagsString
);
96 * diff 2 xml, but overwrite the property file's file1/2 setting with
99 public boolean diff(String file1
, String file2
) throws IOException
{
105 public boolean diff() throws IOException
{
107 boolean result
= false;
109 writer_
.println("parsing "+ file1_
+ "...");
110 // parse the Xml file
111 Document doc1
= parseXml(file1_
);
113 writer_
.println("parsing "+ file1_
+ "...");
114 Document doc2
= parseXml(file2_
);
116 if (doc1
!= null && doc2
!= null) {
117 writer_
.println("diffing "+ file1_
+ " & " + file2_
+ "...");
118 result
= compareNode(doc1
, doc2
);
123 private void diffLog(String errMsg
, Node node1
, Node node2
) {
125 String node1Str
= "";
126 String node2Str
= "";
129 node1Str
= "[Type]:" + nodeInfo(node1
) +
130 " [Name]:" + node1
.getNodeName();
131 if (node1
.getNodeValue() != null)
132 node1Str
+= " [Value]:" + node1
.getNodeValue();
136 node2Str
= "[Type]:" + nodeInfo(node2
) +
137 " [Name]:" + node2
.getNodeName();
138 if (node2
.getNodeValue() != null)
139 node2Str
+= " [Value]:" + node2
.getNodeValue();
142 writer_
.println(errMsg
);
143 writer_
.println(" Node1 - " + node1Str
);
144 writer_
.println(" Node2 - " + node2Str
);
147 private String
nodeInfo(Node node
) {
150 switch (node
.getNodeType()) {
152 case Node
.ELEMENT_NODE
:
155 case Node
.ATTRIBUTE_NODE
:
161 case Node
.CDATA_SECTION_NODE
:
162 str
= "CDATA_SECTION";
164 case Node
.ENTITY_REFERENCE_NODE
:
165 str
= "ENTITY_REFERENCE";
167 case Node
.ENTITY_NODE
:
170 case Node
.PROCESSING_INSTRUCTION_NODE
:
171 str
= "PROCESSING_INSTRUCTION";
173 case Node
.COMMENT_NODE
:
176 case Node
.DOCUMENT_NODE
:
179 case Node
.DOCUMENT_TYPE_NODE
:
180 str
= "DOCUMENT_TYPE";
182 case Node
.DOCUMENT_FRAGMENT_NODE
:
183 str
= "DOCUMENT_FRAGMENT";
185 case Node
.NOTATION_NODE
:
192 private boolean ignoreTag(String nodeName
) {
196 for (int i
= 0; i
< tags_
.length
; i
++) {
197 if (tags_
[i
].equals(nodeName
))
204 // for future use if we want to compare attributes
205 private boolean attributesEqual(Node node1
, Node node2
) {
209 private boolean compareNode(Node node1
, Node node2
) {
210 boolean equal
= false;
214 if (node1
== null && node2
== null) {
217 } else if (node1
== null || node2
== null) {
218 diffLog("DIFF: one of the node is null", node1
, node2
);
222 if (node1
.getNodeType() != node2
.getNodeType()) {
223 diffLog("DIFF: nodetype is different", node1
, node2
);
227 if (node1
.getNodeName() == null && node2
.getNodeName() == null) {
229 } else if (node1
.getNodeName() == null ||
230 node2
.getNodeName() == null) {
231 diffLog("DIFF: one of the nodeName is null", node1
, node2
);
233 } else if (!node1
.getNodeName().equals(node2
.getNodeName())) {
234 diffLog("DIFF: nodeName is different", node1
, node2
);
238 if (ignoreTag(node1
.getNodeName())) {
239 diffLog("DIFF: Some tag(s) is ignored", node1
, node2
);
244 if (node1
.getNodeValue() == null && node2
.getNodeValue() == null) {
246 } else if (node1
.getNodeValue() == null ||
247 node2
.getNodeValue() == null) {
248 diffLog("DIFF: one of the nodevalue is null", node1
, node2
);
250 } else if (!node1
.getNodeValue().equals(node2
.getNodeValue())) {
251 diffLog("DIFF: nodeValue is different", node1
, node2
);
255 // try to compare attributes if necessary
256 if (!attributesEqual(node1
, node2
))
259 NodeList node1Children
= node1
.getChildNodes();
260 NodeList node2Children
= node2
.getChildNodes();
262 // number of children have to be the same
263 if (node1Children
== null && node2Children
== null) {
268 if (node1Children
== null || node2Children
== null) {
269 diffLog("DIFF: one node's children is null", node1
, node2
);
273 if (node1Children
.getLength() != node2Children
.getLength()) {
274 diffLog("DIFF: num of children is different", node1
, node2
);
278 // compare all the childrens
281 for (int i
= 0; i
< node1Children
.getLength(); i
++) {
282 if (!compareNode(node1Children
.item(i
),
283 node2Children
.item(i
))) {
294 private Document
parseXml (String filename
) throws IOException
{
296 Document w3cDocument
= null;
301 fis
= new FileInputStream(filename
);
302 } catch (FileNotFoundException ex
) {
303 ex
.printStackTrace(writer_
);
304 writer_
.println(ex
.getMessage());
308 /** factory for DocumentBuilder objects */
309 DocumentBuilderFactory factory
= null;
310 factory
= DocumentBuilderFactory
.newInstance();
311 factory
.setNamespaceAware(true);
312 factory
.setValidating(false);
314 /** DocumentBuilder object */
315 DocumentBuilder builder
= null;
318 builder
= factory
.newDocumentBuilder();
319 } catch (ParserConfigurationException ex
) {
320 ex
.printStackTrace(writer_
);
321 writer_
.println(ex
.getMessage());
326 builder
.setErrorHandler(
327 new org
.xml
.sax
.ErrorHandler() {
328 // ignore fatal errors (an exception is guaranteed)
329 public void fatalError(SAXParseException e
)
330 throws SAXException
{
334 public void error(SAXParseException e
)
335 throws SAXParseException
{
336 // make sure validation error is thrown.
340 public void warning(SAXParseException e
)
341 throws SAXParseException
{
347 w3cDocument
= builder
.parse(fis
);
348 w3cDocument
.getDocumentElement().normalize();
349 } catch (SAXException ex
) {
350 ex
.printStackTrace(writer_
);
351 writer_
.println(ex
.getMessage());
358 private String
[] parseTags(String tagsString
) {
359 Vector tagsVector
= new Vector();
360 if (tagsString
.length() == 0)
365 // break the tag string into a vector of strings by words
366 for (end
= tagsString
.indexOf(" ", start
);
368 start
= end
+ 1, end
= tagsString
.indexOf(" ", start
)) {
369 tagsVector
.add(tagsString
.substring(start
,end
));
372 tagsVector
.add(tagsString
.substring(start
,tagsString
.length()));
374 // convert the vector to array
375 String
[] tags
= new String
[tagsVector
.size()];
376 tagsVector
.copyInto(tags
);
383 * Set the output to the specified argument.
384 * This method is only used internally to prevent
385 * invalid string parameter.
387 * @param str output specifier
389 private static void setOutput(String str
) {
391 if (writer_
== null) {
393 if (str
.equals("System.out")) {
395 setOutput(System
.out
);
397 } else if (str
.equals("System.err")) {
399 setOutput(System
.err
);
405 setOutput(new FileWriter(str
));
407 } catch (IOException e
) {
409 e
.printStackTrace(System
.err
);
416 * Set the output to an OutputStream object.
418 * @param stream OutputStream object
421 private static void setOutput(OutputStream stream
) {
423 setOutput(new OutputStreamWriter(stream
));
427 * Set the Writer object to manage the output.
429 * @param w Writer object to write out
432 private static void setOutput(Writer w
) {
434 if (writer_
!= null) {
439 writer_
= new PrintWriter(new BufferedWriter(w
), true);
442 public static void main(String args
[]) throws IOException
{
444 if (args
.length
!= 0 && args
.length
!= 2) {
445 System
.out
.println("Usage: XmlDiff [<file1> <file2>].");
449 XmlDiff xmldiff
= new XmlDiff();
451 boolean same
= false;
452 if (args
.length
== 2) {
453 same
= xmldiff
.diff(args
[0], args
[1]);
455 same
= xmldiff
.diff();
458 System
.out
.println("Diff result: " + same
);