/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.ast;

import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
import net.sourceforge.phpdt.internal.compiler.CompilationResult;
import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
import net.sourceforge.phpdt.internal.compiler.problem.AbortType;

public class AnonymousLocalTypeDeclaration extends LocalTypeDeclaration {

	public static final char[] ANONYMOUS_EMPTY_NAME = new char[] {};

	public QualifiedAllocationExpression allocation;

	public AnonymousLocalTypeDeclaration(CompilationResult compilationResult) {
		super(compilationResult);
		modifiers = AccDefault;
		name = ANONYMOUS_EMPTY_NAME;
	}

	// use a default name in order to th name lookup
	// to operate just like a regular type (which has a name)
	// without checking systematically if the naem is null ....
	public MethodBinding createsInternalConstructorWithBinding(
			MethodBinding inheritedConstructorBinding) {

		// Add to method'set, the default constuctor that just recall the
		// super constructor with the same arguments
		String baseName = "$anonymous"; //$NON-NLS-1$
		TypeBinding[] argumentTypes = inheritedConstructorBinding.parameters;
		int argumentsLength = argumentTypes.length;
		// the constructor
		ConstructorDeclaration cd = new ConstructorDeclaration(
				this.compilationResult);
		cd.selector = new char[] { 'x' }; // no maining
		cd.sourceStart = sourceStart;
		cd.sourceEnd = sourceEnd;
		cd.modifiers = modifiers & AccVisibilityMASK;
		cd.isDefaultConstructor = true;

		if (argumentsLength > 0) {
			Argument[] arguments = (cd.arguments = new Argument[argumentsLength]);
			for (int i = argumentsLength; --i >= 0;) {
				arguments[i] = new Argument((baseName + i).toCharArray(), 0L,
						null /* type ref */, AccDefault);
			}
		}

		// the super call inside the constructor
		cd.constructorCall = SuperReference.implicitSuperConstructorCall();
		cd.constructorCall.sourceStart = sourceStart;
		cd.constructorCall.sourceEnd = sourceEnd;

		if (argumentsLength > 0) {
			Expression[] args;
			args = cd.constructorCall.arguments = new Expression[argumentsLength];
			for (int i = argumentsLength; --i >= 0;) {
				args[i] = new SingleNameReference((baseName + i).toCharArray(),
						0L);
			}
		}

		// adding the constructor in the methods list
		if (methods == null) {
			methods = new AbstractMethodDeclaration[] { cd };
		} else {
			AbstractMethodDeclaration[] newMethods;
			System
					.arraycopy(
							methods,
							0,
							newMethods = new AbstractMethodDeclaration[methods.length + 1],
							1, methods.length);
			newMethods[0] = cd;
			methods = newMethods;
		}

		// ============BINDING UPDATE==========================
		cd.binding = new MethodBinding(cd.modifiers, // methodDeclaration
				argumentsLength == 0 ? NoParameters : argumentTypes, // arguments
																		// bindings
				inheritedConstructorBinding.thrownExceptions, // exceptions
				binding); // declaringClass

		cd.scope = new MethodScope(scope, cd, true);
		cd.bindArguments();
		cd.constructorCall.resolve(cd.scope);

		if (binding.methods == null) {
			binding.methods = new MethodBinding[] { cd.binding };
		} else {
			MethodBinding[] newMethods;
			System.arraycopy(binding.methods, 0,
					newMethods = new MethodBinding[binding.methods.length + 1],
					1, binding.methods.length);
			newMethods[0] = cd.binding;
			binding.methods = newMethods;
		}
		// ===================================================

		return cd.binding;

	}

	public void resolve(BlockScope scope) {

		if (binding != null) {
			// remember local types binding for innerclass emulation propagation
			scope.referenceCompilationUnit().record((LocalTypeBinding) binding);
		}
		// scope and binding are provided in updateBindingSuperclass
		resolve();
		updateMaxFieldCount();
	}

	public String toString(int tab) {

		return toStringBody(tab);
	}

	/**
	 * Iteration for a local anonymous innertype
	 * 
	 */
	public void traverse(ASTVisitor visitor, BlockScope blockScope) {

		if (ignoreFurtherInvestigation)
			return;
		try {
			if (visitor.visit(this, blockScope)) {

				int fieldsLength;
				int methodsLength;
				int memberTypesLength;

				// <superclass> is bound to the actual type from the allocation
				// expression
				// therefore it has already been iterated at this point.

				if (memberTypes != null) {
					memberTypesLength = memberTypes.length;
					for (int i = 0; i < memberTypesLength; i++)
						memberTypes[i].traverse(visitor, scope);
				}
				if (fields != null) {
					fieldsLength = fields.length;
					for (int i = 0; i < fieldsLength; i++) {
						FieldDeclaration field;
						if ((field = fields[i]).isStatic()) {
							// local type cannot have static fields
						} else {
							field.traverse(visitor, initializerScope);
						}
					}
				}
				if (methods != null) {
					methodsLength = methods.length;
					for (int i = 0; i < methodsLength; i++)
						methods[i].traverse(visitor, scope);
				}
			}
			visitor.endVisit(this, blockScope);
		} catch (AbortType e) {
		}
	}
}
