/*
 * 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.meta;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;

import org.mandarax.kernel.Function;

/**
 * A JFunction is a function defined by a java method. E.g., if <code>'Max'</code>
 * is a constant term (Max itself being an instance of a class <code>Person</code>),
 * and <code>father()</code> is a method defined in this class returning another
 * instance, then we can build a <code>JFunction</code> based on the method
 * <code>father()</code> with a well defined semantic: the term <code>father('Max')</code>
 * can be simplified to a constant term that wrappes the result of invoking
 * <code>father()</code> with the parameter array <code>{Max}</code> (and that is me, Jens :-)).
 * <br>
 * In version 3.2, session support has been added. The last parameter (found in the wrapoped method) 
 * is treated separately if its type is Session (or a subclass of session). Then this parameter is used
 * to pass a session reference at query time to the function or predicate!
 * @see org.mandarax.kernel.Session
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.0
 */
public final class JFunction extends JConstructor implements Function {

    /**
     * Constructor.
     * @param aMethod the method used
     */
    public JFunction() {
        super ();
    }
    
    /**
     * Constructor.
     * @param aMethod the method used
     */
    public JFunction(Method aMethod) {
        super (aMethod);
    }

    /**
     * Constructor.
     * @param aMethod the method used
     * @param aName the name of the object
     */
    public JFunction(Method aMethod, String aName) {
        super (aMethod, aName);
    }

    /**
     * Indicates whether this function equals the object.
     * @param obj the object to compare this object with
     * @return true if the objects are equal, false otherwise
     */
    public boolean equals(Object obj) {
        if(obj instanceof org.mandarax.kernel.Function) {
            Function f      = (Function) obj;
            Class[]  c1     = f.getStructure ();
            Class[]  c2     = getStructure ();
            boolean  result = f.getName ().equals (getName ());

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

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

            return result;
        }

        return false;
    }

    /**
     * Get the return type.
     * @return the return type
     */
    public Class getReturnType() {
        return convertType (method.getReturnType ());
    }

    /**
     * Read the object from an object input stream.
     * @param in an input stream
     */
    private void readObject(ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        in.defaultReadObject ();

        String  methodName     = (String) in.readObject ();
        Class   declaringClass = (Class) in.readObject ();
        Class[] parTypes       = (Class[]) in.readObject ();

        try {
            Method m = declaringClass.getMethod (methodName, parTypes);

            method = m;
        } catch(NoSuchMethodException x) {
            throw new IOException ("Cannot find method " + name);
        }
    }

    /**
     * Write the object to an object output stream.
     * @param out an output stream
     */
    private void writeObject(ObjectOutputStream out)
            throws IOException, ClassNotFoundException {
        out.defaultWriteObject ();
        out.writeObject (method.getName ());
        out.writeObject (method.getDeclaringClass ());
        out.writeObject (method.getParameterTypes ());
    }
}
