View Javadoc
1 /* 2 * Copyright (c) 2003 Scott Howlett & Paul Libbrecht. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * 3. The end-user documentation included with the redistribution, 18 * if any, must include the following acknowledgment: 19 * "This product includes software developed by the Xwing 20 * Project ( http://xwing.sourceforge.net/ )." 21 * Alternately, this acknowledgment may appear in the software itself, 22 * if and wherever such third-party acknowledgments normally appear. 23 * 24 * 4. The name "Xwing" must not be used to endorse or promote products 25 * derived from this software without prior written permission. For 26 * written permission, please contact the project authors via 27 * the Xwing project site, http://xwing.sourceforge.net/ . 28 * 29 * 5. Products derived from this software may not be called "Xwing", 30 * nor may "Xwing" appear in their name, without prior written 31 * permission of the Xwing project. 32 * 33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 34 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 36 * DISCLAIMED. IN NO EVENT SHALL THE XWING AUTHORS OR THE PROJECT 37 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 40 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 41 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 42 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 43 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 44 * SUCH DAMAGE. 45 * 46 * For more information on Xwing, please see 47 * < http://xwing.sourceforge.net/ >. 48 */ 49 package net.sourceforge.xwing.dom; 50 51 import java.util.Iterator; 52 import java.util.Vector; 53 54 55 import org.jaxen.BaseXPath; 56 import org.jaxen.JaxenException; 57 import org.jaxen.dom.DOMXPath; 58 import org.w3c.dom.Node; 59 60 public class XTreeNode { 61 62 private XTreeTemplate template; 63 private XTreeNode parent; 64 private Node node; 65 private String name; 66 private Vector children; 67 68 public XTreeNode(XTreeTemplate pTemplate, Node instanceNode) 69 throws Exception { 70 71 this( 72 pTemplate, 73 null, 74 (Node) (new DOMXPath(pTemplate.getPath())).selectSingleNode( 75 instanceNode)); 76 } 77 78 private XTreeNode(XTreeTemplate pTemplate, XTreeNode pParent, Node pNode) 79 throws JaxenException { 80 81 template = pTemplate; 82 parent = pParent; 83 node = pNode; 84 children = makeNodes(template, this, node); 85 BaseXPath expr = new DOMXPath(template.getNamePath()); 86 name = expr.valueOf(node); 87 } 88 89 private static Vector makeNodes( 90 XTreeTemplate pTemplate, 91 XTreeNode pParent, 92 Node pXmlParent) 93 throws JaxenException { 94 Vector children = new Vector(); 95 BaseXPath expr = new DOMXPath(pTemplate.getPath()); 96 Iterator it = pTemplate.getChildren().iterator(); 97 while (it.hasNext()) { 98 XTreeTemplate childTemplate = (XTreeTemplate) it.next(); 99 expr = new DOMXPath(childTemplate.getPath()); 100 Iterator it2 = expr.selectNodes(pParent.getNode()).iterator(); 101 while (it2.hasNext()) { 102 Node elem = (Node) it2.next(); 103 if (childTemplate.isVirtual()) { 104 children.addAll(makeNodes(childTemplate, pParent, elem)); 105 } else { 106 children.add(new XTreeNode(childTemplate, pParent, elem)); 107 } 108 } 109 110 } 111 return children; 112 } 113 114 public XTreeNode getChildAt(int childIndex) { 115 return (XTreeNode) children.elementAt(childIndex); 116 } 117 118 public void addChild(int index, XTreeNode child) { 119 children.add(index, child); 120 child.parent = this; 121 } 122 123 public void replaceChild(XTreeNode oldChild, XTreeNode newChild) { 124 int idx = children.indexOf(oldChild); 125 children.remove(idx); 126 children.insertElementAt(newChild, idx); 127 oldChild.parent = null; 128 newChild.parent = this; 129 } 130 131 public void removeChild(int index) { 132 children.remove(index); 133 } 134 135 public int getChildCount() { 136 return children.size(); 137 } 138 139 public XTreeNode getParent() { 140 return parent; 141 } 142 143 public int getIndex(XTreeNode node) { 144 return children.indexOf(node); 145 } 146 147 public boolean isLeaf() { 148 return template.getChildren().isEmpty(); 149 } 150 151 public String toString() { 152 return name; 153 } 154 155 public String getName() { 156 return name; 157 } 158 159 public void setName(String pName) { 160 name = pName; 161 } 162 163 public ChangeInfo describeChanges(XTreeNode other, Object eventSource) { 164 165 if (!sameNode(other)) { 166 return new ChangeInfo(this, other, ChangeInfo.COMPLEX); 167 } 168 ChangeInfo result = compareChildren(other); 169 170 if (!name.equals(other.name)) { 171 result.addContentChange(other, null, null); 172 } 173 174 return result; 175 } 176 177 private ChangeInfo compareChildren(XTreeNode other) { 178 int ourKidCount = children.size(); 179 int otherKidCount = other.children.size(); 180 181 Vector longer = ourKidCount > otherKidCount ? children : other.children; 182 Vector shorter = 183 ourKidCount > otherKidCount ? other.children : children; 184 185 int longSize = longer.size(); 186 int shortSize = shorter.size(); 187 int[] matchIndexes = new int[shortSize]; 188 int[] mismatchIndexes = new int[longSize - shortSize]; 189 Object[] mismatchKids = new Object[longSize - shortSize]; 190 Vector nameChanges = null; 191 Vector nameChangeIndexes = null; 192 193 int longBase = 0; 194 int mismatchBase = 0; 195 196 // Iterate through the shorter vector and record the indexes of 197 // matching children as we progress monotonically though the 198 // longer vector. If an element of the shorter vector is not 199 // found in the longer one, then a complex change must have 200 // occurred. 201 for (int i = 0; i < shortSize; ++i) { 202 XTreeNode shortNode = (XTreeNode) shorter.get(i); 203 204 while (true) { 205 XTreeNode longNode = (XTreeNode) longer.get(longBase); 206 if (shortNode.sameNode(longNode)) { 207 matchIndexes[i] = longBase; 208 209 if (!(shortNode.getName().equals(longNode.getName()))) { 210 XTreeNode otherNode = 211 ourKidCount > otherKidCount ? shortNode : longNode; 212 213 int otherIndex = 214 ourKidCount > otherKidCount ? i : longBase; 215 216 if (nameChanges == null) { 217 nameChanges = new Vector(); 218 nameChangeIndexes = new Vector(); 219 } 220 nameChanges.add(otherNode); 221 nameChangeIndexes.add(new Integer(otherIndex)); 222 } 223 ++longBase; 224 break; 225 } else if (longBase == longSize || shortSize == longSize) { 226 return new ChangeInfo(this, other, ChangeInfo.COMPLEX); 227 } else { 228 mismatchIndexes[mismatchBase] = longBase++; 229 mismatchKids[mismatchBase] = longNode; 230 ++mismatchBase; 231 } 232 } 233 } 234 235 while (longBase < longSize) { 236 mismatchIndexes[mismatchBase] = longBase; 237 mismatchKids[mismatchBase] = (XTreeNode) longer.get(longBase); 238 ++longBase; 239 ++mismatchBase; 240 } 241 242 ChangeInfo curState = null; 243 244 if (ourKidCount < otherKidCount) { 245 curState = 246 new ChangeInfo( 247 this, 248 other, 249 ChangeInfo.ADDS, 250 mismatchIndexes, 251 mismatchKids); 252 } else if (ourKidCount > otherKidCount) { 253 curState = 254 new ChangeInfo( 255 this, 256 other, 257 ChangeInfo.DELETES, 258 mismatchIndexes, 259 mismatchKids); 260 } else { 261 curState = new ChangeInfo(null, null, ChangeInfo.SAME); 262 } 263 264 for (int i = 0; i < shortSize; ++i) { 265 XTreeNode shortNode = (XTreeNode) shorter.get(i); 266 XTreeNode longNode = (XTreeNode) longer.get(matchIndexes[i]); 267 268 ChangeInfo childState = 269 (ourKidCount > otherKidCount) 270 ? longNode.compareChildren(shortNode) 271 : shortNode.compareChildren(longNode); 272 273 curState.merge(childState); 274 if (curState.getStatus() == ChangeInfo.COMPLEX) { 275 return curState; 276 } 277 278 } 279 280 if (nameChanges != null) { 281 Object[] nodes = nameChanges.toArray(); 282 283 int[] indices = new int[nameChanges.size()]; 284 285 for (int i = 0; i < nameChanges.size(); ++i) { 286 indices[i] = ((Integer) nameChangeIndexes.get(i)).intValue(); 287 } 288 289 curState.addContentChange(this, indices, nodes); 290 } 291 292 return curState; 293 } 294 295 public Node getNode() { 296 return node; 297 } 298 299 private Vector getChildren() { 300 return children; 301 } 302 303 public Vector getPath() { 304 Vector result = new Vector(); 305 appendPath(result); 306 return result; 307 } 308 309 private void appendPath(Vector pv) { 310 if (parent != null) { 311 parent.appendPath(pv); 312 } 313 pv.add(this); 314 } 315 316 private boolean sameNode(XTreeNode other) { 317 return template == other.template && node == other.node; 318 } 319 320 public XTreeTemplate getTemplate() { 321 return template; 322 } 323 324 }

This page was automatically generated by Maven