KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.ArrayList JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Map.Entry;
29 import java.util.Set JavaDoc;
30 import java.util.SortedMap JavaDoc;
31 import java.util.TreeMap JavaDoc;
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 JavaDoc;
38 import org.w3c.dom.NodeList JavaDoc;
39
40 /*
41  * This class is used by XDMTreeDiff (during Sync) to merge changes back to the original
42  * xdm model document
43  *
44  * @author Ayub Khan
45  */

46 public class MergeDiff {
47     
48     /** Creates a new instance of MergeDiff */
49     public MergeDiff() {
50     }
51     
52     public void merge(XDMModel model, List JavaDoc<Difference> deList) {
53         this.model = model;
54         
55         HashMap JavaDoc<Node, Set JavaDoc<Change>> changesByNode = new HashMap JavaDoc<Node, Set JavaDoc<Change>>();
56         HashMap JavaDoc<Node, Set JavaDoc<Difference>> childrenDiffsByNode = new HashMap JavaDoc<Node, Set JavaDoc<Difference>>();
57         HashMap JavaDoc<Integer JavaDoc,Node> idToChangeds = new HashMap JavaDoc<Integer JavaDoc,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 JavaDoc<Node, Set JavaDoc<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 JavaDoc<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 JavaDoc<Node, Set JavaDoc<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 JavaDoc<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 JavaDoc<Node, Set JavaDoc<T>> map) {
93         Set JavaDoc<T> diffs = map.get(key);
94         if (diffs == null) {
95             diffs = new HashSet JavaDoc<T>();
96             map.put(key, diffs);
97         }
98         diffs.add(diff);
99     }
100     
101     private Node getCurrentNode(Node target, HashMap JavaDoc<Integer JavaDoc,Node> idToChangeds) {
102         Node newTarget = idToChangeds.get(target.getId());
103         if (newTarget == null || ! newTarget.isInTree()) {
104             List JavaDoc<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 JavaDoc<Difference> diffs, Node target) {
114         int id = target.getId();
115         SortedMap JavaDoc<Integer JavaDoc, Difference> toAddOrReorder = new TreeMap JavaDoc<Integer JavaDoc, 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 JavaDoc("Invald diff type");
156     }
157     
158     private List JavaDoc<Node> getChildrenNodes(Node target) {
159         NodeList JavaDoc cList = target.getChildNodes();
160         ArrayList JavaDoc<Node> nodes = new ArrayList JavaDoc<Node>();
161         for (int i=0; i<cList.getLength(); i++) {
162             nodes.add((Node) cList.item(i));
163         }
164         return nodes;
165     }
166
167     // position change node info needs to be get from new node info because
168
// reordering processing should be after attribute and token changes processed.
169
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 JavaDoc<Integer JavaDoc, Difference> toAddOrReorder) {
179         int id = target.getId();
180         List JavaDoc<Node> worksheet = getChildrenNodes(target);
181         
182         // for accurace, first remove the nodes to be reordered from the worksheet
183
for (Entry<Integer JavaDoc,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 JavaDoc<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         // calculate the final ordering on the worksheet
199
for (Entry<Integer JavaDoc,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                 // actual add of new child nodes
207
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         //calculate array for the reorder permutation
217
assert target.getChildNodes().getLength() == worksheet.size() : "Failed "+toAddOrReorder.values();
218         int[] permutation = new int[worksheet.size()];
219         NodeList JavaDoc 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         // update pure position change newNodeInfo because is used by event firing.
237
for (Entry<Integer JavaDoc,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 JavaDoc<Change> diffs, Node target) {
254         Node parent = null;
255         for (Change change : diffs) {
256             target = applyChange(change, target);
257             parent = change.getNewParent();
258         }
259         // updating the diffs as this will be use by children change processing
260
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         //Apply token change first as attribute changes will update newNodeInfo node.
269
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 JavaDoc<Node> ancestors2 = DiffFinder.getPathToRoot(target);
292         
293         // get new positions
294
NamedNodeMap JavaDoc nm2 = curNode.getAttributes();
295         HashMap JavaDoc<String JavaDoc, Integer JavaDoc> nodeToPosition = new HashMap JavaDoc<String JavaDoc, Integer JavaDoc>();
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 JavaDoc( i ) );
300         }
301         
302         // to ensure accurate order, do delete or modify first, spare adds to the end
303
List JavaDoc<Change.AttributeDiff> attrChanges = ((Change)de).getAttrChanges();
304         SortedMap JavaDoc<Integer JavaDoc, Node> positionToNode = new TreeMap JavaDoc<Integer JavaDoc, 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 JavaDoc 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 JavaDoc,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         // save
331
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 JavaDoc<Node> ancestors = model.modify( oldNode, newNode );
370         return ancestors.isEmpty() ? null : ancestors.get(0);
371     }
372     
373     ////////////////////////////////////////////////////////////////////////////////
374
// Member variables
375
////////////////////////////////////////////////////////////////////////////////
376

377     private XDMModel model;
378 }
379
Popular Tags