// $Id: Result.java,v 1.17 1998/09/29 17:25:58 oliva Exp $

/* Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 *
 * This file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package BR.unicamp.Guarana;

/** A Result object is used as the return value of a request to a
    MetaObject to Handle an Operation or another Result.  It may
    contain a Result to be used as the result of the Operation.  If no
    Result is given, it may provide an Operation to be performed
    instead of the one it was requested to handle (unless it was asked
    to handle a Result already).  Even if such an Operation is not
    provided, a request to inspect or even to modify the Result of the
    Operation, after it is performed, may be included in the Result
    (again, only for request to handle Operations).  A null Result
    should be interpreted as a noResult.

    <p>When a MetaObject is asked to handle a Result, and it returns
    another Result that does not contain a returned or thrown value,
    the Result it returns is simply discarded.

    @see MetaObject#handle

    @author Alexandre Oliva
    @version $Revision: 1.17 $  */
public final class Result {
    static {
        System.loadLibrary("guarana");
    }

    /** This Result mode indicates that a MetaObject does not care about
	the Result of an Operation.

	@see Result#noResult */
    public static final int noResultMode = 0;

    /** This Result mode indicates that a MetaObject intends to observe
	but not to modify the Result of an Operation.

	@see Result#inspectResult */
    public static final int inspectResultMode = 1;

    /** This Result mode indicates that a MetaObject intends to inspect
	the Result of an Operation and possibly replace it with another
	Result.

	@see Result#modifyResult */
    public static final int modifyResultMode = 3;

    /** This variable is to be used as a mask to select either
	noResultMode, inspectResultMode or modifyResultMode.  */
    public static final int resultRequestMask
	= noResultMode | inspectResultMode | modifyResultMode;

    /** This Result mode indicates that the Result contains a value that
	is to be considered the return value of the Operation.  */
    public static final int returnedValue = 4;

    /** This Result mode indicates that the Result contains a value that
	is to be considered as thrown from the execution of the
	Operation.  */
    public static final int thrownValue = 8;

    /** This variable is to be used as a mask to select either
	returnedValue or thrownValue.  */
    public static final int resultValueMask = returnedValue | thrownValue;

    /** This Result indicates that a MetaObject does not care about the
	Result of an Operation.  Composers are allowed to handle
	noResult as inspectResult or modifyResult.  A null Result
	should be interpreted as a noResult request.  */
    public static final Result noResult = operation(null, noResultMode);

    /** This Result indicates that a MetaObject intends to observe but
	not to modify the Result of an Operation.  Composers are allowed
	to handle inspectResult as modifyResult.  */
    public static final Result inspectResult = operation(null, inspectResultMode);

    /** This Result indicates that a MetaObject intends to inspect the
	Result of an Operation and possibly replace it with another
	Result.  */
    public static final Result modifyResult = operation(null, modifyResultMode);

    /** Cannot instantiate Results in Java.  */
    private Result() {}
  
    /** Creates an Operation replacement Result, unless the given
	Operation is null.

	@param operation the replacement Operation.

	@param mode either noResultMode, inspectResultMode or
	modifyResultMode.

	@return a new Operation replacement Result, or either noResult,
	inspectResult or modifyResult, if the given Operation is null.  */
    public static native Result operation(final Operation operation,
					  final int nmode);

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.
	It must be wrapped into a suitable wrapper class, if it is
	supposed to be a primitive type.  Helper methods are available.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnObject(final Object returned,
					     final Operation operation)
	throws IllegalArgumentException;
  
    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnBoolean(final boolean returned,
					      final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.
      
	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnByte(final byte returned,
					   final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnShort(final short returned,
					    final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnInt(final int returned,
					  final Operation operation)
	throws IllegalArgumentException;

    /** Wraps the first argument into the appropriate wrapper class and
	calls returnObject.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnLong(final long returned,
					   final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.  This only exists because
	Kaffe won't handle float arguments correctly.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    private static native Result returnFloat(final double returned,
					     final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static Result returnFloat(final float returned,
				     final Operation operation)
	throws IllegalArgumentException {
	return returnFloat((double)returned, operation);
    }

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnDouble(final double returned,
					     final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnChar(final char returned,
					   final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param returned contains the returned value of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the returned value.

	@exception IllegalArgumentException if the value does not have
	the expected type.  */
    public static native Result returnVoid(final Operation operation)
	throws IllegalArgumentException;

    /** Builds the Result of the Operation.

	@param thrown is the Throwable to be considered the exceptional
	Result of the Operation.

	@param operation is the Operation this Result refers to.

	@return a Result containing the Throwable value.

	@exception IllegalArgumentException if the given Throwable is
	neither a Error nor a RuntimeException, nor does it match the
	exception declaration of the given Operation, if it is a
	constructor or method invocation.  */
    public static Result throwObject(final Throwable thrown,
				     final Operation operation)
	throws IllegalArgumentException {
	return throwObject(thrown, operation, true);
    }

    /** @deprecated

	Builds an exceptional result without checking for type
	compatibility.  This is only to be used within the
	implementation of Guarana.perform, so that it can reify thrown
	results even if the Throwable type is not compatible with the
	declared throws of a method or constructor.  It will be
	removed as soon as Guarana.deliver is rewritten in JNI, so
	that it can reify the throwable itself.

	@param thrown is the Throwable to be considered the exceptional
	Result of the Operation.

	@param operation is the Operation this Result refers to.

	@param check determines whether the exception type should be
	validated or not.

	@return a Result containing the Throwable value.

	@exception IllegalArgumentException if check is true and the
	given Throwable is neither a Error nor a RuntimeException, nor
	does it match the exception declaration of the given
	Operation, if it is a constructor or method invocation.  */
    static native Result throwObject(final Throwable thrown,
				     final Operation operation,
				     final boolean check)
	throws IllegalArgumentException;

    /** This method obtains the mode of the Result.  It may be one of
	noResultMode, inspectResultMode, modifyResultMode,
	returnedValue or thrownValue.

	@return the mode of the Result.  */
    public native int getMode();

    /** Obtains the Operation the Result refers to.  If this Result is a
	replacement Operation, the replacement Operation is returned.
	If it contains a returned or thrown value, the Operation it
	refers to is returned.  Otherwise, it returns null.

	@return the Operation itself. */
    public native Operation getOperation();

    /** Checks whether the Result was an Exception.  This includes both
	base-level and meta-level exceptions.

	@return true if and only if the Operation resulted an Exception,
	or its handling caused an Exception in the meta-level.  */
    public native boolean isException();

    /** Obtains the returned or thrown Object, suitably wrapped if it is
	a primitive type.

	@return the Object thrown or returned by the Operation, or null
	if this Result does not contain such a value or it was of void
	type.  */
    public native Object getObjectValue();

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native boolean getBooleanValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native byte getByteValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native short getShortValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native int getIntValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native long getLongValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native char getCharValue()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public float getFloatValue()
	throws IllegalArgumentException {
	return (float)getFloatValueAsDouble();
    }

    /** Obtains the returned value from the Operation.  This only
	exists because Kaffe won't handle float return values
	correctly.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    private native double getFloatValueAsDouble()
	throws IllegalArgumentException;

    /** Obtains the returned value from the Operation.

	@return the result of the Operation.

	@exception IllegalArgumentException if the result type is not
	the requested one, or no returned value is available.  */
    public native double getDoubleValue()
	throws IllegalArgumentException;

    /** Returns a String representation of the Result.  */
    public String toString() {
	final StringBuffer op = new StringBuffer();
	switch (getMode()) {
	case returnedValue: {
	    op.append("return ");
	    final Object value = getObjectValue();
	    if (getOperation().getType().isPrimitive())
		op.append(value);
	    else
		op.append(Guarana.toString(value));
	    break;
	}
	case thrownValue: {
	    op.append("throw ");
	    op.append(Guarana.toString(getObjectValue()));
	    break;
	}
	case noResultMode:
	    op.append("no result request");
	    break;
	case inspectResultMode:
	    op.append("inspect result request");
	    break;
	case modifyResultMode:
	    op.append("modify result request");
	    break;
	default:
	    throw new IllegalArgumentException("invalid result mode");
	}
	return op.toString();
    }
}
