1 19 20 package org.netbeans.modules.xml.xdm.diff; 21 22 import java.io.IOException ; 23 import java.io.UnsupportedEncodingException ; 24 import java.util.ArrayList ; 25 import java.util.HashMap ; 26 import java.util.List ; 27 import java.util.Map ; 28 import javax.swing.text.BadLocationException ; 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 ; 44 import org.netbeans.modules.xml.xdm.nodes.Token; 45 import org.w3c.dom.NamedNodeMap ; 46 import org.w3c.dom.NodeList ; 47 48 52 public class XDMUtil { 53 54 public enum ComparisonCriteria { 55 EQUAL, 56 IDENTICAL; 57 } 58 59 62 public XDMUtil() { 63 } 64 65 68 public String prettyPrintXML(String doc, String indentation) 69 throws UnsupportedEncodingException , IOException , BadLocationException { 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 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 123 public List <Difference> compareXML(String xml1, String xml2, 124 XDMUtil.ComparisonCriteria criteria) 125 throws Exception { 126 return compareXML(xml1, xml2, criteria, true); 127 } 128 129 143 public List <Difference> compareXML(String firstDoc, 144 String secondDoc, ComparisonCriteria type, boolean filterWhiteSpace) 145 throws BadLocationException , IOException { 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 <Difference> diffs = dif.findDiff(m1.getDocument(), m2.getDocument()); 164 if(filterWhiteSpace) 165 diffs = XDUDiffFinder.filterWhitespace(diffs); if(type == ComparisonCriteria.EQUAL) { List <Difference> filteredDiffs = new ArrayList <Difference>(); 168 for(Difference d:diffs) { 169 if(d instanceof Change) { 170 Change c = (Change)d; 171 if(c.isPositionChanged()) if(!c.isTokenChanged() && !c.isAttributeChanged()) 173 continue; 174 if(c.isAttributeChanged() && !c.isTokenChanged()) { List <Change.AttributeDiff> removeList = 176 new ArrayList <Change.AttributeDiff>(); 177 List <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()) 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) continue; 190 } 191 filteredDiffs.add(d); 192 } 193 } 194 return filteredDiffs; 195 } 196 197 removePseudoAttrPosChanges(diffs); 199 200 filterSchemaLocationDiffs(diffs); 201 202 return diffs; 203 } 204 205 private ElementIdentity createElementIdentity() { 206 ElementIdentity eID = new XDElementIdentity(); 208 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 , IOException { 218 return createXDMModel(sd, ""); 219 } 220 221 private XDMModel createXDMModel(Document sd, String content) 222 throws BadLocationException , IOException { 223 boolean foundXMLProlog = true; 224 if(content.indexOf("<?xml") == -1) 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 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 <Node> ancestors = m2.add(n2, newNode, count++); 244 n2 = ancestors.get(0); 245 } 246 List <Node> ancestors = new ArrayList <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 <Node> ancestors, String indent) { 253 Node parent = n; 254 int index = m.getIndentation().length(); 255 NodeList childs = parent.getChildNodes(); 256 List <Node> visitList = new ArrayList <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 lastTextIndent = "\n"; 265 if(m.getIndentation().length() < indent.length() ) 266 lastTextIndent += indent.substring(m.getIndentation().length()); 267 txt.setText(lastTextIndent); 268 } 269 List <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(); } 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 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 306 public static void removePseudoAttrPosChanges(final List <Difference> diffs) { 307 List <Difference> removeDiffs = new ArrayList <Difference>(); 308 for(Difference dif:diffs) { 309 if(dif instanceof Change) { 310 Change c = (Change)dif; 311 if(c.isAttributeChanged() && !c.isPositionChanged() && !c.isTokenChanged()) { 313 List <Change.AttributeDiff> attrdiffs = c.getAttrChanges(); 314 int size = attrdiffs.size(); 315 List <Change.AttributeDiff> removeAttrs = new ArrayList <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 349 public static void filterAttributeOrderChange(final List <Difference> diffs) { 350 List <Difference> removeDiffs = new ArrayList <Difference>(); 351 for(Difference dif:diffs) { 352 if(dif instanceof Change) { 353 Change c = (Change)dif; 354 if(c.isAttributeChanged() && !c.isPositionChanged() && !c.isTokenChanged()) { 356 List <Change.AttributeDiff> attrdiffs = c.getAttrChanges(); 357 int size = attrdiffs.size(); 358 List <Change.AttributeDiff> removeAttrs = new ArrayList <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 383 public static void filterSchemaLocationDiffs(final List <Difference> diffs) { 384 List <Difference> removeDiffs = new ArrayList <Difference>(); 385 for(Difference dif:diffs) { 386 if(dif instanceof Change) { 387 Change c = (Change)dif; 388 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 403 public static boolean removeSchemaLocationAttrDiffs(Change c) { 404 List <Change.AttributeDiff> attrdiffs = c.getAttrChanges(); 405 int size = attrdiffs.size(); 406 List <Change.AttributeDiff> removeAttrs = new ArrayList <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 428 public XDElementIdentity() { 429 super(); 430 } 431 432 protected boolean compareElement(org.w3c.dom.Element n1, org.w3c.dom.Element n2, org.w3c.dom.Node parent1, org.w3c.dom.Document doc1, org.w3c.dom.Document doc2) { 433 String qName1 = n1.getLocalName(); 434 String qName2 = n2.getLocalName(); 435 String ns1 = ((Node)n1).getNamespaceURI((org.netbeans.modules.xml.xdm.nodes.Document) doc1); 436 String 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("")))) { 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; 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 <Change.Type> checkChange(final Node p1, final Node p2) { 459 List <Change.Type> changes = new ArrayList <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 nm1 = p1.getAttributes(); 471 NamedNodeMap nm2 = p2.getAttributes(); 472 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 NS_PREFIX = "xmlns"; 496 public final static String SCHEMA_LOCATION = "schemaLocation"; 497 public final static String XML_PROLOG = "<?xml version=\"1.0\"?>\n"; 498 } 499 | Popular Tags |