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