/***********************************************************************************************************************************
 * 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.core.compiler.CharOperation;
import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
import net.sourceforge.phpdt.internal.compiler.CompilationResult;
import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
import net.sourceforge.phpdt.internal.compiler.flow.InitializationFlowContext;
import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
import net.sourceforge.phpdt.internal.compiler.lookup.ClassScope;
import net.sourceforge.phpdt.internal.compiler.lookup.CompilationUnitScope;
import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.MemberTypeBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
import net.sourceforge.phpdt.internal.compiler.lookup.NestedTypeBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding;
import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
import net.sourceforge.phpdt.internal.compiler.parser.Parser;
import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
import net.sourceforge.phpdt.internal.compiler.problem.AbortMethod;
import net.sourceforge.phpdt.internal.compiler.problem.AbortType;
import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;

public class TypeDeclaration extends Statement implements ProblemSeverities,
		ReferenceContext {

	public int modifiers;

	public int modifiersSourceStart;

	public char[] name;

	public TypeReference superclass;

	public TypeReference[] superInterfaces;

	public FieldDeclaration[] fields;

	public AbstractMethodDeclaration[] methods;

	public TypeDeclaration[] memberTypes;

	public SourceTypeBinding binding;

	public ClassScope scope;

	public MethodScope initializerScope;

	public MethodScope staticInitializerScope;

	public boolean ignoreFurtherInvestigation = false;

	public int maxFieldCount;

	public int declarationSourceStart;

	public int declarationSourceEnd;

	public int bodyStart;

	public int bodyEnd; // doesn't include the trailing comment if any.

	protected boolean hasBeenGenerated = false;

	public CompilationResult compilationResult;

	private MethodDeclaration[] missingAbstractMethods;

	public TypeDeclaration(CompilationResult compilationResult) {
		this.compilationResult = compilationResult;
	}

	/*
	 * We cause the compilation task to abort to a given extent.
	 */
	public void abort(int abortLevel) {

		if (scope == null) {
			throw new AbortCompilation(); // cannot do better
		}

		CompilationResult compilationResult = scope.referenceCompilationUnit().compilationResult;

		switch (abortLevel) {
		case AbortCompilation:
			throw new AbortCompilation(compilationResult);
		case AbortCompilationUnit:
			throw new AbortCompilationUnit(compilationResult);
		case AbortMethod:
			throw new AbortMethod(compilationResult);
		default:
			throw new AbortType(compilationResult);
		}
	}

	/**
	 * This method is responsible for adding a <clinit>method declaration to the
	 * type method collections. Note that this implementation is inserting it in
	 * first place (as VAJ or javac), and that this impacts the behavior of the
	 * method ConstantPool.resetForClinit(int. int), in so far as the latter
	 * will have to reset the constant pool state accordingly (if it was added
	 * first, it does not need to preserve some of the method specific cached
	 * entries since this will be the first method). inserts the clinit method
	 * declaration in the first position.
	 * 
	 * @see net.sourceforge.phpdt.internal.compiler.codegen.ConstantPool#resetForClinit(int,
	 *      int)
	 */
	public final void addClinit() {

		// see comment on needClassInitMethod
		if (needClassInitMethod()) {
			int length;
			AbstractMethodDeclaration[] methods;
			if ((methods = this.methods) == null) {
				length = 0;
				methods = new AbstractMethodDeclaration[1];
			} else {
				length = methods.length;
				System.arraycopy(methods, 0,
						(methods = new AbstractMethodDeclaration[length + 1]),
						1, length);
			}
			Clinit clinit = new Clinit(this.compilationResult);
			methods[0] = clinit;
			// clinit is added in first location, so as to minimize the use of
			// ldcw (big consumer of constant inits)
			clinit.declarationSourceStart = clinit.sourceStart = sourceStart;
			clinit.declarationSourceEnd = clinit.sourceEnd = sourceEnd;
			clinit.bodyEnd = sourceEnd;
			this.methods = methods;
		}
	}

	/**
	 * Flow analysis for a local innertype
	 * 
	 */
	public FlowInfo analyseCode(BlockScope currentScope,
			FlowContext flowContext, FlowInfo flowInfo) {

		if (ignoreFurtherInvestigation)
			return flowInfo;
		try {
			bits |= IsReachableMASK;
			LocalTypeBinding localType = (LocalTypeBinding) binding;

			localType.setConstantPoolName(currentScope.compilationUnitScope()
					.computeConstantPoolName(localType));
			manageEnclosingInstanceAccessIfNecessary(currentScope);

			updateMaxFieldCount(); // propagate down the max field count
			internalAnalyseCode(flowContext, flowInfo);
		} catch (AbortType e) {
			this.ignoreFurtherInvestigation = true;
		}
		return flowInfo;
	}

	/**
	 * Flow analysis for a member innertype
	 * 
	 */
	public void analyseCode(ClassScope enclosingClassScope) {

		if (ignoreFurtherInvestigation)
			return;
		try {
			// propagate down the max field count
			updateMaxFieldCount();
			internalAnalyseCode(null, FlowInfo.initial(maxFieldCount));
		} catch (AbortType e) {
			this.ignoreFurtherInvestigation = true;
		}
	}

	/**
	 * Flow analysis for a local member innertype
	 * 
	 */
	public void analyseCode(ClassScope currentScope, FlowContext flowContext,
			FlowInfo flowInfo) {

		if (ignoreFurtherInvestigation)
			return;
		try {
			bits |= IsReachableMASK;
			LocalTypeBinding localType = (LocalTypeBinding) binding;

			localType.setConstantPoolName(currentScope.compilationUnitScope()
					.computeConstantPoolName(localType));
			manageEnclosingInstanceAccessIfNecessary(currentScope);

			updateMaxFieldCount(); // propagate down the max field count
			internalAnalyseCode(flowContext, flowInfo);
		} catch (AbortType e) {
			this.ignoreFurtherInvestigation = true;
		}
	}

	/**
	 * Flow analysis for a package member type
	 * 
	 */
	public void analyseCode(CompilationUnitScope unitScope) {

		if (ignoreFurtherInvestigation)
			return;
		try {
			internalAnalyseCode(null, FlowInfo.initial(maxFieldCount));
		} catch (AbortType e) {
			this.ignoreFurtherInvestigation = true;
		}
	}

	/*
	 * Check for constructor vs. method with no return type. Answers true if at
	 * least one constructor is defined
	 */
	public boolean checkConstructors(Parser parser) {

		// if a constructor has not the name of the type,
		// convert it into a method with 'null' as its return type
		boolean hasConstructor = false;
		if (methods != null) {
			for (int i = methods.length; --i >= 0;) {
				AbstractMethodDeclaration am;
				if ((am = methods[i]).isConstructor()) {
					if (!CharOperation.equals(am.selector, name)) {
						// the constructor was in fact a method with no return
						// type
						// unless an explicit constructor call was supplied
						ConstructorDeclaration c = (ConstructorDeclaration) am;
						if ((c.constructorCall == null)
								|| (c.constructorCall.isImplicitSuper())) { // changed
																			// to a
																			// method
							MethodDeclaration m = new MethodDeclaration(
									this.compilationResult);
							m.sourceStart = c.sourceStart;
							m.sourceEnd = c.sourceEnd;
							m.bodyStart = c.bodyStart;
							m.bodyEnd = c.bodyEnd;
							m.declarationSourceEnd = c.declarationSourceEnd;
							m.declarationSourceStart = c.declarationSourceStart;
							m.selector = c.selector;
							m.statements = c.statements;
							m.modifiers = c.modifiers;
							m.arguments = c.arguments;
							m.thrownExceptions = c.thrownExceptions;
							m.explicitDeclarations = c.explicitDeclarations;
							m.returnType = null;
							methods[i] = m;
						}
					} else {
						if (this.isInterface()) {
							// report the problem and continue the parsing
							parser.problemReporter()
									.interfaceCannotHaveConstructors(
											(ConstructorDeclaration) am);
						}
						hasConstructor = true;
					}
				}
			}
		}
		return hasConstructor;
	}

	public CompilationResult compilationResult() {

		return this.compilationResult;
	}

	public ConstructorDeclaration createsInternalConstructor(
			boolean needExplicitConstructorCall, boolean needToInsert) {

		// Add to method'set, the default constuctor that just recall the
		// super constructor with no arguments
		// The arguments' type will be positionned by the TC so just use
		// the default int instead of just null (consistency purpose)

		// the constructor
		ConstructorDeclaration constructor = new ConstructorDeclaration(
				this.compilationResult);
		constructor.isDefaultConstructor = true;
		constructor.selector = name;
		if (modifiers != AccDefault) {
			constructor.modifiers = ((this instanceof MemberTypeDeclaration) && (modifiers & AccPrivate) != 0) ? AccDefault
					: modifiers & AccVisibilityMASK;
		}

		// if you change this setting, please update the
		// SourceIndexer2.buildTypeDeclaration(TypeDeclaration,char[]) method
		constructor.declarationSourceStart = constructor.sourceStart = sourceStart;
		constructor.declarationSourceEnd = constructor.sourceEnd = constructor.bodyEnd = sourceEnd;

		// the super call inside the constructor
		if (needExplicitConstructorCall) {
			constructor.constructorCall = SuperReference
					.implicitSuperConstructorCall();
			constructor.constructorCall.sourceStart = sourceStart;
			constructor.constructorCall.sourceEnd = sourceEnd;
		}

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

	/**
	 * INTERNAL USE ONLY - Creates a fake method declaration for the
	 * corresponding binding. It is used to report errors for missing abstract
	 * methods.
	 */
	public MethodDeclaration addMissingAbstractMethodFor(
			MethodBinding methodBinding) {
		TypeBinding[] argumentTypes = methodBinding.parameters;
		int argumentsLength = argumentTypes.length;
		// the constructor
		MethodDeclaration methodDeclaration = new MethodDeclaration(
				this.compilationResult);
		methodDeclaration.selector = methodBinding.selector;
		methodDeclaration.sourceStart = sourceStart;
		methodDeclaration.sourceEnd = sourceEnd;
		methodDeclaration.modifiers = methodBinding.getAccessFlags()
				& ~AccAbstract;

		if (argumentsLength > 0) {
			String baseName = "arg";//$NON-NLS-1$
			Argument[] arguments = (methodDeclaration.arguments = new Argument[argumentsLength]);
			for (int i = argumentsLength; --i >= 0;) {
				arguments[i] = new Argument((baseName + i).toCharArray(), 0L,
						null /* type ref */, AccDefault);
			}
		}

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

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

		methodDeclaration.scope = new MethodScope(scope, methodDeclaration,
				true);
		methodDeclaration.bindArguments();

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

	/*
	 * Find the matching parse node, answers null if nothing found
	 */
	public FieldDeclaration declarationOf(FieldBinding fieldBinding) {

		if (fieldBinding != null) {
			for (int i = 0, max = this.fields.length; i < max; i++) {
				FieldDeclaration fieldDecl;
				if ((fieldDecl = this.fields[i]).binding == fieldBinding)
					return fieldDecl;
			}
		}
		return null;
	}

	/*
	 * Find the matching parse node, answers null if nothing found
	 */
	public TypeDeclaration declarationOf(MemberTypeBinding memberTypeBinding) {

		if (memberTypeBinding != null) {
			for (int i = 0, max = this.memberTypes.length; i < max; i++) {
				TypeDeclaration memberTypeDecl;
				if ((memberTypeDecl = this.memberTypes[i]).binding == memberTypeBinding)
					return memberTypeDecl;
			}
		}
		return null;
	}

	/*
	 * Find the matching parse node, answers null if nothing found
	 */
	public AbstractMethodDeclaration declarationOf(MethodBinding methodBinding) {

		if (methodBinding != null) {
			for (int i = 0, max = this.methods.length; i < max; i++) {
				AbstractMethodDeclaration methodDecl;

				if ((methodDecl = this.methods[i]).binding == methodBinding)
					return methodDecl;
			}
		}
		return null;
	}

	/*
	 * Finds the matching type amoung this type's member types. Returns null if
	 * no type with this name is found. The type name is a compound name
	 * relative to this type eg. if this type is X and we're looking for Y.X.A.B
	 * then a type name would be {X, A, B}
	 */
	public TypeDeclaration declarationOfType(char[][] typeName) {

		int typeNameLength = typeName.length;
		if (typeNameLength < 1 || !CharOperation.equals(typeName[0], this.name)) {
			return null;
		}
		if (typeNameLength == 1) {
			return this;
		}
		char[][] subTypeName = new char[typeNameLength - 1][];
		System.arraycopy(typeName, 1, subTypeName, 0, typeNameLength - 1);
		for (int i = 0; i < this.memberTypes.length; i++) {
			TypeDeclaration typeDecl = this.memberTypes[i]
					.declarationOfType(subTypeName);
			if (typeDecl != null) {
				return typeDecl;
			}
		}
		return null;
	}

	/**
	 * Generic bytecode generation for type
	 */
	// public void generateCode(ClassFile enclosingClassFile) {
	//
	// if (hasBeenGenerated)
	// return;
	// hasBeenGenerated = true;
	// if (ignoreFurtherInvestigation) {
	// if (binding == null)
	// return;
	// ClassFile.createProblemType(
	// this,
	// scope.referenceCompilationUnit().compilationResult);
	// return;
	// }
	// try {
	// // create the result for a compiled type
	// ClassFile classFile = new ClassFile(binding, enclosingClassFile, false);
	// // generate all fiels
	// classFile.addFieldInfos();
	//
	// // record the inner type inside its own .class file to be able
	// // to generate inner classes attributes
	// if (binding.isMemberType())
	// classFile.recordEnclosingTypeAttributes(binding);
	// if (binding.isLocalType()) {
	// enclosingClassFile.recordNestedLocalAttribute(binding);
	// classFile.recordNestedLocalAttribute(binding);
	// }
	// if (memberTypes != null) {
	// for (int i = 0, max = memberTypes.length; i < max; i++) {
	// // record the inner type inside its own .class file to be able
	// // to generate inner classes attributes
	// classFile.recordNestedMemberAttribute(memberTypes[i].binding);
	// memberTypes[i].generateCode(scope, classFile);
	// }
	// }
	// // generate all methods
	// classFile.setForMethodInfos();
	// if (methods != null) {
	// for (int i = 0, max = methods.length; i < max; i++) {
	// methods[i].generateCode(scope, classFile);
	// }
	// }
	//			
	// classFile.generateMissingAbstractMethods(this.missingAbstractMethods,
	// scope.referenceCompilationUnit().compilationResult);
	//
	// // generate all methods
	// classFile.addSpecialMethods();
	//
	// if (ignoreFurtherInvestigation) { // trigger problem type generation for
	// code gen errors
	// throw new AbortType(scope.referenceCompilationUnit().compilationResult);
	// }
	//
	// // finalize the compiled type result
	// classFile.addAttributes();
	// scope.referenceCompilationUnit().compilationResult.record(
	// binding.constantPoolName(),
	// classFile);
	// } catch (AbortType e) {
	// if (binding == null)
	// return;
	// ClassFile.createProblemType(
	// this,
	// scope.referenceCompilationUnit().compilationResult);
	// }
	// }
	/**
	 * Bytecode generation for a local inner type (API as a normal statement
	 * code gen)
	 */
	// public void generateCode(BlockScope blockScope, CodeStream codeStream) {
	//
	// if (hasBeenGenerated) return;
	// int pc = codeStream.position;
	// if (binding != null) ((NestedTypeBinding)
	// binding).computeSyntheticArgumentSlotSizes();
	// generateCode(codeStream.classFile);
	// codeStream.recordPositionsFrom(pc, this.sourceStart);
	// }
	/**
	 * Bytecode generation for a member inner type
	 */
	// public void generateCode(ClassScope classScope, ClassFile
	// enclosingClassFile) {
	//
	// if (hasBeenGenerated) return;
	// if (binding != null) ((NestedTypeBinding)
	// binding).computeSyntheticArgumentSlotSizes();
	// generateCode(enclosingClassFile);
	// }
	/**
	 * Bytecode generation for a package member
	 */
	// public void generateCode(CompilationUnitScope unitScope) {
	//
	// generateCode((ClassFile) null);
	// }
	public boolean hasErrors() {
		return this.ignoreFurtherInvestigation;
	}

	/**
	 * Common flow analysis for all types
	 * 
	 */
	public void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) {

		if (this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
			if (!scope.referenceCompilationUnit().compilationResult
					.hasSyntaxError()) {
				scope.problemReporter().unusedPrivateType(this);
			}
		}

		ReferenceBinding[] defaultHandledExceptions = new ReferenceBinding[] { scope
				.getJavaLangThrowable() }; // tolerate any kind of
		// exception
		InitializationFlowContext initializerContext = new InitializationFlowContext(
				null, this, initializerScope);
		InitializationFlowContext staticInitializerContext = new InitializationFlowContext(
				null, this, staticInitializerScope);
		FlowInfo nonStaticFieldInfo = flowInfo.copy().unconditionalInits()
				.discardFieldInitializations();
		FlowInfo staticFieldInfo = flowInfo.copy().unconditionalInits()
				.discardFieldInitializations();
		if (fields != null) {
			for (int i = 0, count = fields.length; i < count; i++) {
				FieldDeclaration field = fields[i];
				if (field.isStatic()) {
					/*
					 * if (field.isField()){
					 * staticInitializerContext.handledExceptions =
					 * NoExceptions; // no exception is allowed jls8.3.2 } else {
					 */
					staticInitializerContext.handledExceptions = defaultHandledExceptions; // tolerate
																							// them
																							// all,
																							// and
																							// record
																							// them
					/* } */
					staticFieldInfo = field.analyseCode(staticInitializerScope,
							staticInitializerContext, staticFieldInfo);
					// in case the initializer is not reachable, use a
					// reinitialized flowInfo and enter a fake reachable
					// branch, since the previous initializer already got the
					// blame.
					if (staticFieldInfo == FlowInfo.DEAD_END) {
						staticInitializerScope.problemReporter()
								.initializerMustCompleteNormally(field);
						staticFieldInfo = FlowInfo.initial(maxFieldCount)
								.setReachMode(FlowInfo.UNREACHABLE);
					}
				} else {
					/*
					 * if (field.isField()){
					 * initializerContext.handledExceptions = NoExceptions; //
					 * no exception is allowed jls8.3.2 } else {
					 */
					initializerContext.handledExceptions = defaultHandledExceptions; // tolerate
																						// them
																						// all,
																						// and
																						// record
																						// them
					/* } */
					nonStaticFieldInfo = field.analyseCode(initializerScope,
							initializerContext, nonStaticFieldInfo);
					// in case the initializer is not reachable, use a
					// reinitialized flowInfo and enter a fake reachable
					// branch, since the previous initializer already got the
					// blame.
					if (nonStaticFieldInfo == FlowInfo.DEAD_END) {
						initializerScope.problemReporter()
								.initializerMustCompleteNormally(field);
						nonStaticFieldInfo = FlowInfo.initial(maxFieldCount)
								.setReachMode(FlowInfo.UNREACHABLE);
					}
				}
			}
		}
		if (memberTypes != null) {
			for (int i = 0, count = memberTypes.length; i < count; i++) {
				if (flowContext != null) { // local type
					memberTypes[i].analyseCode(scope, flowContext,
							nonStaticFieldInfo.copy());
				} else {
					memberTypes[i].analyseCode(scope);
				}
			}
		}
		if (methods != null) {
			UnconditionalFlowInfo outerInfo = flowInfo.copy()
					.unconditionalInits().discardFieldInitializations();
			FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits()
					.discardNonFieldInitializations().addInitializationsFrom(
							outerInfo);
			for (int i = 0, count = methods.length; i < count; i++) {
				AbstractMethodDeclaration method = methods[i];
				if (method.ignoreFurtherInvestigation)
					continue;
				if (method.isInitializationMethod()) {
					if (method.isStatic()) { // <clinit>
						method.analyseCode(scope, staticInitializerContext,
								staticFieldInfo.unconditionalInits()
										.discardNonFieldInitializations()
										.addInitializationsFrom(outerInfo));
					} else { // constructor
						method.analyseCode(scope, initializerContext,
								constructorInfo.copy());
					}
				} else { // regular method
					method.analyseCode(scope, null, flowInfo.copy());
				}
			}
		}
	}

	public boolean isInterface() {

		return (modifiers & AccInterface) != 0;
	}

	/*
	 * Access emulation for a local type force to emulation of access to direct
	 * enclosing instance. By using the initializer scope, we actually only
	 * request an argument emulation, the field is not added until actually
	 * used. However we will force allocations to be qualified with an enclosing
	 * instance. 15.9.2
	 */
	public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope) {

		NestedTypeBinding nestedType = (NestedTypeBinding) binding;

		MethodScope methodScope = currentScope.methodScope();
		if (!methodScope.isStatic && !methodScope.isConstructorCall) {

			nestedType.addSyntheticArgumentAndField(binding.enclosingType());
		}
		// add superclass enclosing instance arg for anonymous types (if
		// necessary)
		if (binding.isAnonymousType()) {
			ReferenceBinding superclass = binding.superclass;
			if (superclass.enclosingType() != null && !superclass.isStatic()) {
				if (!binding.superclass.isLocalType()
						|| ((NestedTypeBinding) binding.superclass)
								.getSyntheticField(superclass.enclosingType(),
										true) != null) {

					nestedType.addSyntheticArgument(superclass.enclosingType());
				}
			}
		}
	}

	/*
	 * Access emulation for a local member type force to emulation of access to
	 * direct enclosing instance. By using the initializer scope, we actually
	 * only request an argument emulation, the field is not added until actually
	 * used. However we will force allocations to be qualified with an enclosing
	 * instance.
	 * 
	 * Local member cannot be static.
	 */
	public void manageEnclosingInstanceAccessIfNecessary(ClassScope currentScope) {

		NestedTypeBinding nestedType = (NestedTypeBinding) binding;
		nestedType.addSyntheticArgumentAndField(binding.enclosingType());
	}

	/**
	 * A <clinit>will be requested as soon as static fields or assertions are
	 * present. It will be eliminated during classfile creation if no bytecode
	 * was actually produced based on some optimizations/compiler settings.
	 */
	public final boolean needClassInitMethod() {

		// always need a <clinit> when assertions are present
		if ((this.bits & AddAssertionMASK) != 0)
			return true;
		if (fields == null)
			return false;
		if (isInterface())
			return true; // fields are implicitly statics
		for (int i = fields.length; --i >= 0;) {
			FieldDeclaration field = fields[i];
			// need to test the modifier directly while there is no binding yet
			if ((field.modifiers & AccStatic) != 0)
				return true;
		}
		return false;
	}

	public void parseMethod(UnitParser parser, CompilationUnitDeclaration unit) {

		// connect method bodies
		if (unit.ignoreMethodBodies)
			return;

		// no scope were created, so cannot report further errors
		// if (binding == null)
		// return;

		// members
		if (memberTypes != null) {
			int length = memberTypes.length;
			for (int i = 0; i < length; i++)
				memberTypes[i].parseMethod(parser, unit);
		}

		// methods
		if (methods != null) {
			int length = methods.length;
			for (int i = 0; i < length; i++)
				methods[i].parseStatements(parser, unit);
		}

		// initializers
		if (fields != null) {
			int length = fields.length;
			for (int i = 0; i < length; i++) {
				if (fields[i] instanceof Initializer) {
					((Initializer) fields[i]).parseStatements(parser, this,
							unit);
				}
			}
		}
	}

	public void resolve() {

		if (binding == null) {
			ignoreFurtherInvestigation = true;
			return;
		}

		try {
			// check superclass & interfaces
			if (binding.superclass != null) // watch out for Object ! (and other
											// roots)
				if (isTypeUseDeprecated(binding.superclass, scope))
					scope.problemReporter().deprecatedType(binding.superclass,
							superclass);
			if (superInterfaces != null)
				for (int i = superInterfaces.length; --i >= 0;)
					if (superInterfaces[i].resolvedType != null)
						if (isTypeUseDeprecated(
								superInterfaces[i].resolvedType, scope))
							scope.problemReporter().deprecatedType(
									superInterfaces[i].resolvedType,
									superInterfaces[i]);
			maxFieldCount = 0;
			int lastFieldID = -1;
			if (fields != null) {
				for (int i = 0, count = fields.length; i < count; i++) {
					FieldDeclaration field = fields[i];
					if (field.isField()) {
						if (field.binding == null) {
							// still discover secondary errors
							if (field.initialization != null)
								field.initialization
										.resolve(field.isStatic() ? staticInitializerScope
												: initializerScope);
							ignoreFurtherInvestigation = true;
							continue;
						}
						maxFieldCount++;
						lastFieldID = field.binding.id;
					} else { // initializer
						((Initializer) field).lastFieldID = lastFieldID + 1;
					}
					field.resolve(field.isStatic() ? staticInitializerScope
							: initializerScope);
				}
			}
			if (memberTypes != null) {
				for (int i = 0, count = memberTypes.length; i < count; i++) {
					memberTypes[i].resolve(scope);
				}
			}
			int missingAbstractMethodslength = this.missingAbstractMethods == null ? 0
					: this.missingAbstractMethods.length;
			int methodsLength = this.methods == null ? 0 : methods.length;
			if ((methodsLength + missingAbstractMethodslength) > 0xFFFF) {
				scope.problemReporter().tooManyMethods(this);
			}

			if (methods != null) {
				for (int i = 0, count = methods.length; i < count; i++) {
					methods[i].resolve(scope);
				}
			}
		} catch (AbortType e) {
			this.ignoreFurtherInvestigation = true;
			return;
		}
		;
	}

	public void resolve(BlockScope blockScope) {
		// local type declaration

		// need to build its scope first and proceed with binding's creation
		blockScope.addLocalType(this);

		// and TC....
		if (binding != null) {
			// remember local types binding for innerclass emulation propagation
			blockScope.referenceCompilationUnit().record(
					(LocalTypeBinding) binding);

			// binding is not set if the receiver could not be created
			resolve();
			updateMaxFieldCount();
		}
	}

	public void resolve(ClassScope upperScope) {
		// member scopes are already created
		// request the construction of a binding if local member type

		if (binding != null && binding instanceof LocalTypeBinding) {
			// remember local types binding for innerclass emulation propagation
			upperScope.referenceCompilationUnit().record(
					(LocalTypeBinding) binding);
		}
		resolve();
		updateMaxFieldCount();
	}

	public void resolve(CompilationUnitScope upperScope) {
		// top level : scope are already created

		resolve();
		updateMaxFieldCount();
	}

	public void tagAsHavingErrors() {
		ignoreFurtherInvestigation = true;
	}

	public StringBuffer print(int indent, StringBuffer output) {

		// if ((this.bits & IsAnonymousTypeMASK) == 0) {
		printIndent(indent, output);
		printHeader(0, output);
		// }
		return printBody(indent, output);
	}

	public StringBuffer printBody(int indent, StringBuffer output) {

		output.append(" {"); //$NON-NLS-1$
		if (memberTypes != null) {
			for (int i = 0; i < memberTypes.length; i++) {
				if (memberTypes[i] != null) {
					output.append('\n');
					memberTypes[i].print(indent + 1, output);
				}
			}
		}
		if (fields != null) {
			for (int fieldI = 0; fieldI < fields.length; fieldI++) {
				if (fields[fieldI] != null) {
					output.append('\n');
					fields[fieldI].print(indent + 1, output);
				}
			}
		}
		if (methods != null) {
			for (int i = 0; i < methods.length; i++) {
				if (methods[i] != null) {
					output.append('\n');
					methods[i].print(indent + 1, output);
				}
			}
		}
		output.append('\n');
		return printIndent(indent, output).append('}');
	}

	public StringBuffer printHeader(int indent, StringBuffer output) {

		printModifiers(this.modifiers, output);
		output.append(isInterface() ? "interface " : "class "); //$NON-NLS-1$ //$NON-NLS-2$
		output.append(name);
		if (superclass != null) {
			output.append(" extends "); //$NON-NLS-1$
			superclass.print(0, output);
		}
		if (superInterfaces != null && superInterfaces.length > 0) {
			output.append(isInterface() ? " extends " : " implements ");//$NON-NLS-2$ //$NON-NLS-1$
			for (int i = 0; i < superInterfaces.length; i++) {
				if (i > 0)
					output.append(", "); //$NON-NLS-1$
				superInterfaces[i].print(0, output);
			}
		}
		return output;
	}

	public StringBuffer printStatement(int tab, StringBuffer output) {
		return print(tab, output);
	}

	public String toString(int tab) {

		return tabString(tab) + toStringHeader() + toStringBody(tab);
	}

	public String toStringBody(int tab) {

		String s = " {"; //$NON-NLS-1$
		if (memberTypes != null) {
			for (int i = 0; i < memberTypes.length; i++) {
				if (memberTypes[i] != null) {
					s += "\n" + memberTypes[i].toString(tab + 1); //$NON-NLS-1$
				}
			}
		}
		if (fields != null) {
			for (int fieldI = 0; fieldI < fields.length; fieldI++) {
				if (fields[fieldI] != null) {
					s += "\n" + fields[fieldI].toString(tab + 1); //$NON-NLS-1$
					if (fields[fieldI].isField())
						s += ";"; //$NON-NLS-1$
				}
			}
		}
		if (methods != null) {
			for (int i = 0; i < methods.length; i++) {
				if (methods[i] != null) {
					s += "\n" + methods[i].toString(tab + 1); //$NON-NLS-1$
				}
			}
		}
		s += "\n" + tabString(tab) + "}"; //$NON-NLS-2$ //$NON-NLS-1$
		return s;
	}

	public String toStringHeader() {

		String s = ""; //$NON-NLS-1$
		if (modifiers != AccDefault) {
			s += modifiersString(modifiers);
		}
		s += (isInterface() ? "interface " : "class ") + new String(name);//$NON-NLS-1$ //$NON-NLS-2$
		if (superclass != null)
			s += " extends " + superclass.toString(0); //$NON-NLS-1$
		if (superInterfaces != null && superInterfaces.length > 0) {
			s += (isInterface() ? " extends " : " implements ");//$NON-NLS-2$ //$NON-NLS-1$
			for (int i = 0; i < superInterfaces.length; i++) {
				s += superInterfaces[i].toString(0);
				if (i != superInterfaces.length - 1)
					s += ", "; //$NON-NLS-1$
			}
			;
		}
		;
		return s;
	}

	/**
	 * Iteration for a local innertype
	 * 
	 */
	public void traverse(ASTVisitor visitor, BlockScope blockScope) {
		if (ignoreFurtherInvestigation)
			return;
		try {
			if (visitor.visit(this, blockScope)) {
				if (superclass != null)
					superclass.traverse(visitor, scope);
				if (superInterfaces != null) {
					int superInterfaceLength = superInterfaces.length;
					for (int i = 0; i < superInterfaceLength; i++)
						superInterfaces[i].traverse(visitor, scope);
				}
				if (memberTypes != null) {
					int memberTypesLength = memberTypes.length;
					for (int i = 0; i < memberTypesLength; i++)
						memberTypes[i].traverse(visitor, scope);
				}
				if (fields != null) {
					int 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) {
					int methodsLength = methods.length;
					for (int i = 0; i < methodsLength; i++)
						methods[i].traverse(visitor, scope);
				}
			}
			visitor.endVisit(this, blockScope);
		} catch (AbortType e) {
			// silent abort
		}
	}

	/**
	 * Iteration for a member innertype
	 * 
	 */
	public void traverse(ASTVisitor visitor, ClassScope classScope) {
		if (ignoreFurtherInvestigation)
			return;
		try {
			if (visitor.visit(this, classScope)) {
				if (superclass != null)
					superclass.traverse(visitor, scope);
				if (superInterfaces != null) {
					int superInterfaceLength = superInterfaces.length;
					for (int i = 0; i < superInterfaceLength; i++)
						superInterfaces[i].traverse(visitor, scope);
				}
				if (memberTypes != null) {
					int memberTypesLength = memberTypes.length;
					for (int i = 0; i < memberTypesLength; i++)
						memberTypes[i].traverse(visitor, scope);
				}
				if (fields != null) {
					int fieldsLength = fields.length;
					for (int i = 0; i < fieldsLength; i++) {
						FieldDeclaration field;
						if ((field = fields[i]).isStatic()) {
							field.traverse(visitor, staticInitializerScope);
						} else {
							field.traverse(visitor, initializerScope);
						}
					}
				}
				if (methods != null) {
					int methodsLength = methods.length;
					for (int i = 0; i < methodsLength; i++)
						methods[i].traverse(visitor, scope);
				}
			}
			visitor.endVisit(this, classScope);
		} catch (AbortType e) {
			// silent abort
		}
	}

	/**
	 * Iteration for a package member type
	 * 
	 */
	public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) {

		if (ignoreFurtherInvestigation)
			return;
		try {
			if (visitor.visit(this, unitScope)) {
				if (superclass != null)
					superclass.traverse(visitor, scope);
				if (superInterfaces != null) {
					int superInterfaceLength = superInterfaces.length;
					for (int i = 0; i < superInterfaceLength; i++)
						superInterfaces[i].traverse(visitor, scope);
				}
				if (memberTypes != null) {
					int memberTypesLength = memberTypes.length;
					for (int i = 0; i < memberTypesLength; i++)
						memberTypes[i].traverse(visitor, scope);
				}
				if (fields != null) {
					int fieldsLength = fields.length;
					for (int i = 0; i < fieldsLength; i++) {
						FieldDeclaration field;
						if ((field = fields[i]).isStatic()) {
							field.traverse(visitor, staticInitializerScope);
						} else {
							field.traverse(visitor, initializerScope);
						}
					}
				}
				if (methods != null) {
					int methodsLength = methods.length;
					for (int i = 0; i < methodsLength; i++)
						methods[i].traverse(visitor, scope);
				}
			}
			visitor.endVisit(this, unitScope);
		} catch (AbortType e) {
		}
	}

	/**
	 * MaxFieldCount's computation is necessary so as to reserve space for the
	 * flow info field portions. It corresponds to the maximum amount of fields
	 * this class or one of its innertypes have.
	 * 
	 * During name resolution, types are traversed, and the max field count is
	 * recorded on the outermost type. It is then propagated down during the
	 * flow analysis.
	 * 
	 * This method is doing either up/down propagation.
	 */
	void updateMaxFieldCount() {

		if (binding == null)
			return; // error scenario
		TypeDeclaration outerMostType = scope.outerMostClassScope()
				.referenceType();
		if (maxFieldCount > outerMostType.maxFieldCount) {
			outerMostType.maxFieldCount = maxFieldCount; // up
		} else {
			maxFieldCount = outerMostType.maxFieldCount; // down
		}
	}
}