001    // Copyright 2007 Regents of the University of California.  May be used
002    // under the terms of the revised BSD license.  See LICENSING for details.
003    /**
004     * @author Tyler Close
005     */
006    package org.joe_e;
007    
008    import java.lang.reflect.AccessibleObject;
009    import java.lang.reflect.Field;
010    import java.lang.reflect.Modifier;
011    import java.util.Arrays;
012    import java.util.Comparator;
013    
014    /**
015     * This abstract class contains implementations of the equals() and hashCode()
016     * methods that satisfy the Selfless interface.  The provided equals() method
017     * computes equality of all fields using reflection.
018     */
019    public abstract class Struct implements Selfless {
020    
021        protected Struct() {}
022        
023        /**
024         * Tests for equality with another object.  An obect is equal to this one
025         * if it is of identical type and each field is equal for the two objects.
026         * The objects' fields are equal if both are null, or if their values
027         * return true for <code>equals()</code>.  This implementation uses
028         * reflection to work for any subclass of <code>Struct</code>.
029         * @param other candidate object
030         * @return true if it is equal to this object
031         */
032        public final boolean equals(final Object other) {
033            if (other == null || getClass() != other.getClass()) {
034                return false;
035            }
036            
037            // traverse class hierarchy, finding declared fields.  This is
038            // necessary since getFields() only returns public fields.
039            for (Class<?> i = getClass(); i != Struct.class; i = i.getSuperclass()) {
040                final Field[] fields = i.getDeclaredFields();
041                AccessibleObject.setAccessible(fields, true);
042                Arrays.sort(fields, 
043                    new Comparator<Field>() {
044                        public int compare(final Field a, final Field b) {
045                             return a.getName().compareTo(b.getName());
046                        }
047                    });
048                for (final Field f : fields) {
049                    if (!Modifier.isStatic(f.getModifiers())) {
050                        try {
051                            final Object a = f.get(this);
052                            final Object b = f.get(other);                          
053                            if (a == null ? b != null : !a.equals(b)) {
054                                return false;
055                            }
056                        } catch (final IllegalAccessException e) {
057                            // Should never happen.
058                            throw new IllegalAccessError();
059                        }
060                    }
061                }
062            }
063            
064            return true;
065        }
066        
067        /**
068         * Calculates the hash code.
069         * 
070         * This method will satisfy the contract of the hashCode method for any
071         * subclass of <code>Struct</code>.  (two structs that are 
072         * <code>equal()</code> structs will always have the same hashCode).  The 
073         * precise return value of this method is unspecified, and may change in
074         * future releases.
075         * @return a hash value
076         */
077        public final int hashCode() { 
078            return getClass().getName().hashCode();
079        }
080    }