/*****************************************************************************
 *                                                                           *
 *  This file is part of the BeanShell Java Scripting distribution.          *
 *  Documentation and updates may be found at http://www.beanshell.org/      *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  Patrick Niemeyer (pat@pat.net)                                           *
 *  Author of Learning Java, O'Reilly & Associates                           *
 *  http://www.pat.net/~pat/                                                 *
 *                                                                           *
 *****************************************************************************/


package org.gjt.sp.jedit.bsh;

class BSHAssignment extends SimpleNode implements ParserConstants
{
    public int operator;

    BSHAssignment(int id) { super(id); }

    public Object eval(
		CallStack callstack, Interpreter interpreter) 
		throws EvalError
    {
        BSHPrimaryExpression lhsNode = 
			(BSHPrimaryExpression)jjtGetChild(0);

		if ( lhsNode == null )
			throw new InterpreterError( "Error, null LHSnode" );

		boolean strictJava = interpreter.getStrictJava();
        LHS lhs = lhsNode.toLHS( callstack, interpreter);
        if ( lhs == null )
            throw new InterpreterError( "Error, null LHS" );

		// For operator-assign operations save the lhs value before evaluating
		// the rhs.  This is correct Java behavior for postfix operations
		// e.g. i=1; i+=i++; // should be 2 not 3
		Object lhsValue = null;
		if ( operator != ASSIGN ) // assign doesn't need the pre-value
			try {
				lhsValue = lhs.getValue();
			} catch ( UtilEvalError e ) {
				throw e.toEvalError( this, callstack );
			}

        SimpleNode rhsNode = (SimpleNode)jjtGetChild(1);

        Object rhs;
		
		// implement "blocks" foo = { };
		// if ( rhsNode instanceof BSHBlock )
		//    rsh =
		// else
        rhs = rhsNode.eval(callstack, interpreter);

        if ( rhs == Primitive.VOID )
            throw new EvalError("Void assignment.", this, callstack );

		try {
			switch(operator)
			{
				case ASSIGN:
					return lhs.assign( rhs, strictJava );

				case PLUSASSIGN:
					return lhs.assign( 
						operation(lhsValue, rhs, PLUS), strictJava );

	            case MINUSASSIGN:
					return lhs.assign( 
						operation(lhsValue, rhs, MINUS), strictJava );

				case STARASSIGN:
					return lhs.assign( 
						operation(lhsValue, rhs, STAR), strictJava );

	            case SLASHASSIGN:
					return lhs.assign( 
						operation(lhsValue, rhs, SLASH), strictJava );

	            case ANDASSIGN:
				case ANDASSIGNX:
					return lhs.assign( 
						operation(lhsValue, rhs, BIT_AND), strictJava );

	            case ORASSIGN:
	            case ORASSIGNX:
	                return lhs.assign( 
						operation(lhsValue, rhs, BIT_OR), strictJava );

	            case XORASSIGN:
	                return lhs.assign( 
						operation(lhsValue, rhs, XOR), strictJava );

	            case MODASSIGN:
	                return lhs.assign( 
						operation(lhsValue, rhs, MOD), strictJava );

	            case LSHIFTASSIGN:
	            case LSHIFTASSIGNX:
	                return lhs.assign( 
						operation(lhsValue, rhs, LSHIFT), strictJava );

	            case RSIGNEDSHIFTASSIGN:
	            case RSIGNEDSHIFTASSIGNX:
	                return lhs.assign( 
					operation(lhsValue, rhs, RSIGNEDSHIFT ), strictJava );

	            case RUNSIGNEDSHIFTASSIGN:
	            case RUNSIGNEDSHIFTASSIGNX:
	                return lhs.assign( 
						operation(lhsValue, rhs, RUNSIGNEDSHIFT), 
						strictJava );

				default:
					throw new InterpreterError(
						"unimplemented operator in assignment BSH");
			}
		} catch ( UtilEvalError e ) {
			throw e.toEvalError( this, callstack );
		}
    }

    private Object operation( Object lhs, Object rhs, int kind ) 
		throws UtilEvalError
    {
		/*
			Implement String += value;
			According to the JLS, value may be anything.
			In BeanShell, we'll disallow VOID (undefined) values.
			(or should we map them to the empty string?)
		*/
		if ( lhs instanceof String && rhs != Primitive.VOID ) {
			if ( kind != PLUS )
				throw new UtilEvalError(
					"Use of non + operator with String LHS" );

			return (String)lhs + rhs;
		}

        if ( lhs instanceof Primitive || rhs instanceof Primitive )
            if(lhs == Primitive.VOID || rhs == Primitive.VOID)
                throw new UtilEvalError(
					"Illegal use of undefined object or 'void' literal" );
            else if ( lhs == Primitive.NULL || rhs == Primitive.NULL )
                throw new UtilEvalError(
					"Illegal use of null object or 'null' literal" );


        if( (lhs instanceof Boolean || lhs instanceof Character ||
             lhs instanceof Number || lhs instanceof Primitive) &&
            (rhs instanceof Boolean || rhs instanceof Character ||
             rhs instanceof Number || rhs instanceof Primitive) )
        {
            return Primitive.binaryOperation(lhs, rhs, kind);
        }

        throw new UtilEvalError("Non primitive value in operator: " +
            lhs.getClass() + " " + tokenImage[kind] + " " + rhs.getClass() );
    }
}
