001/* 002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved. 003 * 004 * This software is open source. 005 * See the bottom of this file for the licence. 006 * 007 * $Id: NodeComparator.java,v 1.2 2001/05/31 07:20:22 jstrachan Exp $ 008 */ 009 010package org.dom4j.util; 011 012import java.util.Comparator; 013 014import org.dom4j.Attribute; 015import org.dom4j.Branch; 016import org.dom4j.CharacterData; 017import org.dom4j.CDATA; 018import org.dom4j.Comment; 019import org.dom4j.Document; 020import org.dom4j.DocumentType; 021import org.dom4j.Element; 022import org.dom4j.Entity; 023import org.dom4j.Namespace; 024import org.dom4j.Node; 025import org.dom4j.ProcessingInstruction; 026import org.dom4j.QName; 027import org.dom4j.Text; 028 029/** <p><code>NodeComparator</code> is a {@link Comparator} of Node instances 030 * which is capable of comparing Nodes for equality based on their values.</p> 031 * 032 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 033 * @version $Revision: 1.2 $ 034 */ 035public class NodeComparator implements Comparator { 036 037 /** 038 * Compares its two arguments for order. Returns a negative integer, 039 * zero, or a positive integer as the first argument is less than, equal 040 * to, or greater than the second.<p> 041 * 042 * The implementor must ensure that <tt>sgn(compare(x, y)) == 043 * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This 044 * implies that <tt>compare(x, y)</tt> must throw an exception if and only 045 * if <tt>compare(y, x)</tt> throws an exception.)<p> 046 * 047 * The implementor must also ensure that the relation is transitive: 048 * <tt>((compare(x, y)>0) && (compare(y, z)>0))</tt> implies 049 * <tt>compare(x, z)>0</tt>.<p> 050 * 051 * Finally, the implementer must ensure that <tt>compare(x, y)==0</tt> 052 * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all 053 * <tt>z</tt>.<p> 054 * 055 * It is generally the case, but <i>not</i> strictly required that 056 * <tt>(compare(x, y)==0) == (x.equals(y))</tt>. Generally speaking, 057 * any comparator that violates this condition should clearly indicate 058 * this fact. The recommended language is "Note: this comparator 059 * imposes orderings that are inconsistent with equals." 060 * 061 * @param o1 the first object to be compared. 062 * @param o2 the second object to be compared. 063 * @return a negative integer, zero, or a positive integer as the 064 * first argument is less than, equal to, or greater than the 065 * second. 066 * @throws ClassCastException if the arguments' types prevent them from 067 * being compared by this Comparator. 068 */ 069 public int compare(Object o1, Object o2) { 070 if ( o1 == o2 ) { 071 return 0; 072 } 073 else if ( o1 == null ) { 074 // null is less 075 return -1; 076 } 077 else if ( o2 == null ) { 078 return 1; 079 } 080 if ( o1 instanceof Node ) { 081 if ( o2 instanceof Node ) { 082 return compare( (Node) o1, (Node) o2 ); 083 } 084 else { 085 // Node implementations are greater 086 return 1; 087 } 088 } 089 else { 090 if ( o2 instanceof Node ) { 091 // Node implementations are greater 092 return -1; 093 } 094 else { 095 if ( o1 instanceof Comparable ) { 096 Comparable c1 = (Comparable) o1; 097 return c1.compareTo( o2 ); 098 } 099 else { 100 int answer = o1.getClass().getName().compareTo( 101 o2.getClass().getName() 102 ); 103 if ( answer == 0 ) { 104 answer = o1.hashCode() - o2.hashCode(); 105 } 106 return answer; 107 } 108 } 109 } 110 } 111 112 public int compare( Node n1, Node n2 ) { 113 int nodeType1 = n1.getNodeType(); 114 int nodeType2 = n2.getNodeType(); 115 int answer = nodeType1 - nodeType2; 116 if ( answer != 0 ) { 117 return answer; 118 } 119 else { 120 switch (nodeType1) { 121 case Node.ELEMENT_NODE: 122 return compare((Element) n1, (Element) n2); 123 case Node.DOCUMENT_NODE: 124 return compare((Document) n1, (Document) n2); 125 case Node.ATTRIBUTE_NODE: 126 return compare((Attribute) n1, (Attribute) n2); 127 case Node.TEXT_NODE: 128 return compare((Text) n1, (Text) n2); 129 case Node.CDATA_SECTION_NODE: 130 return compare((CDATA) n1, (CDATA) n2); 131 case Node.ENTITY_REFERENCE_NODE: 132 return compare((Entity) n1, (Entity) n2); 133 case Node.PROCESSING_INSTRUCTION_NODE: 134 return compare((ProcessingInstruction) n1, (ProcessingInstruction) n2); 135 case Node.COMMENT_NODE: 136 return compare((Comment) n1, (Comment) n2); 137 case Node.DOCUMENT_TYPE_NODE: 138 return compare((DocumentType) n1, (DocumentType) n2); 139 case Node.NAMESPACE_NODE: 140 return compare((Namespace) n1, (Namespace) n2); 141 default: 142 throw new RuntimeException( 143 "Invalid node types. node1: " + n1 + " and node2: " + n2 144 ); 145 } 146 } 147 } 148 149 public int compare( Document n1, Document n2 ) { 150 int answer = compare( n1.getName(), n2.getName() ); 151 if ( answer == 0 ) { 152 answer = compare( n1.getDocType(), n2.getDocType() ); 153 if ( answer == 0 ) { 154 answer = compareContent( n1, n2 ); 155 } 156 } 157 return answer; 158 } 159 160 public int compare( Element n1, Element n2 ) { 161 int answer = compare( n1.getQName(), n2.getQName() ); 162 if ( answer == 0 ) { 163 // lets compare attributes 164 int c1 = n1.attributeCount(); 165 int c2 = n2.attributeCount(); 166 answer = c1 - c2; 167 if ( answer == 0 ) { 168 for ( int i = 0; i < c1; i++ ) { 169 Attribute a1 = n1.attribute(i); 170 Attribute a2 = n2.attribute(i); 171 answer = compare( a1, a2 ); 172 if ( answer != 0 ) { 173 return answer; 174 } 175 } 176 answer = compareContent( n1, n2 ); 177 } 178 } 179 return answer; 180 } 181 182 public int compare( Attribute n1, Attribute n2 ) { 183 int answer = compare( n1.getQName(), n2.getQName() ); 184 if ( answer == 0 ) { 185 answer = compare( n1.getValue(), n2.getValue() ); 186 } 187 return answer; 188 } 189 190 public int compare( QName n1, QName n2 ) { 191 int answer = compare( n1.getNamespaceURI(), n2.getNamespaceURI() ); 192 if ( answer == 0 ) { 193 answer = compare( n1.getQualifiedName(), n2.getQualifiedName() ); 194 } 195 return answer; 196 } 197 198 public int compare( Namespace n1, Namespace n2 ) { 199 int answer = compare( n1.getURI(), n2.getURI() ); 200 if ( answer == 0 ) { 201 answer = compare( n1.getPrefix(), n2.getPrefix() ); 202 } 203 return answer; 204 } 205 206 public int compare( CharacterData t1, CharacterData t2 ) { 207 return compare( t1.getText(), t2.getText() ); 208 } 209 210 public int compare( DocumentType o1, DocumentType o2 ) { 211 if ( o1 == o2 ) { 212 return 0; 213 } 214 else if ( o1 == null ) { 215 // null is less 216 return -1; 217 } 218 else if ( o2 == null ) { 219 return 1; 220 } 221 int answer = compare( o1.getPublicID(), o2.getPublicID() ); 222 if ( answer == 0 ) { 223 answer = compare( o1.getSystemID(), o2.getSystemID() ); 224 if ( answer == 0 ) { 225 answer = compare( o1.getName(), o2.getName() ); 226 } 227 } 228 return answer; 229 } 230 231 public int compare( Entity n1, Entity n2 ) { 232 int answer = compare( n1.getName(), n2.getName() ); 233 if ( answer == 0 ) { 234 answer = compare( n1.getText(), n2.getText() ); 235 } 236 return answer; 237 } 238 239 public int compare( ProcessingInstruction n1, ProcessingInstruction n2 ) { 240 int answer = compare( n1.getTarget(), n2.getTarget() ); 241 if ( answer == 0 ) { 242 answer = compare( n1.getText(), n2.getText() ); 243 } 244 return answer; 245 } 246 247 public int compareContent( Branch b1, Branch b2 ) { 248 int c1 = b1.nodeCount(); 249 int c2 = b2.nodeCount(); 250 int answer = c1 - c2; 251 if ( answer == 0 ) { 252 for ( int i = 0; i < c1; i++ ) { 253 Node n1 = b1.node(i); 254 Node n2 = b2.node(i); 255 answer = compare( n1, n2 ); 256 if ( answer != 0 ) { 257 break; 258 } 259 } 260 } 261 return answer; 262 } 263 264 public int compare( String o1, String o2 ) { 265 if ( o1 == o2 ) { 266 return 0; 267 } 268 else if ( o1 == null ) { 269 // null is less 270 return -1; 271 } 272 else if ( o2 == null ) { 273 return 1; 274 } 275 return o1.compareTo( o2 ); 276 } 277} 278 279 280 281 282/* 283 * Redistribution and use of this software and associated documentation 284 * ("Software"), with or without modification, are permitted provided 285 * that the following conditions are met: 286 * 287 * 1. Redistributions of source code must retain copyright 288 * statements and notices. Redistributions must also contain a 289 * copy of this document. 290 * 291 * 2. Redistributions in binary form must reproduce the 292 * above copyright notice, this list of conditions and the 293 * following disclaimer in the documentation and/or other 294 * materials provided with the distribution. 295 * 296 * 3. The name "DOM4J" must not be used to endorse or promote 297 * products derived from this Software without prior written 298 * permission of MetaStuff, Ltd. For written permission, 299 * please contact dom4j-info@metastuff.com. 300 * 301 * 4. Products derived from this Software may not be called "DOM4J" 302 * nor may "DOM4J" appear in their names without prior written 303 * permission of MetaStuff, Ltd. DOM4J is a registered 304 * trademark of MetaStuff, Ltd. 305 * 306 * 5. Due credit should be given to the DOM4J Project 307 * (http://dom4j.org/). 308 * 309 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS 310 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 311 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 312 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 313 * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 314 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 315 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 316 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 317 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 318 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 319 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 320 * OF THE POSSIBILITY OF SUCH DAMAGE. 321 * 322 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved. 323 * 324 * $Id: NodeComparator.java,v 1.2 2001/05/31 07:20:22 jstrachan Exp $ 325 */