1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.xmerge.merger.diff;
25 
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28 import org.w3c.dom.NamedNodeMap;
29 
30 import org.openoffice.xmerge.ConverterCapabilities;
31 import org.openoffice.xmerge.merger.Iterator;
32 import org.openoffice.xmerge.util.Debug;
33 import org.openoffice.xmerge.util.Resources;
34 
35 import java.util.Vector;
36 import java.util.List;
37 
38 
39 /**
40  *  <p>This is an implementation of the <code>Iterator</code> interface.
41  *  It will traverse the tree and find <code>Node</code> sequences.</p>
42  *
43  *  <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code> will
44  *  be a snap shot of that tree. That means even the tree is modified later,
45  *  than the cached paragraph <code>Node</code> list will not be updated
46  *  accordingly.  For this reason and for performance reasons this
47  *  <code>Iterator</code> does not support any operation methods such as
48  *  insert, remove or replace.  The main purpose of this
49  *  <code>Iterator</code> is to be used with difference, not with merge.</p>
50  *
51  *  @author smak
52  */
53 public abstract class NodeIterator implements Iterator {
54 
55     private List nodeList = null;
56     private int currentPosition = 0;
57     private Node root;
58     private ConverterCapabilities cc_ = null;
59 
60 
61     /**
62      *  Standard constructor.
63      *
64      *  @param  cc    The <code>ConverterCapabilities</code>.
65      *  @param  node  The initial root <code>Node</code>.
66      */
NodeIterator(ConverterCapabilities cc, Node node)67     public NodeIterator(ConverterCapabilities cc, Node node) {
68         cc_ = cc;
69         nodeList = new Vector();
70         root = node;
71         markTree(node);
72     }
73 
74 
next()75     public Object next() {
76         if (currentPosition < nodeList.size() - 1) {
77             currentPosition++;
78             return currentElement();
79         } else {
80             return null;
81         }
82     }
83 
84 
previous()85     public Object previous() {
86         if (currentPosition > 0) {
87             currentPosition--;
88             return currentElement();
89         } else {
90             return null;
91         }
92     }
93 
94 
start()95     public Object start() {
96         currentPosition = 0;
97         return currentElement();
98     }
99 
100 
end()101     public Object end() {
102         int size = nodeList.size();
103 
104         if (size > 0) {
105             currentPosition = size - 1;
106             return currentElement();
107         } else  {
108             return null;
109         }
110     }
111 
112 
currentElement()113     public Object currentElement() {
114 
115         if (currentPosition < 0 || currentPosition >= nodeList.size()) {
116             return null;
117         }
118 
119         return nodeList.get(currentPosition);
120     }
121 
122 
elementCount()123     public int elementCount() {
124         return nodeList.size();
125     }
126 
127 
equivalent(Object obj1, Object obj2)128     public boolean equivalent(Object obj1, Object obj2) {
129         boolean equal = false;
130         String errMsg = null;
131         if (!(obj1 instanceof Node && obj2 instanceof Node)) {
132             errMsg = Resources.getInstance().getString("NOT_NODE_ERROR");
133             Debug.log(Debug.ERROR, errMsg);
134         } else {
135             Node node1 = (Node)obj1;
136             Node node2 = (Node)obj2;
137 
138             equal = compareNode(node1, node2);
139         }
140         return equal;
141     }
142 
143 
refresh()144     public void refresh() {
145         nodeList = new Vector();
146         markTree(root);
147         currentPosition = 0;
148     }
149 
150 
151     /**
152      *  Used to compare two <code>Node</code> objects (type/name/value)
153      *  and all their children <code>Node</code> objects.
154      *
155      *  @param  node1  The first <code>Node</code> to compare.
156      *  @param  node2  The second <code>Node</code> to compare.
157      *
158      *  @return  true if <code>Node</code> is equal, false otherwise.
159      */
compareNode(Node node1, Node node2)160     protected boolean compareNode(Node node1, Node node2) {
161         boolean equal = false;
162 
163         nodeCheck: {
164 
165             if (node1 == null || node2 == null) {
166                 break nodeCheck;
167             }
168 
169             // nodevalue is short
170             if (node1.getNodeType() != node2.getNodeType()) {
171                 break nodeCheck;
172             }
173 
174             // nodeName will not be null
175             if (!node1.getNodeName().equals(node2.getNodeName())) {
176                 break nodeCheck;
177             }
178 
179             // nodeValue can be null for a lot of type of cells
180             if (node1.getNodeValue() == null && node2.getNodeValue() == null) {
181                 // empty
182             } else if (node1.getNodeValue() == null ||
183                        node2.getNodeValue() == null) {
184                 break nodeCheck;
185             } else if (!node1.getNodeValue().equals(node2.getNodeValue())) {
186                 break nodeCheck;
187             }
188 
189             // try to compare attributes
190             if (!attributesEqual(node1, node2)) {
191                 break nodeCheck;
192             }
193 
194             // don't need to compare if both node do not have children
195             if (!node1.hasChildNodes() && !node2.hasChildNodes()) {
196                 equal = true;
197                 break nodeCheck;
198             // don't need to compare if one node has children but not the other
199             } else if (!node1.hasChildNodes() || !node2.hasChildNodes()) {
200                 equal = false;
201                 break nodeCheck;
202             // need to compare if both node has children
203             } else if (!childrenEqual(node1, node2)) {
204                 break nodeCheck;
205             }
206 
207             equal = true;
208         }
209 
210         return equal;
211     }
212 
213 
214     /**
215      *  Compare the children of two <code>Node</code> objects.  This
216      *  method can be intentionally overridden by any class that
217      *  extend from <code>NodeIterator</code> so that it can have
218      *  its own children comparison if necessary.
219      *
220      *  @param  node1  The first <code>Node</code> to compare.
221      *  @param  node2  The second <code>Node</code> to compare.
222      *
223      *  @return  true if children are equal, false otherwise.
224      */
childrenEqual(Node node1, Node node2)225     protected boolean childrenEqual(Node node1, Node node2) {
226 
227         boolean equal = false;
228 
229         childrenCheck: {
230             NodeList node1Children = node1.getChildNodes();
231             NodeList node2Children = node2.getChildNodes();
232 
233             if (node1Children == null || node2Children == null) {
234                 break childrenCheck;
235             }
236 
237             if (node1Children.getLength() != node2Children.getLength())  {
238                 break childrenCheck;
239             }
240 
241             // compare all the childrens
242             equal = true;
243 
244             for (int i = 0; i < node1Children.getLength(); i++) {
245                 if (!compareNode(node1Children.item(i),
246                                  node2Children.item(i))) {
247                     equal = false;
248                     break childrenCheck;
249                 }
250             }
251         }
252 
253         return equal;
254     }
255 
256 
257     /**
258      *  Compare attributes of two <code>Node</code> objects.  This
259      *  method can be intentionally overridden by any class that
260      *  extends from <code>NodeIterator</code> so that it can have
261      *  its own attribute comparison.
262      *
263      *  @param  node1  The first <code>Node</code> to compare.
264      *  @param  node2  The second <code>Node</code> to compare.
265      *
266      *  @return  true if attributes are equal, false otherwise.
267      */
attributesEqual(Node node1, Node node2)268     protected boolean attributesEqual(Node node1, Node node2) {
269 
270         boolean equal = false;
271         String nodeName = node1.getNodeName();
272         NamedNodeMap attrNode[] = new NamedNodeMap[2];
273         attrNode[0] = node1.getAttributes();
274         attrNode[1] = node2.getAttributes();
275 
276         // attribute node will be null if node is not an element node
277         // and attribute nodes are equal if both are not element node
278         if (attrNode[0] == null || attrNode[1] == null) {
279             if (attrNode[0] == null && attrNode[1] == null) {
280                 equal = true;
281             }
282             return equal;
283         }
284 
285         // compare the attributes from node1 vs node2 and node2 vs node1
286         // though it's a little inefficient for the duplication of comparison
287         // as the number of attributes is not so many, it should not be
288         // a big problem.
289         int len [] = new int[2];
290         int src, dst;
291 
292         attrCheck: {
293             for (int i = 0; i < 2; i++) {
294 
295                 if (i == 0) {
296                     src = 0;
297                     dst = 1;
298                 } else {
299                     src = 1;
300                     dst = 0;
301                 }
302 
303                 len[src] = attrNode[src].getLength();
304 
305                 for (int j = 0; j < len[src]; j++) {
306                     Node srcAttr = attrNode[src].item(j);
307                     String srcAttrName = srcAttr.getNodeName();
308 
309                     // copy the supported attrs
310                     if (cc_ == null ||
311                         cc_.canConvertAttribute(nodeName, srcAttrName)) {
312 
313                         // check whether the attribute exist in dst node
314                         Node dstAttr = attrNode[dst].getNamedItem(srcAttrName);
315 
316                         if (dstAttr == null)  {
317                             Debug.log(Debug.INFO,
318                                       "[NodeIterator] Attr not exist in dst - "
319                                       + srcAttrName);
320                             break attrCheck;
321                         }
322 
323                         // then compare the attribute values
324                         if (!srcAttr.getNodeValue().equals(
325                              dstAttr.getNodeValue())) {
326                             Debug.log(Debug.INFO,
327                                       "[NodeIterator] Attr diff src: " +
328                                       srcAttr.getNodeValue() + " dst: "+
329                                       dstAttr.getNodeValue());
330                             break attrCheck;
331                         }
332                     } // end if cc_ loop
333                 } // end for j loop
334             } // end for i loop
335 
336             // the whole checking is done smoothly and all attributes are equal
337             equal = true;
338         }
339 
340         return equal;
341     }
342 
343 
344     /**
345      *  Check whether a <code>Node</code> is supported.  This method
346      *  can be intentionally overridden by any class that extends from
347      *  <code>NodeIterator</code> so that it can specify which
348      *  <code>Node</code> to support.
349      *
350      *  @param  node  <code>Node</code> to check.
351      *
352      *  @return  true if <code>Node</code> is supported, false otherwise.
353      */
nodeSupported(Node node)354     protected abstract boolean nodeSupported(Node node);
355 
356     // doing a depth first search for the tree and mark all supported nodes
markTree(Node node)357     private void markTree(Node node) {
358 
359         // if this is a supported node, then we add it to our cache table
360         if (nodeSupported(node)) {
361             nodeList.add(node);
362         } else {
363         // or we go through all children nodes recursively
364         // (can be optimized in future)
365             String nodeName = node.getNodeName();
366             if ( cc_ == null || cc_.canConvertTag(nodeName)) {
367                 NodeList nodeList = node.getChildNodes();
368                 int nodeListLength = nodeList.getLength();
369                 for (int i = 0; i < nodeListLength; i++) {
370                     markTree(nodeList.item(i));
371                 }
372             }
373             else {
374                 Debug.log(Debug.INFO, " [NodeIterator::markTree] Skipping node "
375                                       + nodeName);
376             }
377         }
378     }
379 }
380 
381