/*******************************************************************************
 * 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;

import java.util.ArrayList;

import net.sourceforge.phpdt.core.compiler.CharOperation;
import net.sourceforge.phpdt.core.compiler.IProblem;
import net.sourceforge.phpdt.internal.compiler.ast.ASTNode;
import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.AnonymousLocalTypeDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.Argument;
import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.NameReference;
import net.sourceforge.phpdt.internal.compiler.ast.QualifiedAllocationExpression;
import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
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.problem.AbortCompilation;
import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
import net.sourceforge.phpdt.internal.core.util.CommentRecorderParser;

/**
 * A source element parser extracts structural and reference information from a
 * piece of source.
 * 
 * also see
 * 
 * @ISourceElementRequestor
 * 
 * The structural investigation includes: - the package statement - import
 * statements - top-level types: package member, member types (member types of
 * member types...) - fields - methods
 * 
 * If reference information is requested, then all source constructs are
 * investigated and type, field & method references are provided as well.
 * 
 * Any (parsing) problem encountered is also provided.
 */

public class SourceElementParser extends CommentRecorderParser {// extends
	// UnitParser {

	ISourceElementRequestor requestor;

	int fieldCount;

	int localIntPtr;

	int lastFieldEndPosition;

	ISourceType sourceType;

	boolean reportReferenceInfo;

	char[][] typeNames;

	char[][] superTypeNames;

	int nestedTypeIndex;

	static final char[] JAVA_LANG_OBJECT = "java.lang.Object".toCharArray(); //$NON-NLS-1$

	NameReference[] unknownRefs;

	int unknownRefsCounter;

	LocalDeclarationVisitor localDeclarationVisitor = null;

	// CompilerOptions options;

	/**
	 * An ast visitor that visits local type declarations.
	 */
	public class LocalDeclarationVisitor extends ASTVisitor {
		public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) {
			notifySourceElementRequestor(typeDeclaration, sourceType == null);
			return false; // don't visit members as this was done during
							// notifySourceElementRequestor(...)
		}

		public boolean visit(TypeDeclaration typeDeclaration, ClassScope scope) {
			notifySourceElementRequestor(typeDeclaration, sourceType == null);
			return false; // don't visit members as this was done during
							// notifySourceElementRequestor(...)
		}

	}

	public SourceElementParser(final ISourceElementRequestor requestor,
			IProblemFactory problemFactory, CompilerOptions options) {
		// we want to notify all syntax error with the acceptProblem API
		// To do so, we define the record method of the ProblemReporter
		super(new ProblemReporter(DefaultErrorHandlingPolicies
				.exitAfterAllProblems(), options, problemFactory) {
			public void record(IProblem problem, CompilationResult unitResult,
					ReferenceContext referenceContext) {
				unitResult.record(problem, referenceContext);
				if (requestor != null) {
					requestor.acceptProblem(problem);
				}
			}
		});
		// true);
		// options.sourceLevel >= CompilerOptions.JDK1_4);
		this.requestor = requestor;
		typeNames = new char[4][];
		superTypeNames = new char[4][];
		nestedTypeIndex = 0;
		this.options = options;
	}

	/**
	 * @deprecated use SourceElementParser(ISourceElementRequestor,
	 *             IProblemFactory, CompilerOptions)
	 */
	public SourceElementParser(final ISourceElementRequestor requestor,
			IProblemFactory problemFactory) {
		this(requestor, problemFactory, new CompilerOptions());
	}

	public SourceElementParser(final ISourceElementRequestor requestor,
			IProblemFactory problemFactory, CompilerOptions options,
			boolean reportLocalDeclarations) {
		this(requestor, problemFactory, options);
		if (reportLocalDeclarations) {
			this.localDeclarationVisitor = new LocalDeclarationVisitor();
		}
	}

	// public void checkAnnotation() {
	// int firstCommentIndex = scanner.commentPtr;
	//
	// super.checkAnnotation();
	//
	// // modify the modifier source start to point at the first comment
	// if (firstCommentIndex >= 0) {
	// modifiersSourceStart = scanner.commentStarts[0];
	// }
	// }
	// protected void classInstanceCreation(boolean alwaysQualified) {
	//
	// boolean previousFlag = reportReferenceInfo;
	// reportReferenceInfo = false; // not to see the type reference reported in
	// super call to getTypeReference(...)
	// super.classInstanceCreation(alwaysQualified);
	// reportReferenceInfo = previousFlag;
	// if (reportReferenceInfo){
	// AllocationExpression alloc =
	// (AllocationExpression)expressionStack[expressionPtr];
	// TypeReference typeRef = alloc.type;
	// requestor.acceptConstructorReference(
	// typeRef instanceof SingleTypeReference
	// ? ((SingleTypeReference) typeRef).token
	// : CharOperation.concatWith(alloc.type.getTypeName(), '.'),
	// alloc.arguments == null ? 0 : alloc.arguments.length,
	// alloc.sourceStart);
	// }
	// }
	// protected void consumeConstructorHeaderName() {
	// // ConstructorHeaderName ::= Modifiersopt 'Identifier' '('
	//
	// /* recovering - might be an empty message send */
	// if (currentElement != null){
	// if (lastIgnoredToken == TokenNamenew){ // was an allocation expression
	// lastCheckPoint = scanner.startPosition; // force to restart at this exact
	// position
	// restartRecovery = true;
	// return;
	// }
	// }
	// SourceConstructorDeclaration cd = new
	// SourceConstructorDeclaration(this.compilationUnit.compilationResult);
	//
	// //name -- this is not really revelant but we do .....
	// cd.selector = identifierStack[identifierPtr];
	// long selectorSourcePositions = identifierPositionStack[identifierPtr--];
	// identifierLengthPtr--;
	//
	// //modifiers
	// cd.declarationSourceStart = intStack[intPtr--];
	// cd.modifiers = intStack[intPtr--];
	//
	// //highlight starts at the selector starts
	// cd.sourceStart = (int) (selectorSourcePositions >>> 32);
	// cd.selectorSourceEnd = (int) selectorSourcePositions;
	// pushOnAstStack(cd);
	//
	// cd.sourceEnd = lParenPos;
	// cd.bodyStart = lParenPos+1;
	// listLength = 0; // initialize listLength before reading parameters/throws
	//
	// // recovery
	// if (currentElement != null){
	// lastCheckPoint = cd.bodyStart;
	// if ((currentElement instanceof RecoveredType && lastIgnoredToken !=
	// TokenNameDOT)
	// || cd.modifiers != 0){
	// currentElement = currentElement.add(cd, 0);
	// lastIgnoredToken = -1;
	// }
	// }
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// protected void consumeExitVariableWithInitialization() {
	// // ExitVariableWithInitialization ::= $empty
	// // the scanner is located after the comma or the semi-colon.
	// // we want to include the comma or the semi-colon
	// super.consumeExitVariableWithInitialization();
	// if (isLocalDeclaration() || ((currentToken != TokenNameCOMMA) &&
	// (currentToken != TokenNameSEMICOLON)))
	// return;
	// ((SourceFieldDeclaration) astStack[astPtr]).fieldEndPosition =
	// scanner.currentPosition - 1;
	// }
	// protected void consumeExitVariableWithoutInitialization() {
	// // ExitVariableWithoutInitialization ::= $empty
	// // do nothing by default
	// super.consumeExitVariableWithoutInitialization();
	// if (isLocalDeclaration() || ((currentToken != TokenNameCOMMA) &&
	// (currentToken != TokenNameSEMICOLON)))
	// return;
	// ((SourceFieldDeclaration) astStack[astPtr]).fieldEndPosition =
	// scanner.currentPosition - 1;
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// protected void consumeFieldAccess(boolean isSuperAccess) {
	// // FieldAccess ::= Primary '.' 'Identifier'
	// // FieldAccess ::= 'super' '.' 'Identifier'
	// super.consumeFieldAccess(isSuperAccess);
	// FieldReference fr = (FieldReference) expressionStack[expressionPtr];
	// if (reportReferenceInfo) {
	// requestor.acceptFieldReference(fr.token, fr.sourceStart);
	// }
	// }
	// protected void consumeMethodHeaderName() {
	// // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
	// SourceMethodDeclaration md = new
	// SourceMethodDeclaration(this.compilationUnit.compilationResult);
	//
	// //name
	// md.selector = identifierStack[identifierPtr];
	// long selectorSourcePositions = identifierPositionStack[identifierPtr--];
	// identifierLengthPtr--;
	// //type
	// md.returnType = getTypeReference(intStack[intPtr--]);
	// //modifiers
	// md.declarationSourceStart = intStack[intPtr--];
	// md.modifiers = intStack[intPtr--];
	//
	// //highlight starts at selector start
	// md.sourceStart = (int) (selectorSourcePositions >>> 32);
	// md.selectorSourceEnd = (int) selectorSourcePositions;
	// pushOnAstStack(md);
	// md.sourceEnd = lParenPos;
	// md.bodyStart = lParenPos+1;
	// listLength = 0; // initialize listLength before reading parameters/throws
	//	
	// // recovery
	// if (currentElement != null){
	// if (currentElement instanceof RecoveredType
	// //|| md.modifiers != 0
	// || (scanner.getLineNumber(md.returnType.sourceStart)
	// == scanner.getLineNumber(md.sourceStart))){
	// lastCheckPoint = md.bodyStart;
	// currentElement = currentElement.add(md, 0);
	// lastIgnoredToken = -1;
	// } else {
	// lastCheckPoint = md.sourceStart;
	// restartRecovery = true;
	// }
	// }
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// protected void consumeMethodInvocationName() {
	// // MethodInvocation ::= Name '(' ArgumentListopt ')'
	//
	// // when the name is only an identifier...we have a message send to "this"
	// (implicit)
	// super.consumeMethodInvocationName();
	// MessageSend messageSend = (MessageSend) expressionStack[expressionPtr];
	// Expression[] args = messageSend.arguments;
	// if (reportReferenceInfo) {
	// requestor.acceptMethodReference(
	// messageSend.selector,
	// args == null ? 0 : args.length,
	// (int)(messageSend.nameSourcePosition >>> 32));
	// }
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// protected void consumeMethodInvocationPrimary() {
	// super.consumeMethodInvocationPrimary();
	// MessageSend messageSend = (MessageSend) expressionStack[expressionPtr];
	// Expression[] args = messageSend.arguments;
	// if (reportReferenceInfo) {
	// requestor.acceptMethodReference(
	// messageSend.selector,
	// args == null ? 0 : args.length,
	// (int)(messageSend.nameSourcePosition >>> 32));
	// }
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// protected void consumeMethodInvocationSuper() {
	// // MethodInvocation ::= 'super' '.' 'Identifier' '(' ArgumentListopt ')'
	// super.consumeMethodInvocationSuper();
	// MessageSend messageSend = (MessageSend) expressionStack[expressionPtr];
	// Expression[] args = messageSend.arguments;
	// if (reportReferenceInfo) {
	// requestor.acceptMethodReference(
	// messageSend.selector,
	// args == null ? 0 : args.length,
	// (int)(messageSend.nameSourcePosition >>> 32));
	// }
	// }
	// protected void consumeSingleTypeImportDeclarationName() {
	// // SingleTypeImportDeclarationName ::= 'import' Name
	// /* push an ImportRef build from the last name
	// stored in the identifier stack. */
	//
	// super.consumeSingleTypeImportDeclarationName();
	// ImportReference impt = (ImportReference)astStack[astPtr];
	// if (reportReferenceInfo) {
	// requestor.acceptTypeReference(impt.tokens, impt.sourceStart,
	// impt.sourceEnd);
	// }
	// }
	// protected void consumeTypeImportOnDemandDeclarationName() {
	// // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*'
	// /* push an ImportRef build from the last name
	// stored in the identifier stack. */
	//
	// super.consumeTypeImportOnDemandDeclarationName();
	// ImportReference impt = (ImportReference)astStack[astPtr];
	// if (reportReferenceInfo) {
	// requestor.acceptUnknownReference(impt.tokens, impt.sourceStart,
	// impt.sourceEnd);
	// }
	// }
	// protected FieldDeclaration createFieldDeclaration(Expression
	// initialization, char[] name, int sourceStart, int sourceEnd) {
	// return new SourceFieldDeclaration(null, name, sourceStart, sourceEnd);
	// }
	// protected CompilationUnitDeclaration endParse(int act) {
	// if (sourceType != null) {
	// if (sourceType.isInterface()) {
	// consumeInterfaceDeclaration();
	// } else {
	// consumeClassDeclaration();
	// }
	// }
	// if (compilationUnit != null) {
	// CompilationUnitDeclaration result = super.endParse(act);
	// return result;
	// } else {
	// return null;
	// }
	// }
	/*
	 * Flush annotations defined prior to a given positions.
	 * 
	 * Note: annotations are stacked in syntactical order
	 * 
	 * Either answer given <position>, or the end position of a comment line
	 * immediately following the <position> (same line)
	 * 
	 * e.g. void foo(){ } // end of method foo
	 */
	// 
	// public int flushAnnotationsDefinedPriorTo(int position) {
	//
	// return lastFieldEndPosition =
	// super.flushAnnotationsDefinedPriorTo(position);
	// }
	// public TypeReference getTypeReference(int dim) {
	// /* build a Reference on a variable that may be qualified or not
	// * This variable is a type reference and dim will be its dimensions
	// */
	// int length;
	// if ((length = identifierLengthStack[identifierLengthPtr--]) == 1) {
	// // single variable reference
	// if (dim == 0) {
	// SingleTypeReference ref =
	// new SingleTypeReference(
	// identifierStack[identifierPtr],
	// identifierPositionStack[identifierPtr--]);
	// if (reportReferenceInfo) {
	// requestor.acceptTypeReference(ref.token, ref.sourceStart);
	// }
	// return ref;
	// } else {
	// ArrayTypeReference ref =
	// new ArrayTypeReference(
	// identifierStack[identifierPtr],
	// dim,
	// identifierPositionStack[identifierPtr--]);
	// ref.sourceEnd = endPosition;
	// if (reportReferenceInfo) {
	// requestor.acceptTypeReference(ref.token, ref.sourceStart);
	// }
	// return ref;
	// }
	// } else {
	// if (length < 0) { //flag for precompiled type reference on base types
	// TypeReference ref = TypeReference.baseTypeReference(-length, dim);
	// ref.sourceStart = intStack[intPtr--];
	// if (dim == 0) {
	// ref.sourceEnd = intStack[intPtr--];
	// } else {
	// intPtr--; // no need to use this position as it is an array
	// ref.sourceEnd = endPosition;
	// }
	// if (reportReferenceInfo){
	// requestor.acceptTypeReference(ref.getTypeName(), ref.sourceStart,
	// ref.sourceEnd);
	// }
	// return ref;
	// } else { //Qualified variable reference
	// char[][] tokens = new char[length][];
	// identifierPtr -= length;
	// long[] positions = new long[length];
	// System.arraycopy(identifierStack, identifierPtr + 1, tokens, 0, length);
	// System.arraycopy(
	// identifierPositionStack,
	// identifierPtr + 1,
	// positions,
	// 0,
	// length);
	// if (dim == 0) {
	// QualifiedTypeReference ref = new QualifiedTypeReference(tokens,
	// positions);
	// if (reportReferenceInfo) {
	// requestor.acceptTypeReference(ref.tokens, ref.sourceStart,
	// ref.sourceEnd);
	// }
	// return ref;
	// } else {
	// ArrayQualifiedTypeReference ref =
	// new ArrayQualifiedTypeReference(tokens, dim, positions);
	// ref.sourceEnd = endPosition;
	// if (reportReferenceInfo) {
	// requestor.acceptTypeReference(ref.tokens, ref.sourceStart,
	// ref.sourceEnd);
	// }
	// return ref;
	// }
	// }
	// }
	// }
	// public NameReference getUnspecifiedReference() {
	// /* build a (unspecified) NameReference which may be qualified*/
	//
	// int length;
	// if ((length = identifierLengthStack[identifierLengthPtr--]) == 1) {
	// // single variable reference
	// SingleNameReference ref =
	// new SingleNameReference(
	// identifierStack[identifierPtr],
	// identifierPositionStack[identifierPtr--]);
	// if (reportReferenceInfo) {
	// this.addUnknownRef(ref);
	// }
	// return ref;
	// } else {
	// //Qualified variable reference
	// char[][] tokens = new char[length][];
	// identifierPtr -= length;
	// System.arraycopy(identifierStack, identifierPtr + 1, tokens, 0, length);
	// QualifiedNameReference ref =
	// new QualifiedNameReference(
	// tokens,
	// (int) (identifierPositionStack[identifierPtr + 1] >> 32), // sourceStart
	// (int) identifierPositionStack[identifierPtr + length]); // sourceEnd
	// if (reportReferenceInfo) {
	// this.addUnknownRef(ref);
	// }
	// return ref;
	// }
	// }
	// public NameReference getUnspecifiedReferenceOptimized() {
	// /* build a (unspecified) NameReference which may be qualified
	// The optimization occurs for qualified reference while we are
	// certain in this case the last item of the qualified name is
	// a field access. This optimization is IMPORTANT while it results
	// that when a NameReference is build, the type checker should always
	// look for that it is not a type reference */
	//
	// int length;
	// if ((length = identifierLengthStack[identifierLengthPtr--]) == 1) {
	// // single variable reference
	// SingleNameReference ref =
	// new SingleNameReference(
	// identifierStack[identifierPtr],
	// identifierPositionStack[identifierPtr--]);
	// ref.bits &= ~ASTNode.RestrictiveFlagMASK;
	// ref.bits |= LOCAL | FIELD;
	// if (reportReferenceInfo) {
	// this.addUnknownRef(ref);
	// }
	// return ref;
	// }
	//
	// //Qualified-variable-reference
	// //In fact it is variable-reference DOT field-ref , but it would result in
	// a
	// type
	// //conflict tha can be only reduce by making a superclass (or inetrface )
	// between
	// //nameReference and FiledReference or putting FieldReference under
	// NameReference
	// //or else..........This optimisation is not really relevant so just leave
	// as it is
	//
	// char[][] tokens = new char[length][];
	// identifierPtr -= length;
	// System.arraycopy(identifierStack, identifierPtr + 1, tokens, 0, length);
	// QualifiedNameReference ref =
	// new QualifiedNameReference(
	// tokens,
	// (int) (identifierPositionStack[identifierPtr + 1] >> 32),
	// // sourceStart
	// (int) identifierPositionStack[identifierPtr + length]); // sourceEnd
	// ref.bits &= ~ASTNode.RestrictiveFlagMASK;
	// ref.bits |= LOCAL | FIELD;
	// if (reportReferenceInfo) {
	// this.addUnknownRef(ref);
	// }
	// return ref;
	// }
	// /**
	// *
	// * INTERNAL USE-ONLY
	// */
	// private boolean isLocalDeclaration() {
	// int nestedDepth = nestedType;
	// while (nestedDepth >= 0) {
	// if (nestedMethod[nestedDepth] != 0) {
	// return true;
	// }
	// nestedDepth--;
	// }
	// return false;
	// }
	/*
	 * Update the bodyStart of the corresponding parse node
	 */
	public void notifySourceElementRequestor(
			CompilationUnitDeclaration parsedUnit) {
		if (parsedUnit == null) {
			// when we parse a single type member declaration the compilation
			// unit is
			// null, but we still
			// want to be able to notify the requestor on the created ast node
			if (astStack[0] instanceof AbstractMethodDeclaration) {
				notifySourceElementRequestor((AbstractMethodDeclaration) astStack[0]);
				return;
			}
			return;
		}
		// range check
		boolean isInRange = scanner.initialPosition <= parsedUnit.sourceStart
				&& scanner.eofPosition >= parsedUnit.sourceEnd;

		// if (reportReferenceInfo) {
		// notifyAllUnknownReferences();
		// }
		// collect the top level ast nodes
		int length = 0;
		ASTNode[] nodes = null;
		if (sourceType == null) {
			if (isInRange) {
				requestor.enterCompilationUnit();
			}
			// ImportReference currentPackage = parsedUnit.currentPackage;
			ImportReference[] imports = parsedUnit.imports;
			// TypeDeclaration[] types = parsedUnit.types;
			ArrayList types = parsedUnit.types;
			if (types != null) {
				// length =
				// (currentPackage == null ? 0 : 1)
				// + (imports == null ? 0 : imports.length)
				// + (types == null ? 0 : types.length);
				// nodes = new ASTNode[length];
				length = (imports == null ? 0 : imports.length) + types.size();
				nodes = new ASTNode[length];
				int index = 0;
				// if (currentPackage != null) {
				// nodes[index++] = currentPackage;
				// }
				if (imports != null) {
					for (int i = 0, max = imports.length; i < max; i++) {
						nodes[index++] = imports[i];
					}
				}

				for (int i = 0, max = types.size(); i < max; i++) {
					nodes[index++] = (ASTNode) types.get(i);
				}
			}
		} else {
			// TypeDeclaration[] types = parsedUnit.types;
			ArrayList types = parsedUnit.types;
			if (types != null) {
				length = types.size();
				nodes = new ASTNode[length];
				for (int i = 0, max = types.size(); i < max; i++) {
					nodes[i] = (ASTNode) types.get(i);
				}
			}
		}

		// notify the nodes in the syntactical order
		if (nodes != null && length > 0) {
			quickSort(nodes, 0, length - 1);
			for (int i = 0; i < length; i++) {
				ASTNode node = nodes[i];
				if (node instanceof ImportReference) {
					ImportReference importRef = (ImportReference) node;
					// if (node == parsedUnit.currentPackage) {
					// notifySourceElementRequestor(importRef, true);
					// } else {
					notifySourceElementRequestor(importRef, false);
					// }
				} // else { instanceof TypeDeclaration
				if (node instanceof TypeDeclaration) {
					notifySourceElementRequestor((TypeDeclaration) node,
							sourceType == null);
					// notifySourceElementRequestor((CompilationUnitDeclaration)node,
					// sourceType == null);
				}
				// jsurfer - INSERT start
				if (node instanceof AbstractMethodDeclaration) {
					notifySourceElementRequestor((AbstractMethodDeclaration) node);
				}
				// jsurfer - INSERT end
			}
		}

		if (sourceType == null) {
			if (isInRange) {
				requestor.exitCompilationUnit(parsedUnit.sourceEnd);
			}
		}
	}

	// private void notifyAllUnknownReferences() {
	// for (int i = 0, max = this.unknownRefsCounter; i < max; i++) {
	// NameReference nameRef = this.unknownRefs[i];
	// if ((nameRef.bits & BindingIds.VARIABLE) != 0) {
	// if ((nameRef.bits & BindingIds.TYPE) == 0) {
	// // variable but not type
	// if (nameRef instanceof SingleNameReference) {
	// // local var or field
	// requestor.acceptUnknownReference(((SingleNameReference) nameRef).token,
	// nameRef.sourceStart);
	// } else {
	// // QualifiedNameReference
	// // The last token is a field reference and the previous tokens are a
	// type/variable references
	// char[][] tokens = ((QualifiedNameReference) nameRef).tokens;
	// int tokensLength = tokens.length;
	// requestor.acceptFieldReference(tokens[tokensLength - 1],
	// nameRef.sourceEnd
	// - tokens[tokensLength - 1].length + 1);
	// char[][] typeRef = new char[tokensLength - 1][];
	// System.arraycopy(tokens, 0, typeRef, 0, tokensLength - 1);
	// requestor.acceptUnknownReference(typeRef, nameRef.sourceStart,
	// nameRef.sourceEnd - tokens[tokensLength - 1].length);
	// }
	// } else {
	// // variable or type
	// if (nameRef instanceof SingleNameReference) {
	// requestor.acceptUnknownReference(((SingleNameReference) nameRef).token,
	// nameRef.sourceStart);
	// } else {
	// //QualifiedNameReference
	// requestor.acceptUnknownReference(((QualifiedNameReference)
	// nameRef).tokens,
	// nameRef.sourceStart, nameRef.sourceEnd);
	// }
	// }
	// } else if ((nameRef.bits & BindingIds.TYPE) != 0) {
	// if (nameRef instanceof SingleNameReference) {
	// requestor.acceptTypeReference(((SingleNameReference) nameRef).token,
	// nameRef.sourceStart);
	// } else {
	// // it is a QualifiedNameReference
	// requestor.acceptTypeReference(((QualifiedNameReference) nameRef).tokens,
	// nameRef.sourceStart, nameRef.sourceEnd);
	// }
	// }
	// }
	// }
	/*
	 * Update the bodyStart of the corresponding parse node
	 */
	public void notifySourceElementRequestor(
			AbstractMethodDeclaration methodDeclaration) {

		// range check
		boolean isInRange = scanner.initialPosition <= methodDeclaration.declarationSourceStart
				&& scanner.eofPosition >= methodDeclaration.declarationSourceEnd;

		if (methodDeclaration.isClinit()) {
			this.visitIfNeeded(methodDeclaration);
			return;
		}

		if (methodDeclaration.isDefaultConstructor()) {
			if (reportReferenceInfo) {
				ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) methodDeclaration;
				ExplicitConstructorCall constructorCall = constructorDeclaration.constructorCall;
				if (constructorCall != null) {
					switch (constructorCall.accessMode) {
					case ExplicitConstructorCall.This:
						requestor.acceptConstructorReference(
								typeNames[nestedTypeIndex - 1],
								constructorCall.arguments == null ? 0
										: constructorCall.arguments.length,
								constructorCall.sourceStart);
						break;
					case ExplicitConstructorCall.Super:
					case ExplicitConstructorCall.ImplicitSuper:
						requestor.acceptConstructorReference(
								superTypeNames[nestedTypeIndex - 1],
								constructorCall.arguments == null ? 0
										: constructorCall.arguments.length,
								constructorCall.sourceStart);
						break;
					}
				}
			}
			return;
		}
		char[][] argumentTypes = null;
		char[][] argumentNames = null;
		Argument[] arguments = methodDeclaration.arguments;
		if (arguments != null) {
			int argumentLength = arguments.length;
			argumentTypes = new char[argumentLength][];
			argumentNames = new char[argumentLength][];
			for (int i = 0; i < argumentLength; i++) {
				argumentTypes[i] = returnTypeName(arguments[i].type);
				argumentNames[i] = arguments[i].name;
			}
		}
		char[][] thrownExceptionTypes = null;
		TypeReference[] thrownExceptions = methodDeclaration.thrownExceptions;
		if (thrownExceptions != null) {
			int thrownExceptionLength = thrownExceptions.length;
			thrownExceptionTypes = new char[thrownExceptionLength][];
			for (int i = 0; i < thrownExceptionLength; i++) {
				thrownExceptionTypes[i] = CharOperation.concatWith(
						thrownExceptions[i].getTypeName(), '.');
			}
		}
		// by default no selector end position
		int selectorSourceEnd = -1;
		if (methodDeclaration.isConstructor()) {
			// if (methodDeclaration instanceof SourceConstructorDeclaration) {
			// selectorSourceEnd =
			// ((SourceConstructorDeclaration)
			// methodDeclaration).selectorSourceEnd;
			// }
			if (isInRange) {
				requestor.enterConstructor(
						methodDeclaration.declarationSourceStart,
						methodDeclaration.modifiers,
						methodDeclaration.selector,
						methodDeclaration.sourceStart, selectorSourceEnd,
						argumentTypes, argumentNames, thrownExceptionTypes);
			}
			if (reportReferenceInfo) {
				ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) methodDeclaration;
				ExplicitConstructorCall constructorCall = constructorDeclaration.constructorCall;
				if (constructorCall != null) {
					switch (constructorCall.accessMode) {
					case ExplicitConstructorCall.This:
						requestor.acceptConstructorReference(
								typeNames[nestedTypeIndex - 1],
								constructorCall.arguments == null ? 0
										: constructorCall.arguments.length,
								constructorCall.sourceStart);
						break;
					case ExplicitConstructorCall.Super:
					case ExplicitConstructorCall.ImplicitSuper:
						requestor.acceptConstructorReference(
								superTypeNames[nestedTypeIndex - 1],
								constructorCall.arguments == null ? 0
										: constructorCall.arguments.length,
								constructorCall.sourceStart);
						break;
					}
				}
			}
			this.visitIfNeeded(methodDeclaration);
			if (isInRange) {
				requestor
						.exitConstructor(methodDeclaration.declarationSourceEnd);
			}
			return;
		}
		// if (methodDeclaration instanceof SourceMethodDeclaration) {
		// selectorSourceEnd =
		// ((SourceMethodDeclaration) methodDeclaration).selectorSourceEnd;
		// }
		if (isInRange) {
			int modifiers = methodDeclaration.modifiers;
			// boolean deprecated = (modifiers & AccDeprecated) != 0; //
			// remember
			// deprecation so as to not lose it below
			requestor
					.enterMethod(
							methodDeclaration.declarationSourceStart,
							modifiers, // deprecated
							// ?
							// (modifiers
							// &
							// AccJustFlag)
							// |
							// AccDeprecated
							// :
							// modifiers
							// &
							// AccJustFlag,
							returnTypeName(((MethodDeclaration) methodDeclaration).returnType),
							methodDeclaration.selector,
							methodDeclaration.sourceStart, selectorSourceEnd,
							argumentTypes, argumentNames, thrownExceptionTypes);
		}
		this.visitIfNeeded(methodDeclaration);

		if (isInRange) {
			requestor.exitMethod(methodDeclaration.declarationSourceEnd);
		}
	}

	/*
	 * Update the bodyStart of the corresponding parse node
	 */
	public void notifySourceElementRequestor(FieldDeclaration fieldDeclaration) {

		// range check
		boolean isInRange = scanner.initialPosition <= fieldDeclaration.declarationSourceStart
				&& scanner.eofPosition >= fieldDeclaration.declarationSourceEnd;

		if (fieldDeclaration.isField()) {
			int fieldEndPosition = fieldDeclaration.declarationSourceEnd;
			// if (fieldDeclaration instanceof SourceFieldDeclaration) {
			// fieldEndPosition = ((SourceFieldDeclaration)
			// fieldDeclaration).fieldEndPosition;
			// if (fieldEndPosition == 0) {
			// // use the declaration source end by default
			// fieldEndPosition = fieldDeclaration.declarationSourceEnd;
			// }
			// }
			if (isInRange) {
				int modifiers = fieldDeclaration.modifiers;
				boolean deprecated = (modifiers & AccDeprecated) != 0; // remember
				// deprecation so
				// as to not lose
				// it below
				requestor.enterField(fieldDeclaration.declarationSourceStart,
						deprecated ? (modifiers & AccJustFlag) | AccDeprecated
								: modifiers & AccJustFlag,
						returnTypeName(fieldDeclaration.type),
						fieldDeclaration.name, fieldDeclaration.sourceStart,
						fieldDeclaration.sourceEnd);
			}
			// this.visitIfNeeded(fieldDeclaration);
			if (isInRange) {
				// requestor.exitField(
				// // filter out initializations that are not a constant (simple
				// check)
				// (fieldDeclaration.initialization == null
				// || fieldDeclaration.initialization instanceof
				// ArrayInitializer
				// || fieldDeclaration.initialization instanceof
				// AllocationExpression
				// || fieldDeclaration.initialization instanceof
				// ArrayAllocationExpression
				// || fieldDeclaration.initialization instanceof Assignment
				// || fieldDeclaration.initialization instanceof
				// ClassLiteralAccess
				// || fieldDeclaration.initialization instanceof MessageSend
				// || fieldDeclaration.initialization instanceof ArrayReference
				// || fieldDeclaration.initialization instanceof ThisReference)
				// ?
				// -1 :
				// fieldDeclaration.initialization.sourceStart,
				// fieldEndPosition,
				// fieldDeclaration.declarationSourceEnd);
				requestor.exitField(
						// filter out initializations that are not a constant
						// (simple check)
						-1, fieldEndPosition,
						fieldDeclaration.declarationSourceEnd);
			}

		} else {
			// if (isInRange){
			// requestor.enterInitializer(
			// fieldDeclaration.declarationSourceStart,
			// fieldDeclaration.modifiers);
			// }
			// this.visitIfNeeded((Initializer)fieldDeclaration);
			// if (isInRange){
			// requestor.exitInitializer(fieldDeclaration.declarationSourceEnd);
			// }
		}
	}

	public void notifySourceElementRequestor(ImportReference importReference,
			boolean isPackage) {
		// if (isPackage) {
		// requestor.acceptPackage(
		// importReference.declarationSourceStart,
		// importReference.declarationSourceEnd,
		// CharOperation.concatWith(importReference.getImportName(), '.'));
		// } else {
		requestor.acceptImport(importReference.declarationSourceStart,
				importReference.declarationSourceEnd, importReference
						.getIncludeName(), // CharOperation.concatWith(importReference.getImportName(),
											// '.'),
				importReference.onDemand);
		// }
	}

	public void notifySourceElementRequestor(TypeDeclaration typeDeclaration,
			boolean notifyTypePresence) {
		// // public void notifySourceElementRequestor(ASTNode typeDeclaration,
		// boolean notifyTypePresence) {

		// range check
		boolean isInRange = scanner.initialPosition <= typeDeclaration.declarationSourceStart
				&& scanner.eofPosition >= typeDeclaration.declarationSourceEnd;

		FieldDeclaration[] fields = typeDeclaration.fields;
		AbstractMethodDeclaration[] methods = typeDeclaration.methods;
		TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
		int fieldCount = fields == null ? 0 : fields.length;
		int methodCount = methods == null ? 0 : methods.length;
		int memberTypeCount = memberTypes == null ? 0 : memberTypes.length;
		int fieldIndex = 0;
		int methodIndex = 0;
		int memberTypeIndex = 0;
		boolean isInterface = typeDeclaration.isInterface();

		if (notifyTypePresence) {
			char[][] interfaceNames = null;
			int superInterfacesLength = 0;
			TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
			if (superInterfaces != null) {
				superInterfacesLength = superInterfaces.length;
				interfaceNames = new char[superInterfacesLength][];
			} else {
				if (typeDeclaration instanceof AnonymousLocalTypeDeclaration) {
					// see PR 3442
					QualifiedAllocationExpression alloc = ((AnonymousLocalTypeDeclaration) typeDeclaration).allocation;
					if (alloc != null && alloc.type != null) {
						superInterfaces = new TypeReference[] { ((AnonymousLocalTypeDeclaration) typeDeclaration).allocation.type };
						superInterfacesLength = 1;
						interfaceNames = new char[1][];
					}
				}
			}
			if (superInterfaces != null) {
				for (int i = 0; i < superInterfacesLength; i++) {
					interfaceNames[i] = CharOperation.concatWith(
							superInterfaces[i].getTypeName(), '.');
				}
			}
			if (isInterface) {
				if (isInRange) {
					int modifiers = typeDeclaration.modifiers;
					boolean deprecated = false; // (modifiers & AccDeprecated)
												// != 0; //
					// remember deprecation so as to not lose
					// it below
					requestor.enterInterface(
							typeDeclaration.declarationSourceStart,
							modifiers, // deprecated
							// ?
							// (modifiers
							// &
							// AccJustFlag)
							// |
							// AccDeprecated
							// :
							// modifiers
							// &
							// AccJustFlag,
							typeDeclaration.name, typeDeclaration.sourceStart,
							typeDeclaration.sourceEnd, interfaceNames);
				}
				if (nestedTypeIndex == typeNames.length) {
					// need a resize
					System.arraycopy(typeNames, 0,
							(typeNames = new char[nestedTypeIndex * 2][]), 0,
							nestedTypeIndex);
					System.arraycopy(superTypeNames, 0,
							(superTypeNames = new char[nestedTypeIndex * 2][]),
							0, nestedTypeIndex);
				}
				typeNames[nestedTypeIndex] = typeDeclaration.name;
				superTypeNames[nestedTypeIndex++] = JAVA_LANG_OBJECT;
			} else {
				TypeReference superclass = typeDeclaration.superclass;
				if (superclass == null) {
					if (isInRange) {
						requestor
								.enterClass(
										typeDeclaration.declarationSourceStart,
										typeDeclaration.modifiers,
										typeDeclaration.name,
										typeDeclaration.sourceStart,
										typeDeclaration.sourceEnd, null,
										interfaceNames);
					}
				} else {
					if (isInRange) {
						requestor.enterClass(
								typeDeclaration.declarationSourceStart,
								typeDeclaration.modifiers,
								typeDeclaration.name,
								typeDeclaration.sourceStart,
								typeDeclaration.sourceEnd, CharOperation
										.concatWith(superclass.getTypeName(),
												'.'), interfaceNames);
					}
				}
				if (nestedTypeIndex == typeNames.length) {
					// need a resize
					System.arraycopy(typeNames, 0,
							(typeNames = new char[nestedTypeIndex * 2][]), 0,
							nestedTypeIndex);
					System.arraycopy(superTypeNames, 0,
							(superTypeNames = new char[nestedTypeIndex * 2][]),
							0, nestedTypeIndex);
				}
				typeNames[nestedTypeIndex] = typeDeclaration.name;
				superTypeNames[nestedTypeIndex++] = superclass == null ? JAVA_LANG_OBJECT
						: CharOperation.concatWith(superclass.getTypeName(),
								'.');
			}
		}
		while ((fieldIndex < fieldCount) || (memberTypeIndex < memberTypeCount)
				|| (methodIndex < methodCount)) {
			FieldDeclaration nextFieldDeclaration = null;
			AbstractMethodDeclaration nextMethodDeclaration = null;
			TypeDeclaration nextMemberDeclaration = null;

			int position = Integer.MAX_VALUE;
			int nextDeclarationType = -1;
			if (fieldIndex < fieldCount) {
				nextFieldDeclaration = fields[fieldIndex];
				if (nextFieldDeclaration.declarationSourceStart < position) {
					position = nextFieldDeclaration.declarationSourceStart;
					nextDeclarationType = 0; // FIELD
				}
			}
			if (methodIndex < methodCount) {
				nextMethodDeclaration = methods[methodIndex];
				if (nextMethodDeclaration.declarationSourceStart < position) {
					position = nextMethodDeclaration.declarationSourceStart;
					nextDeclarationType = 1; // METHOD
				}
			}
			if (memberTypeIndex < memberTypeCount) {
				nextMemberDeclaration = memberTypes[memberTypeIndex];
				if (nextMemberDeclaration.declarationSourceStart < position) {
					position = nextMemberDeclaration.declarationSourceStart;
					nextDeclarationType = 2; // MEMBER
				}
			}
			switch (nextDeclarationType) {
			case 0:
				fieldIndex++;
				notifySourceElementRequestor(nextFieldDeclaration);
				break;
			case 1:
				methodIndex++;
				notifySourceElementRequestor(nextMethodDeclaration);
				break;
			case 2:
				memberTypeIndex++;
				notifySourceElementRequestor(nextMemberDeclaration, true);
			}
		}
		if (notifyTypePresence) {
			if (isInRange) {
				if (isInterface) {
					requestor
							.exitInterface(typeDeclaration.declarationSourceEnd);
				} else {
					requestor.exitClass(typeDeclaration.declarationSourceEnd);
				}
			}
			nestedTypeIndex--;
		}
	}

	public void parseCompilationUnit(ICompilationUnit unit, int start, int end) {
		// boolean needReferenceInfo) {

		// reportReferenceInfo = needReferenceInfo;
		// boolean old = diet;
		// if (needReferenceInfo) {
		// unknownRefs = new NameReference[10];
		// unknownRefsCounter = 0;
		// }

		try {
			// diet = true;
			CompilationResult compilationUnitResult = new CompilationResult(
					unit, 0, 0, 10); // this.options.maxProblemsPerUnit);
			CompilationUnitDeclaration parsedUnit = parse(unit,
					compilationUnitResult, start, end);
			// if (scanner.recordLineSeparator) {
			// requestor.acceptLineSeparatorPositions(scanner.getLineEnds());
			// }
			// if (this.localDeclarationVisitor != null || needReferenceInfo){
			// diet = false;
			// this.getMethodBodies(parsedUnit);
			// }
			// this.scanner.resetTo(start, end);
			// notifySourceElementRequestor(parsedUnit);
		} catch (AbortCompilation e) {
		} finally {
			// diet = old;
		}
	}

	public CompilationUnitDeclaration parseCompilationUnit(
			ICompilationUnit unit, boolean fullParse) {

		// boolean old = diet;
		// if (fullParse) {
		// unknownRefs = new NameReference[10];
		// unknownRefsCounter = 0;
		// }

		try {
			// diet = true;
			this.reportReferenceInfo = fullParse;
			CompilationResult compilationUnitResult = new CompilationResult(
					unit, 0, 0, this.options.maxProblemsPerUnit);
			CompilationUnitDeclaration parsedUnit = parse(unit,
					compilationUnitResult, false);
			if (scanner.recordLineSeparator) {
				requestor.acceptLineSeparatorPositions(scanner.getLineEnds());
			}
			int initialStart = this.scanner.initialPosition;
			int initialEnd = this.scanner.eofPosition;
			// if (this.localDeclarationVisitor != null || fullParse){
			// diet = false;
			// this.getMethodBodies(parsedUnit);
			// }
			this.scanner.resetTo(initialStart, initialEnd);
			notifySourceElementRequestor(parsedUnit);
			return parsedUnit;
		} catch (AbortCompilation e) {
			// ignore this exception
		} finally {
			// diet = old;
		}
		return null;
	}

	public CompilationUnitDeclaration parseCompletionUnit(
			ICompilationUnit unit, boolean fullParse) {

		// boolean old = diet;
		// if (fullParse) {
		// unknownRefs = new NameReference[10];
		// unknownRefsCounter = 0;
		// }

		try {
			// diet = true;
			this.reportReferenceInfo = fullParse;
			CompilationResult compilationUnitResult = new CompilationResult(
					unit, 0, 0, this.options.maxProblemsPerUnit);
			CompilationUnitDeclaration parsedUnit = parse(unit,
					compilationUnitResult, false);
			// if (scanner.recordLineSeparator) {
			// requestor.acceptLineSeparatorPositions(scanner.getLineEnds());
			// }
			// int initialStart = this.scanner.initialPosition;
			// int initialEnd = this.scanner.eofPosition;
			// // if (this.localDeclarationVisitor != null || fullParse){
			// // diet = false;
			// // this.getMethodBodies(parsedUnit);
			// // }
			// this.scanner.resetTo(initialStart, initialEnd);
			// notifySourceElementRequestor(parsedUnit);
			return parsedUnit;
		} catch (AbortCompilation e) {
			// ignore this exception
		} finally {
			// diet = old;
		}
		return null;
	}

	// public void parseTypeMemberDeclarations(
	// ISourceType sourceType,
	// ICompilationUnit sourceUnit,
	// int start,
	// int end,
	// boolean needReferenceInfo) {
	// boolean old = diet;
	// if (needReferenceInfo) {
	// unknownRefs = new NameReference[10];
	// unknownRefsCounter = 0;
	// }
	//	
	// try {
	// diet = !needReferenceInfo;
	// reportReferenceInfo = needReferenceInfo;
	// CompilationResult compilationUnitResult =
	// new CompilationResult(sourceUnit, 0, 0, this.options.maxProblemsPerUnit);
	// CompilationUnitDeclaration unit =
	// SourceTypeConverter.buildCompilationUnit(
	// new ISourceType[]{sourceType},
	// false, // no need for field and methods
	// false, // no need for member types
	// false, // no need for field initialization
	// problemReporter(),
	// compilationUnitResult);
	// if ((unit == null) || (unit.types == null) || (unit.types.length != 1))
	// return;
	// this.sourceType = sourceType;
	// try {
	// /* automaton initialization */
	// initialize();
	// goForClassBodyDeclarations();
	// /* scanner initialization */
	// scanner.setSource(sourceUnit.getContents());
	// scanner.resetTo(start, end);
	// /* unit creation */
	// referenceContext = compilationUnit = unit;
	// /* initialize the astStacl */
	// // the compilationUnitDeclaration should contain exactly one type
	// pushOnAstStack(unit.types[0]);
	// /* run automaton */
	// parse();
	// notifySourceElementRequestor(unit);
	// } finally {
	// unit = compilationUnit;
	// compilationUnit = null; // reset parser
	// }
	// } catch (AbortCompilation e) {
	// } finally {
	// if (scanner.recordLineSeparator) {
	// requestor.acceptLineSeparatorPositions(scanner.getLineEnds());
	// }
	// diet = old;
	// }
	// }
	//
	// public void parseTypeMemberDeclarations(
	// char[] contents,
	// int start,
	// int end) {
	//
	// boolean old = diet;
	//	
	// try {
	// diet = true;
	//
	// /* automaton initialization */
	// initialize();
	// goForClassBodyDeclarations();
	// /* scanner initialization */
	// scanner.setSource(contents);
	// scanner.recordLineSeparator = false;
	// scanner.taskTags = null;
	// scanner.taskPriorities = null;
	// scanner.resetTo(start, end);
	//
	// /* unit creation */
	// referenceContext = null;
	//
	// /* initialize the astStacl */
	// // the compilationUnitDeclaration should contain exactly one type
	// /* run automaton */
	// parse();
	// notifySourceElementRequestor((CompilationUnitDeclaration)null);
	// } catch (AbortCompilation e) {
	// } finally {
	// diet = old;
	// }
	// }
	/**
	 * Sort the given ast nodes by their positions.
	 */
	private static void quickSort(ASTNode[] sortedCollection, int left,
			int right) {
		int original_left = left;
		int original_right = right;
		ASTNode mid = sortedCollection[(left + right) / 2];
		do {
			while (sortedCollection[left].sourceStart < mid.sourceStart) {
				left++;
			}
			while (mid.sourceStart < sortedCollection[right].sourceStart) {
				right--;
			}
			if (left <= right) {
				ASTNode tmp = sortedCollection[left];
				sortedCollection[left] = sortedCollection[right];
				sortedCollection[right] = tmp;
				left++;
				right--;
			}
		} while (left <= right);
		if (original_left < right) {
			quickSort(sortedCollection, original_left, right);
		}
		if (left < original_right) {
			quickSort(sortedCollection, left, original_right);
		}
	}

	/*
	 * Answer a char array representation of the type name formatted like: -
	 * type name + dimensions Example: "A[][]".toCharArray()
	 * "java.lang.String".toCharArray()
	 */
	private char[] returnTypeName(TypeReference type) {
		if (type == null)
			return null;
		int dimension = type.dimensions();
		if (dimension != 0) {
			char[] dimensionsArray = new char[dimension * 2];
			for (int i = 0; i < dimension; i++) {
				dimensionsArray[i * 2] = '[';
				dimensionsArray[(i * 2) + 1] = ']';
			}
			return CharOperation.concat(CharOperation.concatWith(type
					.getTypeName(), '.'), dimensionsArray);
		}
		return CharOperation.concatWith(type.getTypeName(), '.');
	}

	public void addUnknownRef(NameReference nameRef) {
		if (this.unknownRefs.length == this.unknownRefsCounter) {
			// resize
			System
					.arraycopy(
							this.unknownRefs,
							0,
							(this.unknownRefs = new NameReference[this.unknownRefsCounter * 2]),
							0, this.unknownRefsCounter);
		}
		this.unknownRefs[this.unknownRefsCounter++] = nameRef;
	}

	private void visitIfNeeded(AbstractMethodDeclaration method) {
		if (this.localDeclarationVisitor != null
				&& (method.bits & ASTNode.HasLocalTypeMASK) != 0) {
			if (method.statements != null) {
				int statementsLength = method.statements.length;
				for (int i = 0; i < statementsLength; i++)
					method.statements[i].traverse(this.localDeclarationVisitor,
							method.scope);
			}
		}
	}

	// private void visitIfNeeded(FieldDeclaration field) {
	// if (this.localDeclarationVisitor != null
	// && (field.bits & ASTNode.HasLocalTypeMASK) != 0) {
	// if (field.initialization != null) {
	// field.initialization.traverse(this.localDeclarationVisitor, null);
	// }
	// }
	// }
	//
	// private void visitIfNeeded(Initializer initializer) {
	// if (this.localDeclarationVisitor != null
	// && (initializer.bits & ASTNode.HasLocalTypeMASK) != 0) {
	// if (initializer.block != null) {
	// initializer.block.traverse(this.localDeclarationVisitor, null);
	// }
	// }
	// }
	//
	// protected void reportSyntaxError(int act, int currentKind, int
	// stateStackTop) {
	// if (compilationUnit == null) return;
	// super.reportSyntaxError(act, currentKind,stateStackTop);
	// }
	protected CompilationUnitDeclaration endParse(int act) {
		// if (sourceType != null) {
		// if (sourceType.isInterface()) {
		// consumeInterfaceDeclaration();
		// } else {
		// consumeClassDeclaration();
		// }
		// }
		if (compilationUnit != null) {
			CompilationUnitDeclaration result = super.endParse(act);
			return result;
		} else {
			return null;
		}
	}
}