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