/*
 * Copyright (C) 1999-2004 <a href="mailto:mandarax@jbdietrich.com">Jens Dietrich</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.mandarax.kernel;


/**
 * Abstract super class for predicates. Mainly <code>equals()</code>
 * and <code>hashCode()</code> are implemented here. Implementations of <code>Predicate</code>
 * should consider to subclass this class in order to use these implementations.
 * The idea is to allow also instances of different classes to be equal, since these instances
 * could for example represent the same predicate using different data sources.<p>
 * Note that properties of instances of this class are inmutable. This can be used to cache the hash!
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.0
 */
public abstract class AbstractPredicate implements Predicate {

    protected int cachedHashCode = -1;

    /**
     * Indicates whether the two objects are equal. This method invokes
     * comparing the names and checking the structure types for compatibility (and not for
     * identity!).
     * @param obj the object to compare this predicate with
     * @return true if the objects are equal, false otherwise
     */
    public boolean equals(Object obj) {
        if(obj instanceof org.mandarax.kernel.Predicate) {
            if(toString ().equals ("equalsNot")
                    && obj.toString ().equals ("equalsNot")) {
                System.out.println ("stop here");
            }

            Predicate p      = (Predicate) obj;
            Class[]   c1     = p.getStructure ();
            Class[]   c2     = getStructure ();
            boolean   result = p.getName ().equals (getName ());

            result = result && (c1.length == c2.length);

            for(int i = 0; i < c1.length; i++) {
                result = result
                         && ((c1[i] == c2[i])
                             || (c1[i].isAssignableFrom (c2[i]))
                             || (c2[i].isAssignableFrom (c1[i])));
            }

            // test method 
            // if (result) System.out.println(this.toString() + " = " + obj.toString()); 
            if(toString ().equals ("equalsNot")
                    && obj.toString ().equals ("equalsNot") && !result) {
                System.out.println (
                    "------------------------------------------");

                if( !result) {
                    System.out.println (this.toString () + " != "
                                        + obj.toString ());
                }

                for(int i1 = 0; i1 < c1.length; i1++) {
                    System.out.println (c1[i1].toString ());
                }

                System.out.println ("");

                for(int i2 = 0; i2 < c2.length; i2++) {
                    System.out.println (c2[i2].toString ());
                }
            }

            return result;
        }

        return false;
    }

    /**
     * Get the name of the predicate.
     * @return the name of the predicate
     */
    public abstract String getName();

    /**
     * Get the type structure of the object, e.g. the types of terms.
     * @return the structure of the predicate
     */
    public abstract Class[] getStructure();

    /**
     * Get the hash code of the object.
     * @return the hash code value
     */
    public int hashCode() {
        if(cachedHashCode == -1) {
            if(getName () == null) {
                cachedHashCode = 0;
            } else {
                cachedHashCode = getName ().hashCode ()
                                 + getStructure ().length;
            }
        }

        return cachedHashCode;
    }
}
