/*****************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one                *
 * or more contributor license agreements.  See the NOTICE file              *
 * distributed with this work for additional information                     *
 * regarding copyright ownership.  The ASF licenses this file                *
 * to you under the Apache License, Version 2.0 (the                         *
 * "License"); you may not use this file except in compliance                *
 * with the License.  You may obtain a copy of the License at                *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 * Unless required by applicable law or agreed to in writing,                *
 * software distributed under the License is distributed on an               *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY                    *
 * KIND, either express or implied.  See the License for the                 *
 * specific language governing permissions and limitations                   *
 * under the License.                                                        *
 *                                                                           *
 *                                                                           *
 * This file is part of the BeanShell Java Scripting distribution.           *
 * Documentation and updates may be found at http://www.beanshell.org/       *
 * Patrick Niemeyer (pat@pat.net)                                            *
 * Author of Learning Java, O'Reilly & Associates                            *
 *                                                                           *
 *****************************************************************************/



package bsh;

import java.lang.reflect.Array;

/**
	The name of this class is somewhat misleading.  This covers both the case
	where there is an array initializer and 
*/
class BSHArrayDimensions extends SimpleNode
{
	public Class baseType;
    public int numDefinedDims;
    public int numUndefinedDims;
	/** 
		The Length in each defined dimension.  This value set by the eval() 
		Since the values can come from Expressions we should be re-eval()d each
		time.
	*/
	public int [] definedDimensions;  

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

    public void addDefinedDimension() { numDefinedDims++; }
    public void addUndefinedDimension() { numUndefinedDims++; }

    public Object eval( 
			Class type, CallStack callstack, Interpreter interpreter ) 
		throws EvalError 
	{
		if ( Interpreter.DEBUG ) Interpreter.debug("array base type = "+type);
		baseType = type;
		return eval( callstack, interpreter );
	}

	/**
		Evaluate the structure of the array in one of two ways:

			a) an initializer exists, evaluate it and return
			the fully constructed array object, also record the dimensions
			of that array
			
			b) evaluate and record the lengths in each dimension and 
			return void.

		The structure of the array dims is maintained in dimensions.
	*/
    public Object eval( CallStack callstack, Interpreter interpreter )  
		throws EvalError
    {
		SimpleNode child = (SimpleNode)jjtGetChild(0);

		/*
			Child is array initializer.  Evaluate it and fill in the 
			dimensions it returns.  Initialized arrays are always fully defined
			(no undefined dimensions to worry about).  
			The syntax uses the undefinedDimension count.
			e.g. int [][] { 1, 2 };
		*/
		if (child instanceof BSHArrayInitializer)
		{
			if ( baseType == null )
				throw new EvalError( 
					"Internal Array Eval err:  unknown base type", 
					this, callstack );

			Object initValue = ((BSHArrayInitializer)child).eval(
				baseType, numUndefinedDims, callstack, interpreter);

			Class arrayClass = initValue.getClass();
			int actualDimensions = Reflect.getArrayDimensions(arrayClass);
			definedDimensions = new int[ actualDimensions ];

			// Compare with number of dimensions actually created with the
			// number specified (syntax uses the undefined ones here)
			if ( definedDimensions.length != numUndefinedDims )
				throw new EvalError(
				"Incompatible initializer. Allocation calls for a " + 
				numUndefinedDims+ " dimensional array, but initializer is a " +
					actualDimensions + " dimensional array", this, callstack );

			// fill in definedDimensions [] lengths
			Object arraySlice = initValue;
			for ( int i = 0; i < definedDimensions.length; i++ ) {
				definedDimensions[i] = Array.getLength( arraySlice );
				if ( definedDimensions[i] > 0 )
					arraySlice = Array.get(arraySlice, 0);
			}

			return initValue;
		}
		else 
		// Evaluate the defined dimensions of the array
		{
			definedDimensions = new int[ numDefinedDims ];

			for(int i = 0; i < numDefinedDims; i++)
			{
				try {
					Object length = ((SimpleNode)jjtGetChild(i)).eval(
						callstack, interpreter);
					definedDimensions[i] = ((Primitive)length).intValue();
				}
				catch(Exception e)
				{
					throw new EvalError(
						"Array index: " + i + 
						" does not evaluate to an integer", this, callstack );
				}
			}
		}

        return Primitive.VOID;
    }
}
