1 /************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 import java
.io
.FileWriter
;
29 import java
.io
.InputStream
;
30 import java
.io
.FileInputStream
;
31 import java
.io
.BufferedInputStream
;
32 import java
.io
.BufferedWriter
;
33 import java
.io
.IOException
;
34 import java
.io
.FileNotFoundException
;
35 import java
.io
.OutputStream
;
36 import java
.io
.OutputStreamWriter
;
37 import java
.io
.Writer
;
38 import java
.io
.PrintWriter
;
39 import java
.util
.Vector
;
40 import java
.util
.Properties
;
42 import javax
.xml
.parsers
.DocumentBuilderFactory
;
43 import javax
.xml
.parsers
.DocumentBuilder
;
44 import javax
.xml
.parsers
.ParserConfigurationException
;
46 import org
.w3c
.dom
.Node
;
47 import org
.w3c
.dom
.Document
;
48 import org
.w3c
.dom
.NodeList
;
49 import org
.xml
.sax
.SAXException
;
50 import org
.xml
.sax
.SAXParseException
;
52 * This class will diff 2 Xml files.
57 public final class XmlDiff
{
59 private static final String PROPSFILE
= "XmlDiff.properties";
60 private static final String FILE1
= "XmlDiff.file1";
61 private static final String FILE2
= "XmlDiff.file2";
62 private static final String OUTPUT
= "XmlDiff.output";
63 private static final String IGNORE_TAGS
= "XmlDiff.tags";
65 private Properties props_
= null;
66 private static PrintWriter writer_
= null;
67 private String
[] tags_
= null;
68 private String file1_
= null;
69 private String file2_
= null;
72 * Constructor. Load the properties file.
75 public XmlDiff() throws IOException
{
77 Class c
= this.getClass();
78 InputStream is
= c
.getResourceAsStream(PROPSFILE
);
79 BufferedInputStream bis
= new BufferedInputStream(is
);
80 props_
= new Properties();
84 String file1
= props_
.getProperty(FILE1
, "");
85 String file2
= props_
.getProperty(FILE2
, "");
86 String tagsString
= props_
.getProperty(IGNORE_TAGS
, "");
87 String output
= props_
.getProperty("debug.output", "System.out");
89 tags_
= parseTags(tagsString
);
93 * diff 2 xml, but overwrite the property file's file1/2 setting with
96 public boolean diff(String file1
, String file2
) throws IOException
{
102 public boolean diff() throws IOException
{
104 boolean result
= false;
106 writer_
.println("parsing "+ file1_
+ "...");
107 // parse the Xml file
108 Document doc1
= parseXml(file1_
);
110 writer_
.println("parsing "+ file1_
+ "...");
111 Document doc2
= parseXml(file2_
);
113 if (doc1
!= null && doc2
!= null) {
114 writer_
.println("diffing "+ file1_
+ " & " + file2_
+ "...");
115 result
= compareNode(doc1
, doc2
);
120 private void diffLog(String errMsg
, Node node1
, Node node2
) {
122 String node1Str
= "";
123 String node2Str
= "";
126 node1Str
= "[Type]:" + nodeInfo(node1
) +
127 " [Name]:" + node1
.getNodeName();
128 if (node1
.getNodeValue() != null)
129 node1Str
+= " [Value]:" + node1
.getNodeValue();
133 node2Str
= "[Type]:" + nodeInfo(node2
) +
134 " [Name]:" + node2
.getNodeName();
135 if (node2
.getNodeValue() != null)
136 node2Str
+= " [Value]:" + node2
.getNodeValue();
139 writer_
.println(errMsg
);
140 writer_
.println(" Node1 - " + node1Str
);
141 writer_
.println(" Node2 - " + node2Str
);
144 private String
nodeInfo(Node node
) {
147 switch (node
.getNodeType()) {
149 case Node
.ELEMENT_NODE
:
152 case Node
.ATTRIBUTE_NODE
:
158 case Node
.CDATA_SECTION_NODE
:
159 str
= "CDATA_SECTION";
161 case Node
.ENTITY_REFERENCE_NODE
:
162 str
= "ENTITY_REFERENCE";
164 case Node
.ENTITY_NODE
:
167 case Node
.PROCESSING_INSTRUCTION_NODE
:
168 str
= "PROCESSING_INSTRUCTION";
170 case Node
.COMMENT_NODE
:
173 case Node
.DOCUMENT_NODE
:
176 case Node
.DOCUMENT_TYPE_NODE
:
177 str
= "DOCUMENT_TYPE";
179 case Node
.DOCUMENT_FRAGMENT_NODE
:
180 str
= "DOCUMENT_FRAGMENT";
182 case Node
.NOTATION_NODE
:
189 private boolean ignoreTag(String nodeName
) {
193 for (int i
= 0; i
< tags_
.length
; i
++) {
194 if (tags_
[i
].equals(nodeName
))
201 // for future use if we want to compare attributes
202 private boolean attributesEqual(Node node1
, Node node2
) {
206 private boolean compareNode(Node node1
, Node node2
) {
207 boolean equal
= false;
211 if (node1
== null && node2
== null) {
214 } else if (node1
== null || node2
== null) {
215 diffLog("DIFF: one of the node is null", node1
, node2
);
219 if (node1
.getNodeType() != node2
.getNodeType()) {
220 diffLog("DIFF: nodetype is different", node1
, node2
);
224 if (node1
.getNodeName() == null && node2
.getNodeName() == null) {
226 } else if (node1
.getNodeName() == null ||
227 node2
.getNodeName() == null) {
228 diffLog("DIFF: one of the nodeName is null", node1
, node2
);
230 } else if (!node1
.getNodeName().equals(node2
.getNodeName())) {
231 diffLog("DIFF: nodeName is different", node1
, node2
);
235 if (ignoreTag(node1
.getNodeName())) {
236 diffLog("DIFF: Some tag(s) is ignored", node1
, node2
);
241 if (node1
.getNodeValue() == null && node2
.getNodeValue() == null) {
243 } else if (node1
.getNodeValue() == null ||
244 node2
.getNodeValue() == null) {
245 diffLog("DIFF: one of the nodevalue is null", node1
, node2
);
247 } else if (!node1
.getNodeValue().equals(node2
.getNodeValue())) {
248 diffLog("DIFF: nodeValue is different", node1
, node2
);
252 // try to compare attributes if necessary
253 if (!attributesEqual(node1
, node2
))
256 NodeList node1Children
= node1
.getChildNodes();
257 NodeList node2Children
= node2
.getChildNodes();
259 // number of children have to be the same
260 if (node1Children
== null && node2Children
== null) {
265 if (node1Children
== null || node2Children
== null) {
266 diffLog("DIFF: one node's children is null", node1
, node2
);
270 if (node1Children
.getLength() != node2Children
.getLength()) {
271 diffLog("DIFF: num of children is different", node1
, node2
);
275 // compare all the childrens
278 for (int i
= 0; i
< node1Children
.getLength(); i
++) {
279 if (!compareNode(node1Children
.item(i
),
280 node2Children
.item(i
))) {
291 private Document
parseXml (String filename
) throws IOException
{
293 Document w3cDocument
= null;
298 fis
= new FileInputStream(filename
);
299 } catch (FileNotFoundException ex
) {
300 ex
.printStackTrace(writer_
);
301 writer_
.println(ex
.getMessage());
305 /** factory for DocumentBuilder objects */
306 DocumentBuilderFactory factory
= null;
307 factory
= DocumentBuilderFactory
.newInstance();
308 factory
.setNamespaceAware(true);
309 factory
.setValidating(false);
311 /** DocumentBuilder object */
312 DocumentBuilder builder
= null;
315 builder
= factory
.newDocumentBuilder();
316 } catch (ParserConfigurationException ex
) {
317 ex
.printStackTrace(writer_
);
318 writer_
.println(ex
.getMessage());
323 builder
.setErrorHandler(
324 new org
.xml
.sax
.ErrorHandler() {
325 // ignore fatal errors (an exception is guaranteed)
326 public void fatalError(SAXParseException e
)
327 throws SAXException
{
331 public void error(SAXParseException e
)
332 throws SAXParseException
{
333 // make sure validation error is thrown.
337 public void warning(SAXParseException e
)
338 throws SAXParseException
{
344 w3cDocument
= builder
.parse(fis
);
345 w3cDocument
.getDocumentElement().normalize();
346 } catch (SAXException ex
) {
347 ex
.printStackTrace(writer_
);
348 writer_
.println(ex
.getMessage());
355 private String
[] parseTags(String tagsString
) {
356 Vector tagsVector
= new Vector();
357 if (tagsString
.length() == 0)
362 // break the tag string into a vector of strings by words
363 for (end
= tagsString
.indexOf(" ", start
);
365 start
= end
+ 1, end
= tagsString
.indexOf(" ", start
)) {
366 tagsVector
.add(tagsString
.substring(start
,end
));
369 tagsVector
.add(tagsString
.substring(start
,tagsString
.length()));
371 // convert the vector to array
372 String
[] tags
= new String
[tagsVector
.size()];
373 tagsVector
.copyInto(tags
);
380 * Set the output to the specified argument.
381 * This method is only used internally to prevent
382 * invalid string parameter.
384 * @param str output specifier
386 private static void setOutput(String str
) {
388 if (writer_
== null) {
390 if (str
.equals("System.out")) {
392 setOutput(System
.out
);
394 } else if (str
.equals("System.err")) {
396 setOutput(System
.err
);
402 setOutput(new FileWriter(str
));
404 } catch (IOException e
) {
406 e
.printStackTrace(System
.err
);
413 * Set the output to an OutputStream object.
415 * @param stream OutputStream object
418 private static void setOutput(OutputStream stream
) {
420 setOutput(new OutputStreamWriter(stream
));
424 * Set the Writer object to manage the output.
426 * @param w Writer object to write out
429 private static void setOutput(Writer w
) {
431 if (writer_
!= null) {
436 writer_
= new PrintWriter(new BufferedWriter(w
), true);
439 public static void main(String args
[]) throws IOException
{
441 if (args
.length
!= 0 && args
.length
!= 2) {
442 System
.out
.println("Usage: XmlDiff [<file1> <file2>].");
446 XmlDiff xmldiff
= new XmlDiff();
448 boolean same
= false;
449 if (args
.length
== 2) {
450 same
= xmldiff
.diff(args
[0], args
[1]);
452 same
= xmldiff
.diff();
455 System
.out
.println("Diff result: " + same
);