1 19 20 package org.netbeans.modules.xml.xdm.diff; 21 22 import java.util.ArrayList ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.Map.Entry; 29 import java.util.Set ; 30 import java.util.SortedMap ; 31 import java.util.TreeMap ; 32 import org.netbeans.modules.xml.xdm.XDMModel; 33 import org.netbeans.modules.xml.xdm.nodes.Attribute; 34 import org.netbeans.modules.xml.xdm.nodes.Node; 35 import org.netbeans.modules.xml.xdm.nodes.NodeImpl; 36 import org.netbeans.modules.xml.xdm.nodes.Element; 37 import org.w3c.dom.NamedNodeMap ; 38 import org.w3c.dom.NodeList ; 39 40 46 public class MergeDiff { 47 48 49 public MergeDiff() { 50 } 51 52 public void merge(XDMModel model, List <Difference> deList) { 53 this.model = model; 54 55 HashMap <Node, Set <Change>> changesByNode = new HashMap <Node, Set <Change>>(); 56 HashMap <Node, Set <Difference>> childrenDiffsByNode = new HashMap <Node, Set <Difference>>(); 57 HashMap <Integer ,Node> idToChangeds = new HashMap <Integer ,Node>(); 58 59 for ( Difference de: deList ) { 60 if (de instanceof Change) { 61 Change change = (Change) de; 62 if (change.isAttributeChanged() || change.isTokenChanged()) { 63 addDiffToMap(change.getOldNodeInfo().getNode(), change, changesByNode); 64 } 65 if (change.isPositionChanged()) { 66 addDiffToMap(de.getOldNodeInfo().getParent(), change, childrenDiffsByNode); 67 } 68 } else { 69 addDiffToMap(de.getOldNodeInfo().getParent(), de, childrenDiffsByNode); 70 } 71 } 72 73 for (Map.Entry <Node, Set <Change>> e : changesByNode.entrySet()) { 74 Node target = getCurrentNode(e.getKey(), idToChangeds); 75 assert (target != null) : "target "+e.getKey().getId()+"is no longer inTree"; 76 Set <Change> diffs = e.getValue(); 77 Node changed = applyChanges(diffs, target); 78 assert changed.getId() == target.getId() : "changed id should not change"; 79 idToChangeds.put(changed.getId(), changed); 80 } 81 82 for (Map.Entry <Node, Set <Difference>> e : childrenDiffsByNode.entrySet()) { 83 Node target = getCurrentNode(e.getKey(), idToChangeds); 84 assert (target != null) : "target "+e.getKey().getId()+"is no longer inTree"; 85 Set <Difference> diffs = e.getValue(); 86 Node processed = applyChildrenDiffs(diffs, target); 87 assert processed.getId() == target.getId() : "processed id should not change"; 88 } 89 } 90 91 private <T extends Difference> 92 void addDiffToMap(Node key, T diff, HashMap <Node, Set <T>> map) { 93 Set <T> diffs = map.get(key); 94 if (diffs == null) { 95 diffs = new HashSet <T>(); 96 map.put(key, diffs); 97 } 98 diffs.add(diff); 99 } 100 101 private Node getCurrentNode(Node target, HashMap <Integer ,Node> idToChangeds) { 102 Node newTarget = idToChangeds.get(target.getId()); 103 if (newTarget == null || ! newTarget.isInTree()) { 104 List <Node> path = DiffFinder.getPathToRoot(target); 105 if (path != null && ! path.isEmpty()) { 106 newTarget = path.get(0); 107 idToChangeds.put(target.getId(), newTarget); 108 } 109 } 110 return newTarget; 111 } 112 113 private Node applyChildrenDiffs(final Set <Difference> diffs, Node target) { 114 int id = target.getId(); 115 SortedMap <Integer , Difference> toAddOrReorder = new TreeMap <Integer , Difference>(); 116 for (Difference d : diffs) { 117 if (d instanceof Delete ) { 118 delete(d.getOldNodeInfo()); 119 target = d.getNewParent(); 120 assert id == target.getId(); 121 } else if (d instanceof Add) { 122 toAddOrReorder.put(d.getNewNodeInfo().getPosition(), d); 123 } else if (d instanceof Change) { 124 Change change = (Change) d; 125 assert (change.isPositionChanged() && 126 change.getOldNodeInfo().getParent().getId() == target.getId()); 127 toAddOrReorder.put(change.getNewNodeInfo().getPosition(), change); 128 } 129 } 130 131 132 target = processAddOrReorder(target, toAddOrReorder); 133 assert id == target.getId(); 134 135 for (Difference d : diffs) { 136 d.setNewParent(target); 137 assert getReleventDiffNodeInfo(d).getNode().isInTree() : "Processed child not in tree: "+d; 138 } 139 return target; 140 } 141 142 private NodeInfo getReleventDiffNodeInfo (Difference d) { 143 if (d instanceof Add) { 144 return d.getNewNodeInfo(); 145 } else if (d instanceof Change) { 146 Change c = (Change) d; 147 if (c.isAttributeChanged() || c.isTokenChanged()) { 148 return c.getNewNodeInfo(); 149 } else { 150 return c.getOldNodeInfo(); 151 } 152 } else if (d instanceof Delete) { 153 return d.getOldNodeInfo(); 154 } 155 throw new IllegalArgumentException ("Invald diff type"); 156 } 157 158 private List <Node> getChildrenNodes(Node target) { 159 NodeList cList = target.getChildNodes(); 160 ArrayList <Node> nodes = new ArrayList <Node>(); 161 for (int i=0; i<cList.getLength(); i++) { 162 nodes.add((Node) cList.item(i)); 163 } 164 return nodes; 165 } 166 167 private NodeInfo getReorderNodeInfo(Change change) { 170 assert change.isPositionChanged(); 171 if (change.isAttributeChanged() || change.isTokenChanged()) { 172 return change.getNewNodeInfo(); 173 } else { 174 return change.getOldNodeInfo(); 175 } 176 } 177 178 private Node processAddOrReorder(Node target, SortedMap <Integer , Difference> toAddOrReorder) { 179 int id = target.getId(); 180 List <Node> worksheet = getChildrenNodes(target); 181 182 for (Entry<Integer ,Difference> e : toAddOrReorder.entrySet()) { 184 Difference diff = e.getValue(); 185 if (diff instanceof Change) { 186 Node toReorder = getReorderNodeInfo((Change)diff).getNode(); 187 if (! worksheet.remove(toReorder)) { 188 for (Iterator <Node> i = worksheet.iterator(); i.hasNext();) { 189 Node n = i.next(); 190 if (n.getId() == toReorder.getId()) { 191 i.remove(); 192 break; 193 } 194 } 195 } 196 } 197 } 198 for (Entry<Integer ,Difference> e : toAddOrReorder.entrySet()) { 200 Difference diff = e.getValue(); 201 if (diff instanceof Change) { 202 Node toReorder = getReorderNodeInfo((Change)diff).getNode(); 203 int index = diff.getNewNodeInfo().getPosition(); 204 worksheet.add(index, toReorder); 205 } else if (diff instanceof Add) { 206 add(target, (Add) diff); 208 target = diff.getNewParent(); 209 assert id == target.getId(); 210 211 Node added = ((Add)diff).getNewNodeInfo().getNode(); 212 int index = ((Add)diff).getNewNodeInfo().getPosition(); 213 worksheet.add(index, added); 214 } 215 } 216 assert target.getChildNodes().getLength() == worksheet.size() : "Failed "+toAddOrReorder.values(); 218 int[] permutation = new int[worksheet.size()]; 219 NodeList cList = target.getChildNodes(); 220 for (int i=0; i<cList.getLength(); i++) { 221 Node m = (Node) cList.item(i); 222 int j = -1; 223 for (int k=0; k<worksheet.size(); k++) { 224 Node n = worksheet.get(k); 225 if (m == n || n.isEquivalentNode(m)) { 226 j = k; 227 break; 228 } 229 } 230 assert j > -1 : "current item "+i+" is not on worksheet"; 231 permutation[i] = j; 232 } 233 234 target = reorder(target, permutation); 235 236 for (Entry<Integer ,Difference> e : toAddOrReorder.entrySet()) { 238 Difference diff = e.getValue(); 239 if (diff instanceof Change) { 240 Change c = (Change)diff; 241 if (c.isPositionChanged() && 242 ! c.isAttributeChanged() && ! c.isTokenChanged()) 243 { 244 c.getNewNodeInfo().setNode(c.getOldNodeInfo().getNode()); 245 } 246 } 247 248 } 249 250 return target; 251 } 252 253 private Node applyChanges(Set <Change> diffs, Node target) { 254 Node parent = null; 255 for (Change change : diffs) { 256 target = applyChange(change, target); 257 parent = change.getNewParent(); 258 } 259 for (Change change : diffs) { 261 change.getNewNodeInfo().setNode(target); 262 change.setNewParent(parent); 263 } 264 return target; 265 } 266 267 private Node applyChange(final Change de, Node target) { 268 Node oldNode = de.getOldNodeInfo().getNode(); 270 assert target.getId() == oldNode.getId() : "change target node id != old node id"; 271 Node curNode = de.getNewNodeInfo().getNode(); 272 273 if (de.isTokenChanged()) { 274 NodeImpl newNode = createClone(target); 275 newNode.copyTokens( curNode ); 276 de.setNewParent(modify(oldNode, newNode)); 277 target = newNode; 278 } 279 280 if (de.isAttributeChanged()) { 281 assert oldNode.getLocalName().equals(curNode.getLocalName()); 282 applyAttrTokenChange(target, de); 283 } else if (de.isTokenChanged()) { 284 de.getNewNodeInfo().setNode(target); 285 } 286 return de.getNewNodeInfo().getNode(); 287 } 288 289 private void applyAttrTokenChange(Node target, Change de) { 290 Node curNode = de.getNewNodeInfo().getNode(); 291 List <Node> ancestors2 = DiffFinder.getPathToRoot(target); 292 293 NamedNodeMap nm2 = curNode.getAttributes(); 295 HashMap <String , Integer > nodeToPosition = new HashMap <String , Integer >(); 296 for ( int i=0; i < nm2.getLength(); i++ ) { 297 Attribute newAttr = (Attribute) nm2.item(i); 298 assert newAttr.getName() != null; 299 nodeToPosition.put( newAttr.getName(), new Integer ( i ) ); 300 } 301 302 List <Change.AttributeDiff> attrChanges = ((Change)de).getAttrChanges(); 304 SortedMap <Integer , Node> positionToNode = new TreeMap <Integer , Node>(); 305 for (Change.AttributeDiff attrDiff:attrChanges) { 306 Attribute oldAttr = attrDiff.getOldAttribute(); 307 Attribute currAttr = attrDiff.getNewAttribute(); 308 if ( oldAttr != null ) { 309 if ( currAttr == null ) { 310 ancestors2 = model.delete( oldAttr ); 311 } else { 312 NodeImpl cloneAttr = createClone(oldAttr); 313 cloneAttr.copyTokens(currAttr); 314 ancestors2 = model.modify(oldAttr, cloneAttr); 315 } 316 } else if ( currAttr != null ) { 317 Integer pos = nodeToPosition.get(currAttr.getName()); 318 assert pos != null : "Attribute "+currAttr.getName() + " \n" + de + nodeToPosition + " \n" + positionToNode; 319 positionToNode.put(pos, currAttr); 320 } 321 } 322 323 for (Entry<Integer ,Node> e : positionToNode.entrySet()) { 324 Node copy = createCopy(e.getValue()); 325 curNode = (Element) ancestors2.get(0); 326 ancestors2 = model.add(curNode, copy, e.getKey()); 327 } 328 curNode = (Element) ancestors2.get(0); 329 330 de.getNewNodeInfo().setNode(curNode); 332 assert ancestors2.get(1).isInTree() : "new parent not intree"; 333 de.setNewParent(ancestors2.get(1)); 334 } 335 336 private NodeImpl createCopy(final Node currNode) { 337 NodeImpl newNode = (NodeImpl) ((NodeImpl)currNode).cloneNode(true, false); 338 return newNode; 339 } 340 341 private NodeImpl createClone(final Node oldNode) { 342 NodeImpl newNode = (NodeImpl) ((NodeImpl)oldNode).clone( false, false, false ); 343 return newNode; 344 } 345 346 private void add(Node parent, Difference diff) { 347 NodeInfo newInfo = diff.getNewNodeInfo(); 348 int pos = newInfo.getPosition(); 349 assert pos <= parent.getChildNodes().getLength(); 350 Node newNode = null; 351 if (diff instanceof Change) { 352 newNode = createClone(newInfo.getNode()); 353 } else { 354 newNode = createCopy(newInfo.getNode()); 355 } 356 diff.setNewParent(model.add(parent, newNode, pos).get(0)); 357 newInfo.setNode(newNode); 358 } 359 360 private Node reorder(Node parent, int[] permutation) { 361 return model.reorderChildren(parent, permutation).get(0); 362 } 363 364 private void delete(NodeInfo oldInfo) { 365 oldInfo.setNewParent(model.delete(oldInfo.getNode()).get(0)); 366 } 367 368 private Node modify(Node oldNode, Node newNode) { 369 List <Node> ancestors = model.modify( oldNode, newNode ); 370 return ancestors.isEmpty() ? null : ancestors.get(0); 371 } 372 373 377 private XDMModel model; 378 } 379 | Popular Tags |