/*
 * 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 org.mandarax.kernel.Function;
import org.mandarax.kernel.Session;
import org.mandarax.kernel.Term;

/**
 * Function that changes the type of a term. 
 * Supports casts to a superclass (or interface) and a subclass (or interface). 
 * If types (classes) are incompatible, the setters will throw an exception. 
 * To change types, they must first be reset to null. 
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 3.3
 */

public class CastFunction implements Function {
	private Class targetType = null;
	private Class[] sourceTypes = new Class[1];
	
	/**
	 * Return the return type. 
	 * @see org.mandarax.kernel.Function#getReturnType()
	 */
	public Class getReturnType() {
		return targetType;
	}
	/**
	 * Return the nsame of the function.
	 * @return a name
	 * @see org.mandarax.kernel.Constructor#getName()
	 */
	public String getName() {
		return new StringBuffer()
		.append("cast [")
		.append(sourceTypes[0])
		.append(" -> ")
		.append(targetType)
		.append("]")
		.toString();
	}
	/**
	 * Return the structure of the function. 
	 * @return an array of types (classes) 
	 * @see org.mandarax.kernel.Constructor#getStructure()
	 */
	public Class[] getStructure() {
		return sourceTypes;
	}
	/**
	 * Invoke the function.
	 * @param parameter the parameter
	 * @param session the session 
	 * @see org.mandarax.kernel.Constructor#perform(org.mandarax.kernel.Term[], org.mandarax.kernel.Session)
	 */
	public Object perform(Term[] parameter, Session session) throws IllegalArgumentException, UnsupportedOperationException {
		return parameter[0].resolve(session); // "dynamic" casting does not make sense, the function only changes the declared type
	}
	/**
	 * Indicates whether this function is executable.
	 * @return a boolean
	 */
	public boolean isExecutable() {
		return true;
	}
	
		
	/**
	 * Get the source type.
	 * @return Returns the sourceType.
	 */
	public Class getSourceType() {
		return sourceTypes[0];
	}
	/**
	 * Set the source type.
	 * @param sourceType The sourceType to set.
	 */
	public void setSourceType(Class sourceType) {
		check(sourceType,targetType);
		this.sourceTypes[0] = sourceType;
	}
	/**
	 * Returns the target type.
	 * @return Returns the targetType.
	 */
	public Class getTargetType() {
		return targetType;
	}
	/**
	 * Set the target type.
	 * @param targetType The targetType to set.
	 */
	public void setTargetType(Class targetType) {
		check(sourceTypes[0],targetType);
		this.targetType = targetType;
	}
	/**
	 * Check the source and target type for compatibility.
	 * @param src the source type
	 * @param target the target type
	 * @throws an IllegalArgumentException if cast is not possible 
	 */
	private void check(Class src,Class target) throws IllegalArgumentException {
		if (src==null || target==null) return;
		if (src.isAssignableFrom(target) || target.isAssignableFrom(src)) return;
		else throw new IllegalArgumentException("Cannot cast between types " + src + " and " + target);
	}

}
