/*******************************************************************************
 * Copyright (c) 2001, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jem.internal.proxy.initParser;
/*


 */


import java.util.HashMap;

public class Cast extends Expression {
	protected static final int BYTE_TYPE = 0;
	protected static final int SHORT_TYPE = 1;
	protected static final int INT_TYPE = 2;
	protected static final int LONG_TYPE = 3;
	protected static final int FLOAT_TYPE = 4;
	protected static final int DOUBLE_TYPE = 5;
	
	protected static final HashMap sTypeLookup;
	static {
		sTypeLookup = new HashMap(6);
		sTypeLookup.put(Byte.TYPE, new Integer(BYTE_TYPE));
		sTypeLookup.put(Short.TYPE, new Integer(SHORT_TYPE));
		sTypeLookup.put(Integer.TYPE, new Integer(INT_TYPE));
		sTypeLookup.put(Long.TYPE, new Integer(LONG_TYPE));
		sTypeLookup.put(Float.TYPE, new Integer(FLOAT_TYPE));
		sTypeLookup.put(Double.TYPE, new Integer(DOUBLE_TYPE));
	}
		
	protected Static fCastType;
	protected boolean fIsClosed = false;
	
	public Cast(Static castType, ClassLoader aClassLoader) {
		fCastType = castType;
		fClassLoader = aClassLoader;
	}
	
	public boolean isPrimitive() throws Exception {
		return getTypeClass().isPrimitive();
	}
	
	/**
	 * The type of a Cast expression is the type of the cast type.
	 */
	public Class getTypeClass() throws Exception {
		return fCastType.getTypeClass();
	}
	
	
	public boolean isComplete() {
		return currentExpression != null && currentExpression.isComplete();
	}
	
	/**
	 * If the type is primitive, then we need to
	 * convert the value, otherwise just pass
	 * the value on.
	 *
	 * This is important for primitives because if your
	 * entire initialization string was simply "(short) 3"
	 * then a Short value must be returned so that the
	 * correct kind of primitive proxy is created.
	 */
	public Object evaluate() throws Exception {
		if (getTypeClass() == currentExpression.getTypeClass())
			return currentExpression.evaluate();	// They're the same, so just return it.
		if (getTypeClass().isPrimitive()) {
			// Can only cast a primitive to a primitive, except null can't be cast to a primitive.
			if (!currentExpression.getTypeClass().isPrimitive() || currentExpression.getTypeClass() == Void.TYPE)
				throw new EvaluationException(new ClassCastException(currentExpression.getTypeClass().getName()));			
			// boolean only allows boolean cast.
			if (fCastType.getTypeClass() == Boolean.TYPE || currentExpression.getTypeClass() == Boolean.TYPE)
				throw new EvaluationException(new ClassCastException(currentExpression.getTypeClass().getName()));
			if (fCastType.getTypeClass() == Character.TYPE) {
				// So we have either a Character or a number as the value. Cast that.				
				return new Character((char) ((Number) currentExpression.evaluate()).intValue());
			} else {
				Number value = null;
				// See if the value is character or a number.
				if (currentExpression.getTypeClass() == Character.TYPE)
					value = new Integer(((Character) currentExpression.evaluate()).charValue());
				else
					value = (Number) currentExpression.evaluate();
				switch (((Integer) sTypeLookup.get(fCastType.getTypeClass())).intValue()) {
					case BYTE_TYPE:
						return new Byte(value.byteValue());
					case SHORT_TYPE:
						return new Short(value.shortValue());
					case INT_TYPE:
						return new Integer(value.intValue());
					case LONG_TYPE:
						return new Long(value.longValue());
					case FLOAT_TYPE:
						return new Float(value.floatValue());
					case DOUBLE_TYPE:
						return new Double(value.doubleValue());
					default:
						return null;	// Shouldn't occur. Just satisifies compiler.
				}
			}
			
		} else
			return currentExpression.evaluate();
	}
	/**
	 * A cast expression always pushes onto the cast value
	 */
	public Expression push(char[] token , char tokenDelimiter) {
		// If we don't have a class yet then we are within the statement to try and deterine the type being cast to
		if(fCastType.getPrimitiveTypeClass() == null && !fIsClosed){
			fCastType.push(token,tokenDelimiter);
			// If the type was completed and we have a ) then close us
			if(fCastType.getPrimitiveTypeClass() != null && tokenDelimiter == DelimiterCloseParen){
				fIsClosed = true;
			}
			return this;			
		}
		
		if (!fIsClosed) {
			// The cast is not closed, but we have a static class.  This class must therefore keep processing the tokens as it could be a method or message send
			Expression result =  fCastType.push(token,tokenDelimiter);
			result.parenthesisLevel++;
			return result; 
		}
		
		// If we have no expression push onto the cast value		
		if(currentExpression == null){
			currentExpression = new Statement(fClassLoader);
			currentExpression = currentExpression.push(token,tokenDelimiter);
			return this;
		} 
		Expression result = currentExpression.push(token, tokenDelimiter);
		// If the result if a push then push the stack
		if ( result.isComplete() ) {
			popExpressionStack();
		} else if ( result != currentExpression ) {
			pushExpressionStack(result);
		}		
		return this;
	}
	public String toString(){
		StringBuffer buffer = new StringBuffer();
		buffer.append("Cast("); //$NON-NLS-1$
		if (fCastType != null){
			buffer.append(fCastType.toString());
		} else {
			buffer.append("???"); //$NON-NLS-1$
		}
		buffer.append(") "); //$NON-NLS-1$
		if ( currentExpression != null){
			buffer.append(currentExpression.toString());
		} else {
			buffer.append("???");			 //$NON-NLS-1$
		}
		return buffer.toString();
	}
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.initParser.Expression#getTypeClassName()
	 */
	protected String getTypeClassName() {
		return fCastType.getTypeClassName();
	}

}
