PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/xml.xdm/src/org/netbeans/modules/xml/xdm/diff/MergeDiff.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 403 lines | 296 code | 42 blank | 65 comment | 86 complexity | 5bde81f3fd725489c7654d6e177ce66b MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.xml.xdm.diff;
  45. import java.util.ArrayList;
  46. import java.util.HashMap;
  47. import java.util.HashSet;
  48. import java.util.Iterator;
  49. import java.util.List;
  50. import java.util.Map;
  51. import java.util.Map.Entry;
  52. import java.util.Set;
  53. import java.util.SortedMap;
  54. import java.util.TreeMap;
  55. import org.netbeans.modules.xml.xdm.XDMModel;
  56. import org.netbeans.modules.xml.xdm.nodes.Attribute;
  57. import org.netbeans.modules.xml.xdm.nodes.Node;
  58. import org.netbeans.modules.xml.xdm.nodes.NodeImpl;
  59. import org.netbeans.modules.xml.xdm.nodes.Element;
  60. import org.w3c.dom.NamedNodeMap;
  61. import org.w3c.dom.NodeList;
  62. /*
  63. * This class is used by XDMTreeDiff (during Sync) to merge changes back to the original
  64. * xdm model document
  65. *
  66. * @author Ayub Khan
  67. */
  68. public class MergeDiff {
  69. /** Creates a new instance of MergeDiff */
  70. public MergeDiff() {
  71. }
  72. public void merge(XDMModel model, List<Difference> deList) {
  73. this.model = model;
  74. HashMap<Node, Set<Change>> changesByNode = new HashMap<Node, Set<Change>>();
  75. HashMap<Node, Set<Difference>> childrenDiffsByNode = new HashMap<Node, Set<Difference>>();
  76. HashMap<Integer,Node> idToChangeds = new HashMap<Integer,Node>();
  77. for ( Difference de: deList ) {
  78. if (de instanceof Change) {
  79. Change change = (Change) de;
  80. if (change.isAttributeChanged() || change.isTokenChanged()) {
  81. addDiffToMap(change.getOldNodeInfo().getNode(), change, changesByNode);
  82. }
  83. if (change.isPositionChanged()) {
  84. addDiffToMap(de.getOldNodeInfo().getParent(), change, childrenDiffsByNode);
  85. }
  86. } else {
  87. addDiffToMap(de.getOldNodeInfo().getParent(), de, childrenDiffsByNode);
  88. }
  89. }
  90. for (Map.Entry<Node, Set<Change>> e : changesByNode.entrySet()) {
  91. Node target = getCurrentNode(e.getKey(), idToChangeds);
  92. assert (target != null) : "target "+e.getKey().getId()+"is no longer inTree";
  93. Set<Change> diffs = e.getValue();
  94. Node changed = applyChanges(diffs, target);
  95. assert changed.getId() == target.getId() : "changed id should not change";
  96. idToChangeds.put(changed.getId(), changed);
  97. }
  98. for (Map.Entry<Node, Set<Difference>> e : childrenDiffsByNode.entrySet()) {
  99. Node target = getCurrentNode(e.getKey(), idToChangeds);
  100. assert (target != null) : "target "+e.getKey().getId()+"is no longer inTree";
  101. Set<Difference> diffs = e.getValue();
  102. Node processed = applyChildrenDiffs(diffs, target);
  103. assert processed.getId() == target.getId() : "processed id should not change";
  104. }
  105. }
  106. private <T extends Difference>
  107. void addDiffToMap(Node key, T diff, HashMap<Node, Set<T>> map) {
  108. Set<T> diffs = map.get(key);
  109. if (diffs == null) {
  110. diffs = new HashSet<T>();
  111. map.put(key, diffs);
  112. }
  113. diffs.add(diff);
  114. }
  115. private Node getCurrentNode(Node target, HashMap<Integer,Node> idToChangeds) {
  116. Node newTarget = idToChangeds.get(target.getId());
  117. if (newTarget == null || ! newTarget.isInTree()) {
  118. List<Node> path = DiffFinder.getPathToRoot(target);
  119. if (path != null && ! path.isEmpty()) {
  120. newTarget = path.get(0);
  121. idToChangeds.put(target.getId(), newTarget);
  122. }
  123. }
  124. return newTarget;
  125. }
  126. private Node applyChildrenDiffs(final Set<Difference> diffs, Node target) {
  127. int id = target.getId();
  128. SortedMap<Integer, Difference> toAddOrReorder = new TreeMap<Integer, Difference>();
  129. for (Difference d : diffs) {
  130. if (d instanceof Delete ) {
  131. delete(d.getOldNodeInfo());
  132. target = d.getNewParent();
  133. assert id == target.getId();
  134. } else if (d instanceof Add) {
  135. toAddOrReorder.put(d.getNewNodeInfo().getPosition(), d);
  136. } else if (d instanceof Change) {
  137. Change change = (Change) d;
  138. assert (change.isPositionChanged() &&
  139. change.getOldNodeInfo().getParent().getId() == target.getId());
  140. toAddOrReorder.put(change.getNewNodeInfo().getPosition(), change);
  141. }
  142. }
  143. target = processAddOrReorder(target, toAddOrReorder);
  144. assert id == target.getId();
  145. for (Difference d : diffs) {
  146. d.setNewParent(target);
  147. assert getReleventDiffNodeInfo(d).getNode().isInTree() : "Processed child not in tree: "+d;
  148. }
  149. return target;
  150. }
  151. private NodeInfo getReleventDiffNodeInfo (Difference d) {
  152. if (d instanceof Add) {
  153. return d.getNewNodeInfo();
  154. } else if (d instanceof Change) {
  155. Change c = (Change) d;
  156. if (c.isAttributeChanged() || c.isTokenChanged()) {
  157. return c.getNewNodeInfo();
  158. } else {
  159. return c.getOldNodeInfo();
  160. }
  161. } else if (d instanceof Delete) {
  162. return d.getOldNodeInfo();
  163. }
  164. throw new IllegalArgumentException("Invald diff type");
  165. }
  166. private List<Node> getChildrenNodes(Node target) {
  167. NodeList cList = target.getChildNodes();
  168. ArrayList<Node> nodes = new ArrayList<Node>();
  169. for (int i=0; i<cList.getLength(); i++) {
  170. nodes.add((Node) cList.item(i));
  171. }
  172. return nodes;
  173. }
  174. // position change node info needs to be get from new node info because
  175. // reordering processing should be after attribute and token changes processed.
  176. private NodeInfo getReorderNodeInfo(Change change) {
  177. assert change.isPositionChanged();
  178. if (change.isAttributeChanged() || change.isTokenChanged()) {
  179. return change.getNewNodeInfo();
  180. } else {
  181. return change.getOldNodeInfo();
  182. }
  183. }
  184. private Node processAddOrReorder(Node target, SortedMap<Integer, Difference> toAddOrReorder) {
  185. int id = target.getId();
  186. List<Node> worksheet = getChildrenNodes(target);
  187. // for accurace, first remove the nodes to be reordered from the worksheet
  188. for (Entry<Integer,Difference> e : toAddOrReorder.entrySet()) {
  189. Difference diff = e.getValue();
  190. if (diff instanceof Change) {
  191. Node toReorder = getReorderNodeInfo((Change)diff).getNode();
  192. if (! worksheet.remove(toReorder)) {
  193. for (Iterator<Node> i = worksheet.iterator(); i.hasNext();) {
  194. Node n = i.next();
  195. if (n.getId() == toReorder.getId()) {
  196. i.remove();
  197. break;
  198. }
  199. }
  200. }
  201. }
  202. }
  203. // calculate the final ordering on the worksheet
  204. for (Entry<Integer,Difference> e : toAddOrReorder.entrySet()) {
  205. Difference diff = e.getValue();
  206. if (diff instanceof Change) {
  207. Node toReorder = getReorderNodeInfo((Change)diff).getNode();
  208. int index = diff.getNewNodeInfo().getPosition();
  209. worksheet.add(index, toReorder);
  210. } else if (diff instanceof Add) {
  211. // actual add of new child nodes
  212. add(target, (Add) diff);
  213. target = diff.getNewParent();
  214. assert id == target.getId();
  215. Node added = ((Add)diff).getNewNodeInfo().getNode();
  216. int index = ((Add)diff).getNewNodeInfo().getPosition();
  217. worksheet.add(index, added);
  218. }
  219. }
  220. //calculate array for the reorder permutation
  221. assert target.getChildNodes().getLength() == worksheet.size() : "Failed "+toAddOrReorder.values();
  222. int[] permutation = new int[worksheet.size()];
  223. NodeList cList = target.getChildNodes();
  224. for (int i=0; i<cList.getLength(); i++) {
  225. Node m = (Node) cList.item(i);
  226. int j = -1;
  227. for (int k=0; k<worksheet.size(); k++) {
  228. Node n = worksheet.get(k);
  229. if (m == n || n.isEquivalentNode(m)) {
  230. j = k;
  231. break;
  232. }
  233. }
  234. assert j > -1 : "current item "+i+" is not on worksheet";
  235. permutation[i] = j;
  236. }
  237. target = reorder(target, permutation);
  238. // update pure position change newNodeInfo because is used by event firing.
  239. for (Entry<Integer,Difference> e : toAddOrReorder.entrySet()) {
  240. Difference diff = e.getValue();
  241. if (diff instanceof Change) {
  242. Change c = (Change)diff;
  243. if (c.isPositionChanged() &&
  244. ! c.isAttributeChanged() && ! c.isTokenChanged())
  245. {
  246. c.getNewNodeInfo().setNode(c.getOldNodeInfo().getNode());
  247. }
  248. }
  249. }
  250. return target;
  251. }
  252. private Node applyChanges(Set<Change> diffs, Node target) {
  253. Node parent = null;
  254. for (Change change : diffs) {
  255. target = applyChange(change, target);
  256. parent = change.getNewParent();
  257. }
  258. // updating the diffs as this will be use by children change processing
  259. for (Change change : diffs) {
  260. change.getNewNodeInfo().setNode(target);
  261. change.setNewParent(parent);
  262. }
  263. return target;
  264. }
  265. private Node applyChange(final Change de, Node target) {
  266. //Apply token change first as attribute changes will update newNodeInfo node.
  267. Node oldNode = de.getOldNodeInfo().getNode();
  268. assert target.getId() == oldNode.getId() : "change target node id != old node id";
  269. Node curNode = de.getNewNodeInfo().getNode();
  270. if (de.isTokenChanged()) {
  271. NodeImpl newNode = createClone(target);
  272. newNode.copyTokens( curNode );
  273. de.setNewParent(modify(oldNode, newNode));
  274. target = newNode;
  275. }
  276. if (de.isAttributeChanged()) {
  277. assert oldNode.getLocalName().equals(curNode.getLocalName());
  278. applyAttrTokenChange(target, de);
  279. } else if (de.isTokenChanged()) {
  280. de.getNewNodeInfo().setNode(target);
  281. }
  282. return de.getNewNodeInfo().getNode();
  283. }
  284. private void applyAttrTokenChange(Node target, Change de) {
  285. Node curNode = de.getNewNodeInfo().getNode();
  286. List<Node> ancestors2 = DiffFinder.getPathToRoot(target);
  287. // get new positions
  288. NamedNodeMap nm2 = curNode.getAttributes();
  289. HashMap<String, Integer> nodeToPosition = new HashMap<String, Integer>();
  290. for ( int i=0; i < nm2.getLength(); i++ ) {
  291. Attribute newAttr = (Attribute) nm2.item(i);
  292. assert newAttr.getName() != null;
  293. nodeToPosition.put( newAttr.getName(), new Integer( i ) );
  294. }
  295. // to ensure accurate order, do delete or modify first, spare adds to the end
  296. List<Change.AttributeDiff> attrChanges = ((Change)de).getAttrChanges();
  297. SortedMap<Integer, Node> positionToNode = new TreeMap<Integer, Node>();
  298. for (Change.AttributeDiff attrDiff:attrChanges) {
  299. Attribute oldAttr = attrDiff.getOldAttribute();
  300. Attribute currAttr = attrDiff.getNewAttribute();
  301. if ( oldAttr != null ) {
  302. if ( currAttr == null ) {
  303. ancestors2 = model.delete( oldAttr );
  304. } else {
  305. NodeImpl cloneAttr = createClone(oldAttr);
  306. cloneAttr.copyTokens(currAttr);
  307. ancestors2 = model.modify(oldAttr, cloneAttr);
  308. }
  309. } else if ( currAttr != null ) {
  310. Integer pos = nodeToPosition.get(currAttr.getName());
  311. assert pos != null : "Attribute "+currAttr.getName() + " \n" + de + nodeToPosition + " \n" + positionToNode;
  312. positionToNode.put(pos, currAttr);
  313. }
  314. }
  315. for (Entry<Integer,Node> e : positionToNode.entrySet()) {
  316. Node copy = createCopy(e.getValue());
  317. curNode = (Element) ancestors2.get(0);
  318. ancestors2 = model.add(curNode, copy, e.getKey());
  319. }
  320. curNode = (Element) ancestors2.get(0);
  321. // save
  322. de.getNewNodeInfo().setNode(curNode);
  323. assert ancestors2.get(1).isInTree() : "new parent not intree";
  324. de.setNewParent(ancestors2.get(1));
  325. }
  326. private NodeImpl createCopy(final Node currNode) {
  327. NodeImpl newNode = (NodeImpl) ((NodeImpl)currNode).cloneNode(true, false);
  328. return newNode;
  329. }
  330. private NodeImpl createClone(final Node oldNode) {
  331. NodeImpl newNode = (NodeImpl) ((NodeImpl)oldNode).clone( false, false, false );
  332. return newNode;
  333. }
  334. private void add(Node parent, Difference diff) {
  335. NodeInfo newInfo = diff.getNewNodeInfo();
  336. int pos = newInfo.getPosition();
  337. assert pos <= parent.getChildNodes().getLength();
  338. Node newNode = null;
  339. if (diff instanceof Change) {
  340. newNode = createClone(newInfo.getNode());
  341. } else {
  342. newNode = createCopy(newInfo.getNode());
  343. }
  344. diff.setNewParent(model.add(parent, newNode, pos).get(0));
  345. newInfo.setNode(newNode);
  346. }
  347. private Node reorder(Node parent, int[] permutation) {
  348. return model.reorderChildren(parent, permutation).get(0);
  349. }
  350. private void delete(NodeInfo oldInfo) {
  351. oldInfo.setNewParent(model.delete(oldInfo.getNode()).get(0));
  352. }
  353. private Node modify(Node oldNode, Node newNode) {
  354. List<Node> ancestors = model.modify( oldNode, newNode );
  355. return ancestors.isEmpty() ? null : ancestors.get(0);
  356. }
  357. ////////////////////////////////////////////////////////////////////////////////
  358. // Member variables
  359. ////////////////////////////////////////////////////////////////////////////////
  360. private XDMModel model;
  361. }