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)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
049     * <tt>compare(x, z)&gt;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 */