KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > xdm > diff > XDMUtil


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.xdm.diff;
21
22 import java.io.IOException JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import javax.swing.text.BadLocationException JavaDoc;
29 import org.netbeans.editor.BaseDocument;
30 import org.netbeans.modules.xml.text.syntax.XMLKit;
31 import org.netbeans.modules.xml.xam.ModelSource;
32 import org.netbeans.modules.xml.xam.dom.ElementIdentity;
33 import org.netbeans.modules.xml.xdm.XDMModel;
34 import org.netbeans.modules.xml.xdm.nodes.Attribute;
35 import org.netbeans.modules.xml.xdm.nodes.Element;
36 import org.netbeans.modules.xml.xdm.nodes.Node;
37 import org.netbeans.modules.xml.xdm.nodes.NodeImpl;
38 import org.netbeans.modules.xml.xdm.nodes.Text;
39 import org.netbeans.modules.xml.xdm.diff.Change.AttributeChange;
40 import org.netbeans.modules.xml.xdm.visitor.PositionFinderVisitor;
41 import org.openide.util.Lookup;
42 import org.openide.util.lookup.Lookups;
43 import javax.swing.text.Document JavaDoc;
44 import org.netbeans.modules.xml.xdm.nodes.Token;
45 import org.w3c.dom.NamedNodeMap JavaDoc;
46 import org.w3c.dom.NodeList JavaDoc;
47
48 /**
49  *
50  * @author Ayub Khan
51  */

52 public class XDMUtil {
53     
54     public enum ComparisonCriteria {
55         EQUAL,
56         IDENTICAL;
57     }
58     
59     /**
60      * Constructor for XDMUtil
61      */

62     public XDMUtil() {
63     }
64
65     /*
66      * returns a pretty print version of xml doc, using given indentation
67      */

68     public String JavaDoc prettyPrintXML(String JavaDoc doc, String JavaDoc indentation)
69     throws UnsupportedEncodingException JavaDoc, IOException JavaDoc, BadLocationException JavaDoc {
70         Document sd1 = new BaseDocument(XMLKit.class, false);
71         XDMModel m1 = createXDMModel(sd1, doc);
72         Node root1 = m1.getDocument();
73         
74         Document sd2 = new BaseDocument(XMLKit.class, false);
75         XDMModel m2 = createXDMModel(sd2);
76         m2.setPretty(true);
77         m2.setIndentation(indentation);
78         m2.sync();
79         Node root2 = m2.getDocument();
80         
81         root2 = doPrettyPrint(m2, root2, root1);
82         m2.flush();
83         
84         String JavaDoc prettyXMLStr = sd2.getText(0, sd2.getLength());
85         
86         int firstChildPos1 = -1;
87         Node firstChild1 = (Node) root1.getChildNodes().item(0);
88         if(firstChild1 != null)
89             firstChildPos1 = new PositionFinderVisitor().findPosition(
90                     m1.getDocument(), firstChild1);
91         
92         int firstChildPos2 = -1;
93         Node firstChild2 = (Node) root2.getChildNodes().item(0);
94         if(firstChild2 != null)
95             firstChildPos2 = new PositionFinderVisitor().findPosition(
96                     m2.getDocument(), firstChild2);
97         
98         return (firstChildPos1==-1?doc:(sd1.getText(0, firstChildPos1) +
99                     sd2.getText(firstChildPos2, sd2.getLength() - firstChildPos2)));
100     }
101     
102     /*
103      * compares 2 xml document contents using a criteria
104      *
105      * @param firstDoc
106      * @param secondDoc
107      * @param type
108      * ComparisonCriteria.EQUAL - means Two documents are considered to be
109      * equal if they contain the same elements and attributes regardless
110      * of order.
111      * ComparisonCriteria.IDENTICAL - means Two documents are considered to
112      * be "identical" if they contain the same elements and attributes in
113      * the same order.
114      * filters -
115      * Whitespace diffs
116      * Namespace attribute diffs
117      * Namespace attribute prefix diffs
118      * Attribute whitespace diffs
119      *
120      * Use the next API (4 argument compareXML api) if you do not want to filter
121      * all of the above 4 types of diffs
122      */

123     public List JavaDoc<Difference> compareXML(String JavaDoc xml1, String JavaDoc xml2,
124             XDMUtil.ComparisonCriteria criteria)
125             throws Exception JavaDoc {
126         return compareXML(xml1, xml2, criteria, true);
127     }
128     
129     /*
130      * compares 2 xml document contents using a criteria
131      *
132      * @param firstDoc
133      * @param secondDoc
134      * @param type
135      * ComparisonCriteria.EQUAL - means Two documents are considered to be
136      * equal if they contain the same elements and attributes regardless
137      * of order.
138      * ComparisonCriteria.IDENTICAL - means Two documents are considered to
139      * be "identical" if they contain the same elements and attributes in
140      * the same order.
141      * @param ignoreWhiteSpace - filters whitespace diffs
142      */

143     public List JavaDoc<Difference> compareXML(String JavaDoc firstDoc,
144             String JavaDoc secondDoc, ComparisonCriteria type, boolean filterWhiteSpace)
145     throws BadLocationException JavaDoc, IOException JavaDoc {
146         Document sd1 = new BaseDocument(XMLKit.class, false);
147         XDMModel m1 = createXDMModel(sd1);
148         sd1.remove(0, XML_PROLOG.length());
149         sd1.insertString(0, firstDoc, null);
150         m1.sync();
151         fDoc = m1.getDocument();
152         
153         Document sd2 = new BaseDocument(XMLKit.class, false);
154         sd2.getText(0, sd2.getLength());
155         XDMModel m2 = createXDMModel(sd2);
156         sd2.remove(0, XML_PROLOG.length());
157         sd2.insertString(0, secondDoc, null);
158         m2.setPretty(true);
159         m2.sync();
160         sDoc = m2.getDocument();
161         
162         XDUDiffFinder dif = new XDUDiffFinder(createElementIdentity());
163         List JavaDoc<Difference> diffs = dif.findDiff(m1.getDocument(), m2.getDocument());
164         if(filterWhiteSpace)
165             diffs = XDUDiffFinder.filterWhitespace(diffs);//filter whitespace diffs
166
if(type == ComparisonCriteria.EQUAL) {//remove order change diffs
167
List JavaDoc<Difference> filteredDiffs = new ArrayList JavaDoc<Difference>();
168             for(Difference d:diffs) {
169                 if(d instanceof Change) {
170                     Change c = (Change)d;
171                     if(c.isPositionChanged())//node (element/text) pos change
172
if(!c.isTokenChanged() && !c.isAttributeChanged())
173                             continue;
174                     if(c.isAttributeChanged() && !c.isTokenChanged()) {//attr change only
175
List JavaDoc<Change.AttributeDiff> removeList =
176                                 new ArrayList JavaDoc<Change.AttributeDiff>();
177                         List JavaDoc<Change.AttributeDiff> attrChanges = c.getAttrChanges();
178                         for(int i=0;i<attrChanges.size();i++) {
179                             if(attrChanges.get(i) instanceof Change.AttributeChange) {
180                                 Change.AttributeChange ac =
181                                     (Change.AttributeChange) attrChanges.get(i);
182                                 if(ac.isPositionChanged() && !ac.isTokenChanged())//attr pos change only
183
removeList.add(ac);
184                             }
185                         }
186                         for(int i=0;i<removeList.size();i++)
187                             c.removeAttrChanges(removeList.get(i));
188                         if(c.getAttrChanges().size() == 0) //filter this diff
189
continue;
190                     }
191                     filteredDiffs.add(d);
192                 }
193             }
194             return filteredDiffs;
195         }
196         
197         //remove pseudo attr position changes
198
removePseudoAttrPosChanges(diffs);
199         
200         filterSchemaLocationDiffs(diffs);
201         
202         return diffs;
203     }
204
205     private ElementIdentity createElementIdentity() {
206         //Establish DOM element identities
207
ElementIdentity eID = new XDElementIdentity();
208         //Following values are suitable for Schema and WSDL documents
209
//these default values can be reset by eID.reset() call
210
eID.addIdentifier( "id" );
211         eID.addIdentifier( "name" );
212         eID.addIdentifier( "ref" );
213         return eID;
214     }
215     
216     private XDMModel createXDMModel(Document sd)
217     throws BadLocationException JavaDoc, IOException JavaDoc {
218         return createXDMModel(sd, "");
219     }
220     
221     private XDMModel createXDMModel(Document sd, String JavaDoc content)
222     throws BadLocationException JavaDoc, IOException JavaDoc {
223         boolean foundXMLProlog = true;
224         if(content.indexOf("<?xml") == -1) //insert xml prolog, otherwise XMLSyntaxParser will fail
225
sd.insertString(0, XML_PROLOG+content, null);
226         else
227             sd.insertString(0, content, null);
228         Lookup lookup = Lookups.singleton(sd);
229         ModelSource ms = new ModelSource(lookup, true);
230         XDMModel model = new XDMModel(ms);
231         model.sync();
232         return model;
233     }
234     
235     
236     private Node doPrettyPrint(XDMModel m2, Node n2, Node n1) {
237         Node newNode = null;
238         NodeList JavaDoc childs1 = n1.getChildNodes();
239         int count = 0;
240         for(int i=0;i<childs1.getLength();i++) {
241             n1 = (NodeImpl) childs1.item(i);
242             newNode = ((NodeImpl)n1).cloneNode(true, false);
243             List JavaDoc<Node> ancestors = m2.add(n2, newNode, count++);
244             n2 = ancestors.get(0);
245         }
246         List JavaDoc<Node> ancestors = new ArrayList JavaDoc<Node>();
247         fixPrettyText(m2, n2, ancestors, "");
248         n2 = ancestors.get(0);
249         return n2;
250     }
251     
252     private void fixPrettyText(XDMModel m, final Node n, List JavaDoc<Node> ancestors, String JavaDoc indent) {
253         Node parent = n;
254         int index = m.getIndentation().length();
255         NodeList JavaDoc childs = parent.getChildNodes();
256         List JavaDoc<Node> visitList = new ArrayList JavaDoc<Node>();
257         for(int i=0;i<childs.getLength();i++) {
258             Node child = (Node) childs.item(i);
259             if(checkPrettyText(child)) {
260                 Text txt = (Text) ((NodeImpl)child).cloneNode(true);
261                 if(i < childs.getLength()-1 || ancestors.size() == 0)
262                     txt.setText("\n"+indent);
263                 else {
264                     String JavaDoc lastTextIndent = "\n";
265                     if(m.getIndentation().length() < indent.length() )
266                         lastTextIndent += indent.substring(m.getIndentation().length());
267                     txt.setText(lastTextIndent);
268                 }
269                 List JavaDoc<Node> ancestors2 = m.modify(child, txt);
270                 parent = ancestors2.get(0);
271             }
272             else if(childs.item(i) instanceof Element)
273                 visitList.add((Node)childs.item(i));
274         }
275         ancestors.add(parent);
276         for(int i=0;i<visitList.size();i++) {
277             fixPrettyText(m, (Node)visitList.get((i)), ancestors, indent+m.getIndentation());
278         }
279         visitList.clear(); //no need to keep it beyond here
280
}
281
282     public static boolean checkPrettyText(Node txt) {
283         if (txt instanceof Text) {
284             if ((((NodeImpl)txt).getTokens().size() == 1) &&
285                     isWhitespaceOnly(((NodeImpl)txt).getTokens().get(0).getValue())) {
286                 return true;
287             }
288         }
289         return false;
290     }
291     
292     public static boolean isWhitespaceOnly(String JavaDoc tn) {
293         return XDUDiffFinder.isPossibleWhiteSpace(tn) &&
294                 tn.trim().length() == 0;
295     }
296     
297
298     public static int findPosition(final Node n) {
299         return new PositionFinderVisitor().findPosition(
300                 (Node)n.getOwnerDocument(), n);
301     }
302     
303     /*
304      * filters or removes diffs that are attr position changes
305      */

306     public static void removePseudoAttrPosChanges(final List JavaDoc<Difference> diffs) {
307         List JavaDoc<Difference> removeDiffs = new ArrayList JavaDoc<Difference>();
308         for(Difference dif:diffs) {
309             if(dif instanceof Change) {
310                 Change c = (Change)dif;
311                 //filter attibute position changes only
312
if(c.isAttributeChanged() && !c.isPositionChanged() && !c.isTokenChanged()) {
313                     List JavaDoc<Change.AttributeDiff> attrdiffs = c.getAttrChanges();
314                     int size = attrdiffs.size();
315                     List JavaDoc<Change.AttributeDiff> removeAttrs = new ArrayList JavaDoc<Change.AttributeDiff>();
316                     int delCount = 0;
317                     int addCount = 0;
318                     for(Change.AttributeDiff attrdif:attrdiffs) {
319                         if(attrdif instanceof Change.AttributeDelete)
320                             delCount++;
321                         else if(attrdif instanceof Change.AttributeAdd)
322                             addCount++;
323                         else if(attrdif instanceof Change.AttributeChange) {
324                             Change.AttributeChange attrChange =
325                                     (AttributeChange) attrdif;
326                             if(attrChange.isPositionChanged() && !attrChange.isTokenChanged()) {
327                                 if((attrChange.getOldAttributePosition() - delCount + addCount) ==
328                                         attrChange.getNewAttributePosition())
329                                     removeAttrs.add(attrdif);
330                             }
331                         }
332                     }
333                     for(Change.AttributeDiff attrdif:removeAttrs) {
334                         c.removeAttrChanges(attrdif);
335                     }
336                     if(size > 0 && c.getAttrChanges().size() == 0)
337                         removeDiffs.add(dif);
338                 }
339             }
340         }
341         for(Difference dif:removeDiffs) {
342             diffs.remove(dif);
343         }
344     }
345     
346     /*
347      * filters or removes diffs that are attr position changes
348      */

349     public static void filterAttributeOrderChange(final List JavaDoc<Difference> diffs) {
350         List JavaDoc<Difference> removeDiffs = new ArrayList JavaDoc<Difference>();
351         for(Difference dif:diffs) {
352             if(dif instanceof Change) {
353                 Change c = (Change)dif;
354                 //filter attibute position changes only
355
if(c.isAttributeChanged() && !c.isPositionChanged() && !c.isTokenChanged()) {
356                     List JavaDoc<Change.AttributeDiff> attrdiffs = c.getAttrChanges();
357                     int size = attrdiffs.size();
358                     List JavaDoc<Change.AttributeDiff> removeAttrs = new ArrayList JavaDoc<Change.AttributeDiff>();
359                     for(Change.AttributeDiff attrdif:attrdiffs) {
360                         if(attrdif instanceof Change.AttributeChange) {
361                             Change.AttributeChange attrChange =
362                                     (AttributeChange) attrdif;
363                             if(attrChange.isPositionChanged() && !attrChange.isTokenChanged())
364                                 removeAttrs.add(attrdif);
365                         }
366                     }
367                     for(Change.AttributeDiff attrdif:removeAttrs) {
368                         c.removeAttrChanges(attrdif);
369                     }
370                     if(size > 0 && c.getAttrChanges().size() == 0)
371                         removeDiffs.add(dif);
372                 }
373             }
374         }
375         for(Difference dif:removeDiffs) {
376             diffs.remove(dif);
377         }
378     }
379         
380     /*
381      * filters or removes diffs that are schemalocation attr "xsi:schemaLocation='some url'"
382      */

383     public static void filterSchemaLocationDiffs(final List JavaDoc<Difference> diffs) {
384         List JavaDoc<Difference> removeDiffs = new ArrayList JavaDoc<Difference>();
385         for(Difference dif:diffs) {
386             if(dif instanceof Change) {
387                 Change c = (Change)dif;
388                 //filter namespace attibute changes only
389
if(c.isAttributeChanged() && !c.isPositionChanged() &&
390                         !c.isTokenChanged() && removeSchemaLocationAttrDiffs(c)) {
391                     removeDiffs.add(dif);
392                 }
393             }
394         }
395         for(Difference dif:removeDiffs) {
396             diffs.remove(dif);
397         }
398     }
399     
400     /*
401      * removes attr diffs that are ns attr "prefix:schemaLocation='some url'"
402      */

403     public static boolean removeSchemaLocationAttrDiffs(Change c) {
404         List JavaDoc<Change.AttributeDiff> attrdiffs = c.getAttrChanges();
405         int size = attrdiffs.size();
406         List JavaDoc<Change.AttributeDiff> removeAttrs = new ArrayList JavaDoc<Change.AttributeDiff>();
407         for(Change.AttributeDiff attrdif:attrdiffs) {
408             Attribute oldAttr = attrdif.getOldAttribute();
409             Attribute newAttr = attrdif.getNewAttribute();
410             if(oldAttr != null && oldAttr.getName().endsWith(SCHEMA_LOCATION))
411                 removeAttrs.add(attrdif);
412             else if(newAttr != null && newAttr.getName().endsWith(SCHEMA_LOCATION))
413                 removeAttrs.add(attrdif);
414         }
415         for(Change.AttributeDiff attrdif:removeAttrs) {
416             c.removeAttrChanges(attrdif);
417         }
418         if(size > 0 && attrdiffs.size() == 0)
419             return true;
420         return false;
421     }
422     
423     public class XDElementIdentity extends DefaultElementIdentity {
424
425         /**
426          * Creates a new instance of DefaultElementIdentity
427          */

428         public XDElementIdentity() {
429             super();
430         }
431
432         protected boolean compareElement(org.w3c.dom.Element JavaDoc n1, org.w3c.dom.Element JavaDoc n2, org.w3c.dom.Node JavaDoc parent1, org.w3c.dom.Document JavaDoc doc1, org.w3c.dom.Document JavaDoc doc2) {
433             String JavaDoc qName1 = n1.getLocalName();
434             String JavaDoc qName2 = n2.getLocalName();
435             String JavaDoc ns1 = ((Node)n1).getNamespaceURI((org.netbeans.modules.xml.xdm.nodes.Document) doc1);
436             String JavaDoc ns2 = ((Node)n2).getNamespaceURI((org.netbeans.modules.xml.xdm.nodes.Document) doc2);
437
438             if ( qName1.intern() != qName2.intern() )
439                 return false;
440             if(!((ns1 == null || ns1.equals("")) && (ns2 == null || ns2.equals("")))) {//can determine ns
441
if ( !(ns1 == null && ns2 == null) &&
442                         !(ns1 != null && ns2 != null && ns1.intern() == ns2.intern() ) )
443                     return false;
444             }
445
446             if(parent1 == doc1) return true; //if root no need to compare other identifiers
447

448             return compareAttr( n1, n2);
449         }
450     }
451     
452     public class XDUDiffFinder extends DiffFinder {
453
454         public XDUDiffFinder(ElementIdentity eID) {
455             super(eID);
456         }
457
458         public List JavaDoc<Change.Type> checkChange(final Node p1, final Node p2) {
459             List JavaDoc<Change.Type> changes = new ArrayList JavaDoc<Change.Type>();
460             if (p1 instanceof Element && p2 instanceof Element) {
461                 if ( ! checkAttributesEqual((Element)p1, (Element)p2)) {
462                     changes.add(Change.Type.ATTRIBUTE);
463                 }
464             }
465             return changes;
466         }
467
468         protected boolean checkAttributesEqual(final Element p1, final Element p2) {
469             if (p1 == null || p2 == null) return false;
470             NamedNodeMap JavaDoc nm1 = p1.getAttributes();
471             NamedNodeMap JavaDoc nm2 = p2.getAttributes();
472             //if( nm1.getLength() != nm2.getLength() ) return false;
473

474             for ( int i = 0; i < nm1.getLength(); i++ ) {
475                 Node attr1 = (Node) nm1.item(i);
476                 if(attr1.getNodeName().startsWith("xmlns"))
477                     continue;
478                 Node attr2 = (Node) nm2.getNamedItem(attr1.getNodeName());
479                 if ( attr2 == null ) return false;
480                 if(nm2.item(i) != attr2) return false;
481                 if(!attr1.getNodeValue().equals(attr2.getNodeValue()))
482                     return false;
483             }
484             return true;
485         }
486
487         protected boolean compareTextByValue(Text n1, Text n2) {
488             return n1.getNodeValue().equals(n2.getNodeValue());
489         }
490     }
491     
492     static org.netbeans.modules.xml.xdm.nodes.Document fDoc;
493     static org.netbeans.modules.xml.xdm.nodes.Document sDoc;
494     
495     public final static String JavaDoc NS_PREFIX = "xmlns";
496     public final static String JavaDoc SCHEMA_LOCATION = "schemaLocation";
497     public final static String JavaDoc XML_PROLOG = "<?xml version=\"1.0\"?>\n";
498 }
499
Popular Tags