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