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

import java.util.Locale;
import java.util.Map;

import org.eclipse.jdt.core.CompletionContext;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;

import org.eclipse.jdt.internal.codeassist.complete.*;
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
import org.eclipse.jdt.internal.codeassist.impl.AssistParser;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.codeassist.impl.Keywords;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.core.BasicCompilationUnit;
import org.eclipse.jdt.internal.core.INamingRequestor;
import org.eclipse.jdt.internal.core.InternalNamingConventions;
import org.eclipse.jdt.internal.core.SourceMethod;
import org.eclipse.jdt.internal.core.SourceMethodElementInfo;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.BinaryTypeConverter;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;

/**
 * This class is the entry point for source completions.
 * It contains two public APIs used to call CodeAssist on a given source with
 * a given environment, assisting position and storage (and possibly options).
 */
public final class CompletionEngine
	extends Engine
	implements ISearchRequestor, TypeConstants , TerminalTokens , RelevanceConstants, SuffixConstants {
	
	public HashtableOfObject typeCache;
	
	public static boolean DEBUG = false;
	public static boolean PERF = false;
	
	public final static boolean NO_TYPE_COMPLETION_ON_EMPTY_TOKEN = true;
	
	private final static char[] ERROR_PATTERN = "*error*".toCharArray();  //$NON-NLS-1$
	private final static char[] EXCEPTION_PATTERN = "*exception*".toCharArray();  //$NON-NLS-1$
	private final static char[] SEMICOLON = new char[] { ';' };
	
	private final static char[] CLASS = "Class".toCharArray();  //$NON-NLS-1$
	private final static char[] VOID = "void".toCharArray();  //$NON-NLS-1$
	private final static char[] INT = "int".toCharArray();  //$NON-NLS-1$
	private final static char[] INT_SIGNATURE = new char[]{Signature.C_INT};
	private final static char[] VALUE = "value".toCharArray();  //$NON-NLS-1$
	
	private final static int SUPERTYPE = 1;
	private final static int SUBTYPE = 2;
	
	private final static int FIELD = 0;
	private final static int LOCAL = 1;
	private final static int ARGUMENT = 2;
	
	int expectedTypesPtr = -1;
	TypeBinding[] expectedTypes = new TypeBinding[1];
	int expectedTypesFilter;
	int uninterestingBindingsPtr = -1;
	Binding[] uninterestingBindings = new Binding[1];
	int forbbidenBindingsPtr = -1;
	Binding[] forbbidenBindings = new Binding[1];
	
	boolean assistNodeIsClass;
	boolean assistNodeIsEnum;
	boolean assistNodeIsException;
	boolean assistNodeIsInterface;
	boolean assistNodeIsAnnotation;
	
	IJavaProject javaProject;
	CompletionParser parser;
	CompletionRequestor requestor;
	ProblemReporter problemReporter;
	char[] source;
	char[] completionToken;
	char[] qualifiedCompletionToken;
	boolean resolvingImports = false;
	boolean insideQualifiedReference = false;
	boolean noProposal = true;
	IProblem problem = null;
	int startPosition, actualCompletionPosition, endPosition, offset;
	HashtableOfObject knownPkgs = new HashtableOfObject(10);
	HashtableOfObject knownTypes = new HashtableOfObject(10);
	Scanner nameScanner;

	/*
		static final char[][] mainDeclarations =
			new char[][] {
				"package".toCharArray(),
				"import".toCharArray(),
				"abstract".toCharArray(),
				"final".toCharArray(),
				"public".toCharArray(),
				"class".toCharArray(),
				"interface".toCharArray()};
	
		static final char[][] modifiers = // may want field, method, type & member type modifiers
			new char[][] {
				"abstract".toCharArray(),
				"final".toCharArray(),
				"native".toCharArray(),
				"public".toCharArray(),
				"protected".toCharArray(),
				"private".toCharArray(),
				"static".toCharArray(),
				"strictfp".toCharArray(),
				"synchronized".toCharArray(),
				"transient".toCharArray(),
				"volatile".toCharArray()};
	*/
	static final char[][] baseTypes = new char[][] { 
		"boolean".toCharArray(), //$NON-NLS-1$
		"byte".toCharArray(), //$NON-NLS-1$
		"char".toCharArray(), //$NON-NLS-1$
		"double".toCharArray(), //$NON-NLS-1$
		"float".toCharArray(), //$NON-NLS-1$
		"int".toCharArray(), //$NON-NLS-1$
		"long".toCharArray(), //$NON-NLS-1$
		"short".toCharArray(), //$NON-NLS-1$
		"void".toCharArray(), //$NON-NLS-1$
	};
		
	static final char[] classField = "class".toCharArray();  //$NON-NLS-1$
	static final char[] lengthField = "length".toCharArray();  //$NON-NLS-1$
	static final char[] cloneMethod = "clone".toCharArray();  //$NON-NLS-1$
	static final char[] THIS = "this".toCharArray();  //$NON-NLS-1$
	static final char[] THROWS = "throws".toCharArray();  //$NON-NLS-1$
	
	static InvocationSite FakeInvocationSite = new InvocationSite(){
		public TypeBinding[] genericTypeArguments() { return null; }
		public boolean isSuperAccess(){ return false; }
		public boolean isTypeAccess(){ return false; }
		public void setActualReceiverType(ReferenceBinding receiverType) {}
		public void setDepth(int depth){}
		public void setFieldIndex(int depth){}
		public int sourceStart() { return 0; 	}
		public int sourceEnd() { return 0; 	}
	};

	/**
	 * The CompletionEngine is responsible for computing source completions.
	 *
	 * It requires a searchable name environment, which supports some
	 * specific search APIs, and a requestor to feed back the results to a UI.
	 *
	 *  @param nameEnvironment org.eclipse.jdt.internal.codeassist.ISearchableNameEnvironment
	 *      used to resolve type/package references and search for types/packages
	 *      based on partial names.
	 *
	 *  @param requestor org.eclipse.jdt.internal.codeassist.ICompletionRequestor
	 *      since the engine might produce answers of various forms, the engine 
	 *      is associated with a requestor able to accept all possible completions.
	 *
	 *  @param settings java.util.Map
	 *		set of options used to configure the code assist engine.
	 */
	public CompletionEngine(
			SearchableEnvironment nameEnvironment,
			CompletionRequestor requestor,
			Map settings,
			IJavaProject javaProject) {
		super(settings);
		this.javaProject = javaProject;
		this.requestor = requestor;
		this.nameEnvironment = nameEnvironment;
		this.typeCache = new HashtableOfObject(5);

		this.problemReporter = new ProblemReporter(
				DefaultErrorHandlingPolicies.proceedWithAllProblems(),
				this.compilerOptions,
				new DefaultProblemFactory(Locale.getDefault()) {
					int lastErrorStart;
					
					public IProblem createProblem(
						char[] originatingFileName,
						int problemId,
						String[] problemArguments,
						String[] messageArguments,
						int severity,
						int start,
						int end,
						int lineNumber) {
						
						IProblem pb = super.createProblem(
							originatingFileName,
							problemId,
							problemArguments,
							messageArguments,
							severity,
							start,
							end,
							lineNumber);
						// TODO (david) problems could be detected in other units which got requested (see CompilationUnitProblemFinder)
						if(CompletionEngine.this.actualCompletionPosition > start
							&& this.lastErrorStart < start
							&& pb.isError()
							&& (pb.getID() & IProblem.Syntax) == 0) {
								
							CompletionEngine.this.problem = pb;
							this.lastErrorStart = start;
						}
						
						return pb;
					}

				});
		this.lookupEnvironment =
			new LookupEnvironment(this, this.compilerOptions, this.problemReporter, nameEnvironment);
		this.parser =
			new CompletionParser(this.problemReporter);
		this.nameScanner =
			new Scanner(
				false /*comment*/, 
				false /*whitespace*/, 
				false /*nls*/, 
				this.compilerOptions.sourceLevel, 
				null /*taskTags*/, 
				null/*taskPriorities*/,
				true/*taskCaseSensitive*/);
	}

	/**
	 * One result of the search consists of a new type.
	 *
	 * NOTE - All package and type names are presented in their readable form:
	 *    Package names are in the form "a.b.c".
	 *    Nested type names are in the qualified form "A.I".
	 *    The default package is represented by an empty array.
	 */
	public void acceptType(
		char[] packageName,
		char[] typeName,
		int modifiers,
		AccessRestriction accessRestriction) {

		char[] fullyQualifiedName = CharOperation.concat(packageName, typeName, '.');
		char[] completionName = fullyQualifiedName;

		if (this.knownTypes.containsKey(completionName)) return;

		this.knownTypes.put(completionName, this);

		int accessibility = IAccessRule.K_ACCESSIBLE;
		if(accessRestriction != null) {
			switch (accessRestriction.getProblemId()) {
				case IProblem.ForbiddenReference:
					switch (this.options.restrictedReferenceFilter) {
						case AssistOptions.FILTER_WARNING:
							return;
						case AssistOptions.FILTER_ERROR:
							if(this.forbiddenReferenceIsError) return;
							break;
						
					}
					accessibility = IAccessRule.K_NON_ACCESSIBLE;
					break;
				case IProblem.DiscouragedReference:
					switch (this.options.restrictedReferenceFilter) {
						case AssistOptions.FILTER_WARNING:
							return;
						case AssistOptions.FILTER_ERROR:
							if(this.discouragedReferenceIsError) return;
							break;
						
					}
					accessibility = IAccessRule.K_DISCOURAGED;
					break;
			}
		}
		
		boolean isQualified = true;
		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForRestrictions(accessibility);
		if (this.resolvingImports) {
			completionName = CharOperation.concat(completionName, new char[] { ';' });
			relevance += computeRelevanceForCaseMatching(this.completionToken, fullyQualifiedName);
		} else {
			if (mustQualifyType(packageName, typeName)) {
				if (packageName == null || packageName.length == 0)
					if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
						return; // ignore types from the default package from outside it
			} else {
				completionName = typeName;
				isQualified = false;
			}
			relevance += computeRelevanceForCaseMatching(this.completionToken, typeName);
			relevance += computeRelevanceForExpectingType(packageName, typeName);
			relevance += computeRelevanceForQualification(isQualified);
			
			int kind = modifiers & (IConstants.AccInterface | IConstants.AccEnum | IConstants.AccAnnotation);
			switch (kind) {
				case IConstants.AccAnnotation:
				case IConstants.AccAnnotation | IConstants.AccInterface:
					relevance += computeRelevanceForAnnotation();
					relevance += computeRelevanceForInterface();
					break;
				case IConstants.AccEnum:
					relevance += computeRelevanceForEnum();
					break;
				case IConstants.AccInterface:
					relevance += computeRelevanceForInterface();
					break;
				default:
					relevance += computeRelevanceForClass();
					relevance += computeRelevanceForException(typeName);
					break;
			}
		}
		
		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
			proposal.setDeclarationSignature(packageName);
			proposal.setSignature(createNonGenericTypeSignature(packageName, typeName));
			proposal.setPackageName(packageName);
			proposal.setTypeName(typeName);
			proposal.setCompletion(completionName);
			proposal.setFlags(modifiers);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setRelevance(relevance);
			proposal.setAccessibility(accessibility);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}

	/**
	 * One result of the search consists of a new package.
	 *
	 * NOTE - All package names are presented in their readable form:
	 *    Package names are in the form "a.b.c".
	 *    The default package is represented by an empty array.
	 */
	public void acceptPackage(char[] packageName) {

		if (this.knownPkgs.containsKey(packageName)) return;

		this.knownPkgs.put(packageName, this);
		
		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForCaseMatching(this.qualifiedCompletionToken == null ? this.completionToken : this.qualifiedCompletionToken, packageName);
		relevance += computeRelevanceForQualification(true);
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
		
		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			CompletionProposal proposal = this.createProposal(CompletionProposal.PACKAGE_REF, this.actualCompletionPosition);
			proposal.setDeclarationSignature(packageName);
			proposal.setPackageName(packageName);
			proposal.setCompletion(
					this.resolvingImports
					? CharOperation.concat(packageName, new char[] { '.', '*', ';' })
					: packageName);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}
		
	private void buildContext() {
		CompletionContext context = new CompletionContext();
		
		// build expected types context
		if (this.expectedTypesPtr > -1) {
			int length = this.expectedTypesPtr + 1;
			char[][] expTypes = new char[length][];
			char[][] expKeys = new char[length][];
			for (int i = 0; i < length; i++) {
				expTypes[i] = getSignature(this.expectedTypes[i]);
				expKeys[i] = this.expectedTypes[i].computeUniqueKey();
			}
			context.setExpectedTypesSignatures(expTypes);
			context.setExpectedTypesKeys(expKeys);
		}
		
		this.requestor.acceptContext(context);
	}
	
	private void complete(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope, boolean insideTypeAnnotation) {

		setSourceRange(astNode.sourceStart, astNode.sourceEnd);
		
		scope = computeForbiddenBindings(astNode, astNodeParent, scope);
		computeUninterestingBindings(astNodeParent, scope);
		if(astNodeParent != null) {
			if(!isValidParent(astNodeParent, astNode, scope)) return;
			computeExpectedTypes(astNodeParent, astNode, scope);
		}
		
		buildContext();
		
		if (astNode instanceof CompletionOnFieldType) {

			CompletionOnFieldType field = (CompletionOnFieldType) astNode;
			CompletionOnSingleTypeReference type = (CompletionOnSingleTypeReference) field.type;
			this.completionToken = type.token;
			setSourceRange(type.sourceStart, type.sourceEnd);
			
			findTypesAndPackages(this.completionToken, scope);
			if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
				findKeywordsForMember(this.completionToken, field.modifiers);
			}
			
			if(!field.isLocalVariable && field.modifiers == CompilerModifiers.AccDefault) {
				if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
					findMethods(this.completionToken,null,scope.enclosingSourceType(),scope,new ObjectVector(),false,false,true,null,null,false,false,true);
				}
				if(!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
					proposeNewMethod(this.completionToken, scope.enclosingSourceType());
				}
			}
		} else {
			if(astNode instanceof CompletionOnMethodReturnType) {
				
				CompletionOnMethodReturnType method = (CompletionOnMethodReturnType) astNode;
				SingleTypeReference type = (CompletionOnSingleTypeReference) method.returnType;
				this.completionToken = type.token;
				setSourceRange(type.sourceStart, type.sourceEnd);
				findTypesAndPackages(this.completionToken, scope.parent);
				if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
					findKeywordsForMember(this.completionToken, method.modifiers);
				}
			
				if(method.modifiers == CompilerModifiers.AccDefault) {
					if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
						findMethods(this.completionToken,null,scope.enclosingSourceType(),scope,new ObjectVector(),false,false,true,null,null,false,false,true);
					}
					if(!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
						proposeNewMethod(this.completionToken, scope.enclosingSourceType());
					}
				}
			} else {
				
				if (astNode instanceof CompletionOnSingleNameReference) {
					CompletionOnSingleNameReference singleNameReference = (CompletionOnSingleNameReference) astNode;
					this.completionToken = singleNameReference.token;
					SwitchStatement switchStatement = astNodeParent instanceof SwitchStatement ? (SwitchStatement) astNodeParent : null;
					if(switchStatement != null
							&& switchStatement.expression.resolvedType != null
							&& switchStatement.expression.resolvedType.isEnum()) {
						if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
							this.findEnumConstant(this.completionToken, (SwitchStatement) astNodeParent);
						}
					} else {
						if(this.expectedTypesPtr > -1 && this.expectedTypes[0].isAnnotationType()) {
							findTypesAndPackages(this.completionToken, scope);
						} else {
							findVariablesAndMethods(
								this.completionToken,
								scope,
								singleNameReference,
								scope,
								insideTypeAnnotation,
								singleNameReference.isInsideAnnotationAttribute);
							// can be the start of a qualified type name
							findTypesAndPackages(this.completionToken, scope);
							if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
								if(this.completionToken != null && this.completionToken.length != 0) {
									findKeywords(this.completionToken, singleNameReference.possibleKeywords);
								} else {
									findTrueOrFalseKeywords(singleNameReference.possibleKeywords);
								}
							}
							if(singleNameReference.canBeExplicitConstructor && !this.requestor.isIgnored(CompletionProposal.METHOD_REF)){
								if(CharOperation.prefixEquals(this.completionToken, Keywords.THIS, false)) {
									ReferenceBinding ref = scope.enclosingSourceType();
									findExplicitConstructors(Keywords.THIS, ref, (MethodScope)scope, singleNameReference);
								} else if(CharOperation.prefixEquals(this.completionToken, Keywords.SUPER, false)) {
									ReferenceBinding ref = scope.enclosingSourceType();
									findExplicitConstructors(Keywords.SUPER, ref.superclass(), (MethodScope)scope, singleNameReference);
								}
							}
						}
					}
				} else {
	
					if (astNode instanceof CompletionOnSingleTypeReference) {
	
						this.completionToken = ((CompletionOnSingleTypeReference) astNode).token;
						
						this.assistNodeIsClass = astNode instanceof CompletionOnClassReference;
						this.assistNodeIsEnum = this.assistNodeIsClass;
						this.assistNodeIsException = astNode instanceof CompletionOnExceptionReference;
						this.assistNodeIsInterface = astNode instanceof CompletionOnInterfaceReference;
	
						// can be the start of a qualified type name
						if (qualifiedBinding == null) {
							if(this.completionToken.length == 0 &&
									(astNodeParent instanceof ParameterizedSingleTypeReference ||
											astNodeParent instanceof ParameterizedQualifiedTypeReference)) {
								this.setSourceRange(astNode.sourceStart, astNode.sourceStart - 1, false);
								
								findParameterizedType((TypeReference)astNodeParent);
							} else { 
								findTypesAndPackages(this.completionToken, scope);
							}
						} else {
							if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
								findMemberTypes(
								this.completionToken,
								(ReferenceBinding) qualifiedBinding,
								scope,
								scope.enclosingSourceType(),
								false,
								new ObjectVector());
							}
						}
					} else {
						
						if (astNode instanceof CompletionOnQualifiedNameReference) {
	
							this.insideQualifiedReference = true;
							CompletionOnQualifiedNameReference ref =
								(CompletionOnQualifiedNameReference) astNode;
							this.completionToken = ref.completionIdentifier;
							long completionPosition = ref.sourcePositions[ref.sourcePositions.length - 1];
	
							if (qualifiedBinding instanceof VariableBinding) {
	
								setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
								TypeBinding receiverType = ((VariableBinding) qualifiedBinding).type;
								if (receiverType != null) {
									findFieldsAndMethods(this.completionToken, receiverType.capture(scope, ref.sourceEnd), scope, ref, scope,false,false);
								}
	
							} else {
	
								if (qualifiedBinding instanceof ReferenceBinding) {
									boolean isInsideAnnotationAttribute = ref.isInsideAnnotationAttribute;
									ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
									setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
	
									if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
										findMemberTypes(this.completionToken, receiverType, scope, scope.enclosingSourceType(), false, new ObjectVector());
									}
									if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
										findClassField(this.completionToken, (TypeBinding) qualifiedBinding, scope);
									}
									
									MethodScope methodScope = null;
									if(!isInsideAnnotationAttribute &&
											!this.requestor.isIgnored(CompletionProposal.KEYWORD) &&
											((scope instanceof MethodScope && !((MethodScope)scope).isStatic)
											|| ((methodScope = scope.enclosingMethodScope()) != null && !methodScope.isStatic))) {
										if(this.completionToken.length > 0) {
											findKeywords(this.completionToken, new char[][]{Keywords.THIS});
										} else {
											int relevance = computeBaseRelevance();
											relevance += computeRelevanceForInterestingProposal();
											relevance += computeRelevanceForCaseMatching(this.completionToken, Keywords.THIS);
											relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywords
											this.noProposal = false;
											if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
												CompletionProposal proposal = this.createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
												proposal.setName(Keywords.THIS);
												proposal.setCompletion(Keywords.THIS);
												proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
												proposal.setRelevance(relevance);
												this.requestor.accept(proposal);
												if(DEBUG) {
													this.printDebug(proposal);
												}
											}
										}
									}
	
									if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
										findFields(
											this.completionToken,
											receiverType,
											scope,
											new ObjectVector(),
											new ObjectVector(),
											true,
											ref,
											scope,
											false,
											true);
									}
	
									if(!isInsideAnnotationAttribute && !this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
										findMethods(
											this.completionToken,
											null,
											receiverType,
											scope,
											new ObjectVector(),
											true,
											false,
											false,
											ref,
											scope,
											false,
											false,
											true);
									}
	
								} else {
	
									if (qualifiedBinding instanceof PackageBinding) {
	
										setSourceRange(astNode.sourceStart, (int) completionPosition);
										// replace to the end of the completion identifier
										findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding);
									}
								}
							}
	
						} else {
	
								if (astNode instanceof CompletionOnQualifiedTypeReference) {
	
								this.insideQualifiedReference = true;
								
								this.assistNodeIsClass = astNode instanceof CompletionOnQualifiedClassReference;
								this.assistNodeIsEnum = this.assistNodeIsClass;
								this.assistNodeIsException = astNode instanceof CompletionOnQualifiedExceptionReference;
								this.assistNodeIsInterface = astNode instanceof CompletionOnQualifiedInterfaceReference;
								
								CompletionOnQualifiedTypeReference ref =
									(CompletionOnQualifiedTypeReference) astNode;
								this.completionToken = ref.completionIdentifier;
								long completionPosition = ref.sourcePositions[ref.tokens.length];
	
								// get the source positions of the completion identifier
								if (qualifiedBinding instanceof ReferenceBinding) {
									if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
										setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
										findMemberTypes(
											this.completionToken,
											(ReferenceBinding) qualifiedBinding,
											scope,
											scope.enclosingSourceType(),
											false,
											new ObjectVector());
									}
	
								} else {
	
									if (qualifiedBinding instanceof PackageBinding) {
	
										setSourceRange(astNode.sourceStart, (int) completionPosition);
										// replace to the end of the completion identifier
										findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding);
									}
								}
	
							} else {
	
								if (astNode instanceof CompletionOnMemberAccess) {
									this.insideQualifiedReference = true;
									CompletionOnMemberAccess access = (CompletionOnMemberAccess) astNode;
									long completionPosition = access.nameSourcePosition;
									setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
					
									this.completionToken = access.token;
									
									if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
										findKeywords(this.completionToken, new char[][]{Keywords.NEW});
									}
									
									findFieldsAndMethods(
										this.completionToken,
										((TypeBinding) qualifiedBinding).capture(scope, access.receiver.sourceEnd),
										scope,
										access,
										scope,
										false,
										access.receiver instanceof SuperReference);
	
								} else {
	
									if (astNode instanceof CompletionOnMessageSend) {
										setSourceRange(astNode.sourceStart, astNode.sourceEnd, false);
										
										CompletionOnMessageSend messageSend = (CompletionOnMessageSend) astNode;
										TypeBinding[] argTypes =
											computeTypes(messageSend.arguments, (BlockScope) scope);
										this.completionToken = messageSend.selector;
										if (qualifiedBinding == null) {
											if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
												findImplicitMessageSends(this.completionToken, argTypes, scope, messageSend, scope);
											}
										} else {
											if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
												findMethods(
													this.completionToken,
													argTypes,
													(ReferenceBinding)((ReferenceBinding) qualifiedBinding).capture(scope, messageSend.receiver.sourceEnd),
													scope,
													new ObjectVector(),
													false,
													true,
													false,
													messageSend,
													scope,
													false,
													messageSend.receiver instanceof SuperReference,
													true);
											}
										}
	
									} else {
	
										if (astNode instanceof CompletionOnExplicitConstructorCall) {
											if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
												setSourceRange(astNode.sourceStart, astNode.sourceEnd, false);
												
												CompletionOnExplicitConstructorCall constructorCall =
													(CompletionOnExplicitConstructorCall) astNode;
												TypeBinding[] argTypes =
													computeTypes(constructorCall.arguments, (BlockScope) scope);
												findConstructors(
													(ReferenceBinding) qualifiedBinding,
													argTypes,
													scope,
													constructorCall,
													false);
											}
										} else {
	
											if (astNode instanceof CompletionOnQualifiedAllocationExpression) {
												setSourceRange(astNode.sourceStart, astNode.sourceEnd, false);
												
												CompletionOnQualifiedAllocationExpression allocExpression =
													(CompletionOnQualifiedAllocationExpression) astNode;
												TypeBinding[] argTypes =
													computeTypes(allocExpression.arguments, (BlockScope) scope);
												
												ReferenceBinding ref = (ReferenceBinding) qualifiedBinding;
												if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)
														&& ref.isClass()
														&& !ref.isAbstract()) {
														findConstructors(
															ref,
															argTypes,
															scope,
															allocExpression,
															false);
												}
												if(!this.requestor.isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION)
														&& !ref.isFinal()
														&& !ref.isEnum()){
													findAnonymousType(
														ref,
														argTypes,
														scope,
														allocExpression);
												}
	
											} else {
	
												if (astNode instanceof CompletionOnClassLiteralAccess) {
													if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
														CompletionOnClassLiteralAccess access = (CompletionOnClassLiteralAccess) astNode;
														setSourceRange(access.classStart, access.sourceEnd);
										
														this.completionToken = access.completionIdentifier;
										
														findClassField(this.completionToken, (TypeBinding) qualifiedBinding, scope);
													}
												} else {
													if(astNode instanceof CompletionOnMethodName) {
														if(!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
															CompletionOnMethodName method = (CompletionOnMethodName) astNode;
																
															setSourceRange(method.sourceStart, method.selectorEnd);
																
															FieldBinding[] fields = scope.enclosingSourceType().fields();
															char[][] excludeNames = new char[fields.length][];
															for(int i = 0 ; i < fields.length ; i++){
																excludeNames[i] = fields[i].name;
															}
															
															this.completionToken = method.selector;
															
															findVariableNames(this.completionToken, method.returnType, excludeNames, FIELD, method.modifiers);
														}
													} else {
														if (astNode instanceof CompletionOnFieldName) {
															if(!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
																CompletionOnFieldName field = (CompletionOnFieldName) astNode;
																
																FieldBinding[] fields = scope.enclosingSourceType().fields();
																char[][] excludeNames = new char[fields.length][];
																for(int i = 0 ; i < fields.length ; i++){
																	excludeNames[i] = fields[i].name;
																}
																
																this.completionToken = field.realName;
																
																findVariableNames(field.realName, field.type, excludeNames, FIELD, field.modifiers);
															}
														} else {
															if (astNode instanceof CompletionOnLocalName ||
																astNode instanceof CompletionOnArgumentName){
																if(!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
																	LocalDeclaration variable = (LocalDeclaration) astNode;
																	
																	LocalVariableBinding[] locals = ((BlockScope)scope).locals;
																	char[][] excludeNames = new char[locals.length][];
																	int localCount = 0;
																	for(int i = 0 ; i < locals.length ; i++){
																		if(locals[i] != null) {
																			excludeNames[localCount++] = locals[i].name;
																		}
																	}
																	System.arraycopy(excludeNames, 0, excludeNames = new char[localCount][], 0, localCount);
																	
																	if(variable instanceof CompletionOnLocalName){
																		this.completionToken = ((CompletionOnLocalName) variable).realName;
																		findVariableNames(this.completionToken, variable.type, excludeNames, LOCAL, variable.modifiers);
																	} else {
																		CompletionOnArgumentName arg = (CompletionOnArgumentName) variable;
																		this.completionToken = arg.realName;
																		findVariableNames(this.completionToken, variable.type, excludeNames, arg.isCatchArgument ? LOCAL : ARGUMENT, variable.modifiers);
																	}
																}
															} else {
																if(astNode instanceof CompletionOnKeyword) {
																	if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
																		CompletionOnKeyword keyword = (CompletionOnKeyword)astNode;
																		findKeywords(keyword.getToken(), keyword.getPossibleKeywords());
																	}
																} else if(astNode instanceof CompletionOnParameterizedQualifiedTypeReference) {
																	if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
																		CompletionOnParameterizedQualifiedTypeReference ref = (CompletionOnParameterizedQualifiedTypeReference) astNode;
																		
																		this.insideQualifiedReference = true;
								
																		this.assistNodeIsClass = ref.isClass();
																		this.assistNodeIsException = ref.isException();
																		this.assistNodeIsInterface = ref.isInterface();
																		
																		this.completionToken = ref.completionIdentifier;
																		long completionPosition = ref.sourcePositions[ref.tokens.length];
																		setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
																		findMemberTypes(
																			this.completionToken,
																			(ReferenceBinding) qualifiedBinding,
																			scope,
																			scope.enclosingSourceType(),
																			false,
																			new ObjectVector());
																	}
																} else if(astNode instanceof CompletionOnMarkerAnnotationName) {
																	CompletionOnMarkerAnnotationName annot = (CompletionOnMarkerAnnotationName) astNode;
																	
																	this.assistNodeIsAnnotation = true;
																	if(annot.type instanceof CompletionOnSingleTypeReference) {
																		CompletionOnSingleTypeReference type = (CompletionOnSingleTypeReference) annot.type;
																		this.completionToken = type.token;
																		setSourceRange(type.sourceStart, type.sourceEnd);
																		
																		findTypesAndPackages(this.completionToken, scope);
																	} else if(annot.type instanceof CompletionOnQualifiedTypeReference) {
																		CompletionOnQualifiedTypeReference type = (CompletionOnQualifiedTypeReference) annot.type;
																		this.completionToken = type.completionIdentifier;
																		long completionPosition = type.sourcePositions[type.tokens.length];
																		setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
										
																		findMemberTypes(
																			this.completionToken,
																			(ReferenceBinding) qualifiedBinding,
																			scope,
																			scope.enclosingSourceType(),
																			false,
																			new ObjectVector());
																	}
																} else if (astNode instanceof CompletionOnMemberValueName) {
																	if(!this.requestor.isIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF)) {
																		CompletionOnMemberValueName memberValuePair = (CompletionOnMemberValueName) astNode;
																		Annotation annotation = (Annotation) astNodeParent;
																		
																		this.completionToken = memberValuePair.name;
																		
																		if(this.completionToken.length == 0) {
																			this.setSourceRange(astNode.sourceStart, astNode.sourceStart - 1, false);
								
																			findAnnotationReference(annotation.type);
																		} else {
																			MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
																			this.findAnnotationAttributes(this.completionToken, annotation.memberValuePairs(), (ReferenceBinding)annotation.resolvedType);
																			if(memberValuePairs == null || memberValuePairs.length == 0) {
																				if(annotation.resolvedType instanceof ReferenceBinding) {
																					MethodBinding[] methodBindings =
																						((ReferenceBinding)annotation.resolvedType).availableMethods();
																					if(methodBindings != null &&
																							methodBindings.length == 1 &&
																							CharOperation.equals(methodBindings[0].selector, VALUE)) {
																						if(this.expectedTypesPtr > -1 && this.expectedTypes[0].isAnnotationType()) {
																							findTypesAndPackages(this.completionToken, scope);
																						} else {
																							findVariablesAndMethods(
																								this.completionToken,
																								scope,
																								FakeInvocationSite,
																								scope,
																								insideTypeAnnotation,
																								true);
																							// can be the start of a qualified type name
																							findTypesAndPackages(this.completionToken, scope);
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	
	public void complete(IType type, char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){	
		if(this.requestor != null){
			this.requestor.beginReporting();
		}
		boolean contextAccepted = false;
		IType topLevelType = type;
		while(topLevelType.getDeclaringType() != null) {
			topLevelType = topLevelType.getDeclaringType();
		}
		
		CompilationResult compilationResult = new CompilationResult(topLevelType.getParent().getElementName().toCharArray(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
	
		CompilationUnitDeclaration compilationUnit = null;
	
		try {
			// TypeConverter is used instead of SourceTypeConverter because the type
			// to convert can be a binary type or a source type
			TypeDeclaration typeDeclaration = null;
			if (type instanceof SourceType) {
				SourceType sourceType = (SourceType) type;
				ISourceType info = (ISourceType) sourceType.getElementInfo();
				compilationUnit = SourceTypeConverter.buildCompilationUnit(
					new ISourceType[] {info},//sourceTypes[0] is always toplevel here
					SourceTypeConverter.FIELD_AND_METHOD // need field and methods
					| SourceTypeConverter.MEMBER_TYPE, // need member types
					// no need for field initialization
					this.problemReporter,
					compilationResult);
				if (compilationUnit.types != null)
					typeDeclaration = compilationUnit.types[0];
			} else {
				compilationUnit = new CompilationUnitDeclaration(this.problemReporter, compilationResult, 0);
				typeDeclaration = BinaryTypeConverter.buildTypeDeclaration(type, compilationUnit, compilationResult);
			}
		
			if(typeDeclaration != null) {	
				// build AST from snippet
				Initializer fakeInitializer = parseSnippeInitializer(snippet, position, localVariableTypeNames, localVariableNames, localVariableModifiers, isStatic);
				
				// merge AST
				FieldDeclaration[] oldFields = typeDeclaration.fields;
				FieldDeclaration[] newFields = null;
				if (oldFields != null) {
					newFields = new FieldDeclaration[oldFields.length + 1];
					System.arraycopy(oldFields, 0, newFields, 0, oldFields.length);
					newFields[oldFields.length] = fakeInitializer;
				} else {
					newFields = new FieldDeclaration[] {fakeInitializer};
				}
				typeDeclaration.fields = newFields;
		
				if(DEBUG) {
					System.out.println("SNIPPET COMPLETION AST :"); //$NON-NLS-1$
					System.out.println(compilationUnit.toString());
				}
				
				if (compilationUnit.types != null) {
					try {
						this.lookupEnvironment.buildTypeBindings(compilationUnit, null /*no access restriction*/);
				
						if ((this.unitScope = compilationUnit.scope) != null) {
							this.lookupEnvironment.completeTypeBindings(compilationUnit, true);
							compilationUnit.scope.faultInTypes();
							compilationUnit.resolve();
						}
					} catch (CompletionNodeFound e) {
						//					completionNodeFound = true;
						if (e.astNode != null) {
							contextAccepted = true;
							// if null then we found a problem in the completion node
							complete(e.astNode, this.parser.assistNodeParent, e.qualifiedBinding, e.scope, e.insideTypeAnnotation);
						}
					}
				}
				if(this.noProposal && this.problem != null) {
					if(!contextAccepted) {
						contextAccepted = true;
						this.requestor.acceptContext(new CompletionContext());
					}
					this.requestor.completionFailure(this.problem);
					if(DEBUG) {
						this.printDebug(this.problem);
					}
				}
			}
		} catch(JavaModelException e) {
			// Do nothing
		}
		if(!contextAccepted) {
			contextAccepted = true;
			this.requestor.acceptContext(new CompletionContext());
		}
		if(this.requestor != null){
			this.requestor.endReporting();
		}
	}
	
	private Initializer parseSnippeInitializer(char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){
		StringBuffer prefix = new StringBuffer();
		prefix.append("public class FakeType {\n "); //$NON-NLS-1$
		if(isStatic) {
			prefix.append("static "); //$NON-NLS-1$
		}
		prefix.append("{\n"); //$NON-NLS-1$
		for (int i = 0; i < localVariableTypeNames.length; i++) {
			ASTNode.printModifiers(localVariableModifiers[i], prefix);
			prefix.append(' ');
			prefix.append(localVariableTypeNames[i]);
			prefix.append(' ');
			prefix.append(localVariableNames[i]);
			prefix.append(';');
		}
		
		char[] fakeSource = CharOperation.concat(prefix.toString().toCharArray(), snippet, "}}".toCharArray());//$NON-NLS-1$ 
		this.offset = prefix.length();
		
		String encoding = this.compilerOptions.defaultEncoding;
		BasicCompilationUnit fakeUnit = new BasicCompilationUnit(
			fakeSource, 
			null,
			"FakeType.java", //$NON-NLS-1$
			encoding); 
			
		this.actualCompletionPosition = prefix.length() + position - 1;
			
		CompilationResult fakeResult = new CompilationResult(fakeUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
		CompilationUnitDeclaration fakeAST = this.parser.dietParse(fakeUnit, fakeResult, this.actualCompletionPosition);
		
		parseBlockStatements(fakeAST, this.actualCompletionPosition);
		
		return (Initializer)fakeAST.types[0].fields[0];
	}

	/**
	 * Ask the engine to compute a completion at the specified position
	 * of the given compilation unit.
	 *
	 *  No return
	 *      completion results are answered through a requestor.
	 *
	 *  @param sourceUnit org.eclipse.jdt.internal.compiler.env.ICompilationUnit
	 *      the source of the current compilation unit.
	 *
	 *  @param completionPosition int
	 *      a position in the source where the completion is taking place. 
	 *      This position is relative to the source provided.
	 */
	public void complete(ICompilationUnit sourceUnit, int completionPosition, int pos) {

		if(DEBUG) {
			System.out.print("COMPLETION IN "); //$NON-NLS-1$
			System.out.print(sourceUnit.getFileName());
			System.out.print(" AT POSITION "); //$NON-NLS-1$
			System.out.println(completionPosition);
			System.out.println("COMPLETION - Source :"); //$NON-NLS-1$
			System.out.println(sourceUnit.getContents());
		}
		this.requestor.beginReporting();
		boolean contextAccepted = false;
		try {
			this.actualCompletionPosition = completionPosition - 1;
			this.offset = pos;
			// for now until we can change the UI.
			CompilationResult result = new CompilationResult(sourceUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
			CompilationUnitDeclaration parsedUnit = this.parser.dietParse(sourceUnit, result, this.actualCompletionPosition);

			//		boolean completionNodeFound = false;
			if (parsedUnit != null) {
				if(DEBUG) {
					System.out.println("COMPLETION - Diet AST :"); //$NON-NLS-1$
					System.out.println(parsedUnit.toString());
				}

				// scan the package & import statements first
				if (parsedUnit.currentPackage instanceof CompletionOnPackageReference) {
					contextAccepted = true;
					this.requestor.acceptContext(new CompletionContext());
					if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
						findPackages((CompletionOnPackageReference) parsedUnit.currentPackage);
					}
					if(this.noProposal && this.problem != null) {
						this.requestor.completionFailure(this.problem);
						if(DEBUG) {
							this.printDebug(this.problem);
						}
					}
					return;
				}

				ImportReference[] imports = parsedUnit.imports;
				if (imports != null) {
					for (int i = 0, length = imports.length; i < length; i++) {
						ImportReference importReference = imports[i];
						if (importReference instanceof CompletionOnImportReference) {
							contextAccepted = true;
							this.requestor.acceptContext(new CompletionContext());
							findImports((CompletionOnImportReference) importReference);
							if(this.noProposal && this.problem != null) {
								this.requestor.completionFailure(this.problem);
								if(DEBUG) {
									this.printDebug(this.problem);
								}
							}
							if(importReference.isStatic()) {
								this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
								if ((this.unitScope = parsedUnit.scope) != null) {
									char[][] oldTokens = importReference.tokens;
									int tokenCount = oldTokens.length;
									char[] lastToken = oldTokens[tokenCount - 1];
									char[][] qualifierTokens = CharOperation.subarray(oldTokens, 0, tokenCount - 1);
									
									Binding binding = this.unitScope.getTypeOrPackage(qualifierTokens);
									if(binding != null && binding instanceof ReferenceBinding) {
										ReferenceBinding ref = (ReferenceBinding) binding;
										if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
											this.findImportsOfMemberTypes(lastToken, ref);
										}
										if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
											this.findImportsOfStaticFields(lastToken, ref);
										}
										if(!this.requestor.isIgnored(CompletionProposal.METHOD_NAME_REFERENCE)) {
											this.findImportsOfStaticMethdods(lastToken, ref);
										}
									}
								}
							}
							return;
						} else if(importReference instanceof CompletionOnKeyword) {
							contextAccepted = true;
							this.requestor.acceptContext(new CompletionContext());
							if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
								setSourceRange(importReference.sourceStart, importReference.sourceEnd);
								CompletionOnKeyword keyword = (CompletionOnKeyword)importReference;
								findKeywords(keyword.getToken(), keyword.getPossibleKeywords());
							}
							if(this.noProposal && this.problem != null) {
								this.requestor.completionFailure(this.problem);
								if(DEBUG) {
									this.printDebug(this.problem);
								}
							}
							return;
						}
					}
				}

				if (parsedUnit.types != null) {
					try {
						this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);

						if ((this.unitScope = parsedUnit.scope) != null) {
							this.source = sourceUnit.getContents();
							this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
							parsedUnit.scope.faultInTypes();
							parseBlockStatements(parsedUnit, this.actualCompletionPosition);
							if(DEBUG) {
								System.out.println("COMPLETION - AST :"); //$NON-NLS-1$
								System.out.println(parsedUnit.toString());
							}
							parsedUnit.resolve();
						}
					} catch (CompletionNodeFound e) {
						//					completionNodeFound = true;
						if (e.astNode != null) {
							if(DEBUG) {
								System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
								System.out.println(e.astNode.toString());
								if(this.parser.assistNodeParent != null) {
									System.out.print("COMPLETION - Parent Node : ");  //$NON-NLS-1$
									System.out.println(this.parser.assistNodeParent);
								}
							}
							contextAccepted = true;
							// if null then we found a problem in the completion node
							complete(e.astNode, this.parser.assistNodeParent, e.qualifiedBinding, e.scope, e.insideTypeAnnotation);
						}
					}
				}
			}
			
			if(this.noProposal && this.problem != null) {
				if(!contextAccepted) {
					contextAccepted = true;
					this.requestor.acceptContext(new CompletionContext());
				}
				this.requestor.completionFailure(this.problem);
				if(DEBUG) {
					this.printDebug(this.problem);
				}
			}
			/* Ignore package, import, class & interface keywords for now...
					if (!completionNodeFound) {
						if (parsedUnit == null || parsedUnit.types == null) {
							// this is not good enough... can still be trying to define a second type
							CompletionScanner scanner = (CompletionScanner) this.parser.scanner;
							setSourceRange(scanner.completedIdentifierStart, scanner.completedIdentifierEnd);
							findKeywords(scanner.completionIdentifier, mainDeclarations, null);
						}
						// currently have no way to know if extends/implements are possible keywords
					}
			*/
		} catch (IndexOutOfBoundsException e) { // work-around internal failure - 1GEMF6D
			if(DEBUG) {
				System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
				e.printStackTrace(System.out);
			}
		} catch (InvalidCursorLocation e) { // may eventually report a usefull error
			if(DEBUG) {
				System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
				e.printStackTrace(System.out);
			}
		} catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
			if(DEBUG) {
				System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
				e.printStackTrace(System.out);
			}
		} catch (CompletionNodeFound e){ // internal failure - bugs 5618
			if(DEBUG) {
				System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
				e.printStackTrace(System.out);
			}
		} finally {
			reset();
			if(!contextAccepted) {
				contextAccepted = true;
				this.requestor.acceptContext(new CompletionContext());
			}
			this.requestor.endReporting();
		}
	}

	private TypeBinding[] computeTypes(Expression[] arguments, BlockScope scope) {

		if (arguments == null)
			return null;

		int argsLength = arguments.length;
		TypeBinding[] argTypes = new TypeBinding[argsLength];
		for (int a = argsLength; --a >= 0;)
			argTypes[a] = arguments[a].resolvedType;
		return argTypes;
	}
	
	private void findAnnotationAttributes(char[] token, MemberValuePair[] attributesFound, ReferenceBinding annotation) {
		MethodBinding[] methods = annotation.availableMethods();
		nextAttribute: for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];
			
			if(!CharOperation.prefixEquals(token, method.selector, false)) continue nextAttribute;
			
			int length = attributesFound == null ? 0 : attributesFound.length;
			for (int j = 0; j < length; j++) {
				if(CharOperation.equals(method.selector, attributesFound[j].name, false)) continue nextAttribute;
			}
			
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal(method);
			relevance += computeRelevanceForCaseMatching(token, method.selector);
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.ANNOTATION_ATTRIBUTE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method.returnType));
				proposal.setName(method.selector);
				proposal.setCompletion(method.selector);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	private void findAnnotationReference(TypeReference ref) {
		ReferenceBinding refBinding = (ReferenceBinding) ref.resolvedType;
		if(refBinding != null) {
			char[] packageName = refBinding.qualifiedPackageName();
			char[] typeName = refBinding.qualifiedSourceName();
			
			int accessibility = IAccessRule.K_ACCESSIBLE;
			if(refBinding.hasRestrictedAccess()) {
				AccessRestriction accessRestriction = lookupEnvironment.getAccessRestriction(refBinding);
				if(accessRestriction != null) {
					switch (accessRestriction.getProblemId()) {
						case IProblem.ForbiddenReference:
							switch (this.options.restrictedReferenceFilter) {
								case AssistOptions.FILTER_WARNING:
									return;
								case AssistOptions.FILTER_ERROR:
									if(this.forbiddenReferenceIsError) return;
									break;
								
							}
							accessibility = IAccessRule.K_NON_ACCESSIBLE;
							break;
						case IProblem.DiscouragedReference:
							switch (this.options.restrictedReferenceFilter) {
								case AssistOptions.FILTER_WARNING:
									return;
								case AssistOptions.FILTER_ERROR:
									if(this.discouragedReferenceIsError) return;
									break;
								
							}
							accessibility = IAccessRule.K_DISCOURAGED;
							break;
					}
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(refBinding.sourceName, refBinding.sourceName);
			relevance += computeRelevanceForExpectingType(refBinding);
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(accessibility); // no access restriction for type in the current unit
			
			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(packageName);
				proposal.setSignature(getSignature(refBinding));
				proposal.setPackageName(packageName);
				proposal.setTypeName(typeName);
				proposal.setCompletion(CharOperation.NO_CHAR);
				proposal.setFlags(refBinding.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				proposal.setAccessibility(accessibility);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	private void findAnonymousType(
		ReferenceBinding currentType,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite) {

		if (currentType.isInterface()) {
			char[] completion = CharOperation.NO_CHAR;
			// nothing to insert - do not want to replace the existing selector & arguments
			if (this.source == null
				|| this.source.length <= this.endPosition
				|| this.source[this.endPosition] != ')')
				completion = new char[] { ')' };
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(currentType));
				proposal.setDeclarationKey(currentType.computeUniqueKey());
				proposal.setSignature(
						createMethodSignature(
								CharOperation.NO_CHAR_CHAR,
								CharOperation.NO_CHAR_CHAR,
								CharOperation.NO_CHAR,
								CharOperation.NO_CHAR));
				//proposal.setUniqueKey(null);
				proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
				proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
				//proposal.setParameterPackageNames(null);
				//proposal.setParameterTypeNames(null);
				//proposal.setPackageName(null);
				//proposal.setTypeName(null);
				proposal.setCompletion(completion);
				proposal.setFlags(Flags.AccPublic);
				proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		} else {
			findConstructors(
				currentType,
				argTypes,
				scope,
				invocationSite,
				true);
		}
	}

	private void findClassField(char[] token, TypeBinding receiverType, Scope scope) {

		if (token == null)
			return;

		if (token.length <= classField.length
			&& CharOperation.prefixEquals(token, classField, false /* ignore case */
		)) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(token, classField);
			relevance += computeRelevanceForExpectingType(scope.getJavaLangClass());
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); //no access restriction for class field 
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
				//proposal.setDeclarationSignature(null);
				proposal.setSignature(
						createNonGenericTypeSignature(
								CharOperation.concatWith(JAVA_LANG, '.'),
								CLASS));
				//proposal.setDeclarationPackageName(null);
				//proposal.setDeclarationTypeName(null);
				proposal.setPackageName(CharOperation.concatWith(JAVA_LANG, '.'));
				proposal.setTypeName(CLASS);
				proposal.setName(classField);
				proposal.setCompletion(classField);
				proposal.setFlags(Flags.AccStatic | Flags.AccPublic);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	private void findEnumConstant(char[] enumConstantName, SwitchStatement switchStatement) {
		TypeBinding expressionType = switchStatement.expression.resolvedType;
		if(expressionType != null && expressionType.isEnum()) {
			ReferenceBinding enumType = (ReferenceBinding) expressionType;
			
			FieldBinding[] fields = enumType.fields();
			
			int enumConstantLength = enumConstantName.length;
			next : for (int f = fields.length; --f >= 0;) {			
				FieldBinding field = fields[f];

				if (field.isSynthetic()) continue next;

				if ((field.modifiers & Flags.AccEnum) == 0) continue next;

				if (enumConstantLength > field.name.length) continue next;

				if (!CharOperation.prefixEquals(enumConstantName, field.name, false /* ignore case */))	continue next;
				
				char[] completion = field.name;

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal(field);
				relevance += R_ENUM_CONSTANT;
				relevance += computeRelevanceForCaseMatching(enumConstantName, field.name);
				relevance += computeRelevanceForExpectingType(field.type);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
				
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName()); 
					proposal.setName(field.name);
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}
	private void findExplicitConstructors(
		char[] name,
		ReferenceBinding currentType,
		MethodScope scope,
		InvocationSite invocationSite) {
			
		ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration)scope.referenceContext;
		MethodBinding enclosingConstructor = constructorDeclaration.binding;

		// No visibility checks can be performed without the scope & invocationSite
		MethodBinding[] methods = currentType.availableMethods();
		if(methods != null) {
			next : for (int f = methods.length; --f >= 0;) {
				MethodBinding constructor = methods[f];
				if (constructor != enclosingConstructor && constructor.isConstructor()) {
					
					if (constructor.isSynthetic()) continue next;
						
					if (this.options.checkVisibility
						&& !constructor.canBeSeenBy(invocationSite, scope))	continue next;
					
					TypeBinding[] parameters = constructor.parameters;
					int paramLength = parameters.length;
	
					char[][] parameterPackageNames = new char[paramLength][];
					char[][] parameterTypeNames = new char[paramLength][];
					for (int i = 0; i < paramLength; i++) {
						TypeBinding type = parameters[i];
						parameterPackageNames[i] = type.qualifiedPackageName();
						parameterTypeNames[i] = type.qualifiedSourceName();
					}
					char[][] parameterNames = findMethodParameterNames(constructor,parameterTypeNames);
					
					char[] completion = CharOperation.NO_CHAR;
					if (this.source != null
						&& this.source.length > this.endPosition
						&& this.source[this.endPosition] == '(')
						completion = name;
					else
						completion = CharOperation.concat(name, new char[] { '(', ')' });
					
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(this.completionToken, name);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
					
					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
						CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(currentType));
						proposal.setSignature(getSignature(constructor));
						proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
						proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
						proposal.setParameterPackageNames(parameterPackageNames);
						proposal.setParameterTypeNames(parameterTypeNames);
						//proposal.setPackageName(null);
						//proposal.setTypeName(null);
						proposal.setName(name);
						proposal.setCompletion(completion);
						proposal.setFlags(constructor.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setRelevance(relevance);
						if(parameterNames != null) proposal.setParameterNames(parameterNames);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}
		}
	}
	private void findConstructors(
		ReferenceBinding currentType,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		boolean forAnonymousType) {

		// No visibility checks can be performed without the scope & invocationSite
		MethodBinding[] methods = currentType.availableMethods();
		if(methods != null) {
			int minArgLength = argTypes == null ? 0 : argTypes.length;
			next : for (int f = methods.length; --f >= 0;) {
				MethodBinding constructor = methods[f];
				if (constructor.isConstructor()) {
					
					if (constructor.isSynthetic()) continue next;
						
					if (this.options.checkVisibility
						&& !constructor.canBeSeenBy(invocationSite, scope)) {
						if(!forAnonymousType || !constructor.isProtected())
							continue next;
					}
					
					TypeBinding[] parameters = constructor.parameters;
					int paramLength = parameters.length;
					if (minArgLength > paramLength)
						continue next;
					for (int a = minArgLength; --a >= 0;)
						if (argTypes[a] != null) // can be null if it could not be resolved properly
							if (!argTypes[a].isCompatibleWith(constructor.parameters[a]))
								continue next;
	
					char[][] parameterPackageNames = new char[paramLength][];
					char[][] parameterTypeNames = new char[paramLength][];
					for (int i = 0; i < paramLength; i++) {
						TypeBinding type = parameters[i];
						parameterPackageNames[i] = type.qualifiedPackageName();
						parameterTypeNames[i] = type.qualifiedSourceName();
					}
					char[][] parameterNames = findMethodParameterNames(constructor,parameterTypeNames);
					
					char[] completion = CharOperation.NO_CHAR;
					// nothing to insert - do not want to replace the existing selector & arguments
					if (this.source == null
						|| this.source.length <= this.endPosition
						|| this.source[this.endPosition] != ')')
						completion = new char[] { ')' };
					
					if(forAnonymousType){
						int relevance = computeBaseRelevance();
						relevance += computeRelevanceForInterestingProposal();
						relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
						
						this.noProposal = false;
						if(!this.requestor.isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION)) {
							CompletionProposal proposal = this.createProposal(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, this.actualCompletionPosition);
							proposal.setDeclarationSignature(getSignature(currentType));
							proposal.setDeclarationKey(currentType.computeUniqueKey());
							proposal.setSignature(getSignature(constructor));
							proposal.setKey(constructor.computeUniqueKey());
							proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
							proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
							proposal.setParameterPackageNames(parameterPackageNames);
							proposal.setParameterTypeNames(parameterTypeNames);
							//proposal.setPackageName(null);
							//proposal.setTypeName(null);
							proposal.setCompletion(completion);
							proposal.setFlags(constructor.modifiers);
							proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
							proposal.setRelevance(relevance);
							if(parameterNames != null) proposal.setParameterNames(parameterNames);
							this.requestor.accept(proposal);
							if(DEBUG) {
								this.printDebug(proposal);
							}
						}
					} else {
						int relevance = computeBaseRelevance();
						relevance += computeRelevanceForInterestingProposal();
						relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
						
						this.noProposal = false;
						if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
							CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
							proposal.setDeclarationSignature(getSignature(currentType));
							proposal.setSignature(getSignature(constructor));
							proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
							proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
							proposal.setParameterPackageNames(parameterPackageNames);
							proposal.setParameterTypeNames(parameterTypeNames);
							//proposal.setPackageName(null);
							//proposal.setTypeName(null);
							proposal.setName(currentType.sourceName());
							proposal.setCompletion(completion);
							proposal.setFlags(constructor.modifiers);
							proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
							proposal.setRelevance(relevance);
							if(parameterNames != null) proposal.setParameterNames(parameterNames);
							this.requestor.accept(proposal);
							if(DEBUG) {
								this.printDebug(proposal);
							}
						}
					}
				}
			}
		}
	}
	
	// Helper method for findFields(char[], ReferenceBinding, Scope, ObjectVector, boolean)
	private void findFields(
		char[] fieldName,
		FieldBinding[] fields,
		Scope scope,
		ObjectVector fieldsFound,
		ObjectVector localsFound,
		boolean onlyStaticFields,
		ReferenceBinding receiverType,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean canBePrefixed) {

		ObjectVector newFieldsFound = new ObjectVector();
		// Inherited fields which are hidden by subclasses are filtered out
		// No visibility checks can be performed without the scope & invocationSite
		
		int fieldLength = fieldName.length;
		next : for (int f = fields.length; --f >= 0;) {			
			FieldBinding field = fields[f];

			if (field.isSynthetic())	continue next;

			if (onlyStaticFields && !field.isStatic()) continue next;

			if (fieldLength > field.name.length) continue next;

			if (!CharOperation.prefixEquals(fieldName, field.name, false /* ignore case */))	continue next;

			if (this.options.checkVisibility
				&& !field.canBeSeenBy(receiverType, invocationSite, scope))	continue next;

			boolean prefixRequired = false;

			for (int i = fieldsFound.size; --i >= 0;) {
				Object[] other = (Object[])fieldsFound.elementAt(i);
				FieldBinding otherField = (FieldBinding) other[0];
				ReferenceBinding otherReceiverType = (ReferenceBinding) other[1];
				if (field == otherField && receiverType == otherReceiverType)
					continue next;
				if (CharOperation.equals(field.name, otherField.name, true)) {
					if (field.declaringClass.isSuperclassOf(otherField.declaringClass))
						continue next;
					if (otherField.declaringClass.isInterface()) {
						if (field.declaringClass == scope.getJavaLangObject())
							continue next;
						if (field.declaringClass.implementsInterface(otherField.declaringClass, true))
							continue next;
					}
					if (field.declaringClass.isInterface())
						if (otherField.declaringClass.implementsInterface(field.declaringClass, true))
							continue next;
					if(canBePrefixed) {
						prefixRequired = true;
					} else {
						continue next;
					}
				}
			}

			for (int l = localsFound.size; --l >= 0;) {
				LocalVariableBinding local = (LocalVariableBinding) localsFound.elementAt(l);	

				if (CharOperation.equals(field.name, local.name, true)) {
					SourceTypeBinding declarationType = scope.enclosingSourceType();
					if (declarationType.isAnonymousType() && declarationType != invocationScope.enclosingSourceType()) {
						continue next;
					}
					if(canBePrefixed) {
						prefixRequired = true;
					} else {
						continue next;
					}
					break;
				}
			}
			
			newFieldsFound.add(new Object[]{field, receiverType});
			
			char[] completion = field.name;
			
			if(prefixRequired || this.options.forceImplicitQualification){
				char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), field.isStatic());
				completion = CharOperation.concat(prefix,completion,'.');
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal(field);
			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
			relevance += computeRelevanceForExpectingType(field.type);
			relevance += computeRelevanceForStatic(onlyStaticFields, field.isStatic());
			relevance += computeRelevanceForQualification(prefixRequired);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(field.declaringClass));
				proposal.setSignature(getSignature(field.type));
				proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
				proposal.setPackageName(field.type.qualifiedPackageName());
				proposal.setTypeName(field.type.qualifiedSourceName()); 
				proposal.setName(field.name);
				proposal.setCompletion(completion);
				proposal.setFlags(field.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
		
		fieldsFound.addAll(newFieldsFound);
	}

	private void findFields(
		char[] fieldName,
		ReferenceBinding receiverType,
		Scope scope,
		ObjectVector fieldsFound,
		ObjectVector localsFound,
		boolean onlyStaticFields,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean canBePrefixed) {

		if (fieldName == null)
			return;

		ReferenceBinding currentType = receiverType;
		ReferenceBinding[][] interfacesToVisit = null;
		int lastPosition = -1;
		do {

			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != NoSuperInterfaces) {

				if (interfacesToVisit == null)
					interfacesToVisit = new ReferenceBinding[5][];

				if (++lastPosition == interfacesToVisit.length)
					System.arraycopy(
						interfacesToVisit,
						0,
						interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
						0,
						lastPosition);
				interfacesToVisit[lastPosition] = itsInterfaces;
			}

			FieldBinding[] fields = currentType.availableFields();
			if(fields != null) {
				findFields(
					fieldName,
					fields,
					scope,
					fieldsFound,
					localsFound,
					onlyStaticFields,
					receiverType,
					invocationSite,
					invocationScope,
					implicitCall,
					canBePrefixed);
			}
			currentType = currentType.superclass();
		} while (currentType != null);

		if (interfacesToVisit != null) {
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++) {

					ReferenceBinding anInterface = interfaces[j];
					if ((anInterface.tagBits & TagBits.InterfaceVisited) == 0) {
						// if interface as not already been visited
						anInterface.tagBits |= TagBits.InterfaceVisited;

						FieldBinding[] fields = anInterface.availableFields();
						if(fields !=  null) {
							findFields(
								fieldName,
								fields,
								scope,
								fieldsFound,
								localsFound,
								onlyStaticFields,
								receiverType,
								invocationSite,
								invocationScope,
								implicitCall,
								canBePrefixed);
						}

						ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
						if (itsInterfaces != NoSuperInterfaces) {
							if (++lastPosition == interfacesToVisit.length)
								System.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0,
									lastPosition);
							interfacesToVisit[lastPosition] = itsInterfaces;
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++)
					interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
			}
		}
	}

	private void findFieldsAndMethods(
		char[] token,
		TypeBinding receiverType,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall) {

		if (token == null)
			return;

		if (receiverType.isBaseType())
			return; // nothing else is possible with base types
		
		boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF);
		boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF);
		
		ObjectVector methodsFound = new ObjectVector();
		
		if (receiverType.isArrayType()) {
			if (proposeField
				&& token.length <= lengthField.length
				&& CharOperation.prefixEquals(token, lengthField, false /* ignore case */
			)) {
				
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(token,lengthField);
				relevance += computeRelevanceForExpectingType(BaseTypes.IntBinding);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for length field
				
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setDeclarationSignature(getSignature(receiverType));
					proposal.setSignature(INT_SIGNATURE);
					//proposal.setDeclarationPackageName(null);
					//proposal.setDeclarationTypeName(null);
					//proposal.setPackageName(null);
					proposal.setTypeName(INT);
					proposal.setName(lengthField);
					proposal.setCompletion(lengthField);
					proposal.setFlags(Flags.AccPublic);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
			if (proposeMethod
				&& token.length <= cloneMethod.length
				&& CharOperation.prefixEquals(token, cloneMethod, false /* ignore case */
			)) {
				ReferenceBinding objectRef = scope.getJavaLangObject();
				
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(token, cloneMethod);
				relevance += computeRelevanceForExpectingType(objectRef);
				relevance += computeRelevanceForStatic(false, false);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for clone() method
				
				char[] completion;
				if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(') {
					completion = cloneMethod;
					} else {
					completion = CharOperation.concat(cloneMethod, new char[] { '(', ')' });
				}
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
					proposal.setDeclarationSignature(getSignature(receiverType));
					proposal.setSignature(
							createMethodSignature(
									CharOperation.NO_CHAR_CHAR,
									CharOperation.NO_CHAR_CHAR,
									CharOperation.concatWith(JAVA_LANG, '.'),
									OBJECT));
					//proposal.setDeclarationPackageName(null);
					//proposal.setDeclarationTypeName(null);
					//proposal.setParameterPackageNames(null);
					//proposal.setParameterTypeNames(null);
					proposal.setPackageName(CharOperation.concatWith(JAVA_LANG, '.'));
					proposal.setTypeName(OBJECT);
					proposal.setName(cloneMethod);
					proposal.setCompletion(completion);
					proposal.setFlags(Flags.AccPublic);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
				methodsFound.add(new Object[]{objectRef.getMethods(cloneMethod)[0], objectRef});
			}
			
			receiverType = scope.getJavaLangObject();
		}

		if(proposeField) {
			findFields(
				token,
				(ReferenceBinding) receiverType,
				scope,
				new ObjectVector(),
				new ObjectVector(),
				false,
				invocationSite,
				invocationScope,
				implicitCall,
				true);
		}

		if(proposeMethod) {
			findMethods(
				token,
				null,
				(ReferenceBinding) receiverType,
				scope,
				methodsFound,
				false,
				false,
				false,
				invocationSite,
				invocationScope,
				implicitCall,
				superCall,
				true);
		}
	}

	private void findImports(CompletionOnImportReference importReference) {
		char[][] tokens = importReference.tokens;
			
		char[] importName = CharOperation.concatWith(tokens, '.');
		
		if (importName.length == 0)
			return;
			
		char[] lastToken = tokens[tokens.length - 1];
		if(lastToken != null && lastToken.length == 0)
			importName = CharOperation.concat(importName, new char[]{'.'});

		this.resolvingImports = true;
		setSourceRange(
			importReference.sourceStart,
			importReference.declarationSourceEnd);
			
		this.completionToken =  importName;
		// want to replace the existing .*;
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			this.nameEnvironment.findPackages(importName, this);
		}
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			this.nameEnvironment.findTypes(importName, this);
		}
	}
	
	private void findImportsOfMemberTypes(char[] typeName,	ReferenceBinding ref) {
		ReferenceBinding[] memberTypes = ref.memberTypes();
		
		int typeLength = typeName.length;
		next : for (int m = memberTypes.length; --m >= 0;) {
			ReferenceBinding memberType = memberTypes[m];
			//		if (!wantClasses && memberType.isClass()) continue next;
			//		if (!wantInterfaces && memberType.isInterface()) continue next;
			
			if (typeLength > memberType.sourceName.length)
				continue next;

			if (!CharOperation.prefixEquals(typeName, memberType.sourceName, false
				/* ignore case */
				))
				continue next;

			if (this.options.checkVisibility
				&& !memberType.canBeSeenBy(this.unitScope.fPackage))
				continue next;
			
			char[] completionName = CharOperation.concat(
					memberType.qualifiedPackageName(),
					memberType.qualifiedSourceName(),
					'.');
			
			completionName = CharOperation.concat(completionName, SEMICOLON);
			
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(typeName, memberType.sourceName);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			if (memberType.isClass()) {
				relevance += computeRelevanceForClass();
			} else if(memberType.isEnum()) {
				relevance += computeRelevanceForEnum();
			} else if (memberType.isInterface()) {
				relevance += computeRelevanceForInterface();
			}
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(memberType.qualifiedPackageName());
				proposal.setSignature(getSignature(memberType));
				proposal.setPackageName(memberType.qualifiedPackageName());
				proposal.setTypeName(memberType.qualifiedSourceName());
				proposal.setCompletion(completionName);
				proposal.setFlags(memberType.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	
	private void findImportsOfStaticFields(char[] fieldName, ReferenceBinding ref) {
		FieldBinding[] fields = ref.fields();
		
		int fieldLength = fieldName.length;
		next : for (int m = fields.length; --m >= 0;) {
			FieldBinding field = fields[m];

			if (fieldLength > field.name.length)
				continue next;
			
			if (field.isSynthetic())
				continue next;

			if (!field.isStatic())
				continue next;

			if (!CharOperation.prefixEquals(fieldName, field.name, false
				/* ignore case */
				))
				continue next;

			if (this.options.checkVisibility
				&& !field.canBeSeenBy(this.unitScope.fPackage))
				continue next;
			
			char[] completionName = CharOperation.concat(
					field.declaringClass.qualifiedPackageName(),
					'.',
					field.declaringClass.qualifiedSourceName(),
					'.',
					field.name);
			
			completionName = CharOperation.concat(completionName, SEMICOLON);
			
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(field.declaringClass));
				proposal.setSignature(getSignature(field.type));
				proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
				proposal.setPackageName(field.type.qualifiedPackageName());
				proposal.setTypeName(field.type.qualifiedSourceName()); 
				proposal.setName(field.name);
				proposal.setCompletion(completionName);
				proposal.setFlags(field.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	
	private void findImportsOfStaticMethdods(char[] methodName, ReferenceBinding ref) {
		MethodBinding[] methods = ref.methods();
		
		int methodLength = methodName.length;
		next : for (int m = methods.length; --m >= 0;) {
			MethodBinding method = methods[m];

			if (method.isSynthetic()) continue next;

			if (method.isDefaultAbstract())	continue next;

			if (method.isConstructor()) continue next;

			if (!method.isStatic()) continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(this.unitScope.fPackage)) continue next;
			
			if (methodLength > method.selector.length)
				continue next;

			if (!CharOperation.prefixEquals(methodName, method.selector, false
				/* ignore case */
				))
				continue next;
			
			int length = method.parameters.length;
			char[][] parameterPackageNames = new char[length][];
			char[][] parameterTypeNames = new char[length][];

			for (int i = 0; i < length; i++) {
				TypeBinding type = method.original().parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			
			char[] completionName = CharOperation.concat(
					method.declaringClass.qualifiedPackageName(),
					'.',
					method.declaringClass.qualifiedSourceName(),
					'.',
					method.selector);
			
			completionName = CharOperation.concat(completionName, SEMICOLON);
			
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_NAME_REFERENCE)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_NAME_REFERENCE, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method));
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setName(method.selector);
				proposal.setCompletion(completionName);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	
	// what about onDemand types? Ignore them since it does not happen!
	// import p1.p2.A.*;
	private void findKeywords(char[] keyword, char[][] choices) {
		if(choices == null || choices.length == 0) return;
		
		int length = keyword.length;
		if (length > 0)
			for (int i = 0; i < choices.length; i++)
				if (length <= choices[i].length
					&& CharOperation.prefixEquals(keyword, choices[i], false /* ignore case */
				)){
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(keyword, choices[i]);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors
					
					if(CharOperation.equals(choices[i], Keywords.TRUE) || CharOperation.equals(choices[i], Keywords.FALSE)) {
						relevance += computeRelevanceForExpectingType(BaseTypes.BooleanBinding);
						relevance += computeRelevanceForQualification(false);
					}
					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
						CompletionProposal proposal = this.createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
						proposal.setName(choices[i]);
						proposal.setCompletion(choices[i]);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
	}
	private void findTrueOrFalseKeywords(char[][] choices) {
		if(choices == null || choices.length == 0) return;
		
		if(this.expectedTypesPtr != 0 || this.expectedTypes[0] != BaseTypes.BooleanBinding) return;
		
		for (int i = 0; i < choices.length; i++) {
			if (CharOperation.equals(choices[i], Keywords.TRUE) ||
					CharOperation.equals(choices[i], Keywords.FALSE)
			){
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(CharOperation.NO_CHAR, choices[i]);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors
				relevance += computeRelevanceForExpectingType(BaseTypes.BooleanBinding);
				relevance += computeRelevanceForQualification(false);
				relevance += R_TRUE_OR_FALSE;

				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
					proposal.setName(choices[i]);
					proposal.setCompletion(choices[i]);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}
	
	private void findKeywordsForMember(char[] token, int modifiers) {
		char[][] keywords = new char[Keywords.COUNT][];
		int count = 0;
				
		// visibility
		if((modifiers & IConstants.AccPrivate) == 0
			&& (modifiers & IConstants.AccProtected) == 0
			&& (modifiers & IConstants.AccPublic) == 0) {
			keywords[count++] = Keywords.PROTECTED;
			keywords[count++] = Keywords.PUBLIC;
			if((modifiers & IConstants.AccAbstract) == 0) {
				keywords[count++] = Keywords.PRIVATE;
			}
		}
		
		if((modifiers & IConstants.AccAbstract) == 0) {
			// abtract
			if((modifiers & ~(CompilerModifiers.AccVisibilityMASK | IConstants.AccStatic)) == 0) {
				keywords[count++] = Keywords.ABSTRACT;
			}
			
			// final
			if((modifiers & IConstants.AccFinal) == 0) {
				keywords[count++] = Keywords.FINAL;
			}
			
			// static
			if((modifiers & IConstants.AccStatic) == 0) {
				keywords[count++] = Keywords.STATIC;
			}
			
			boolean canBeField = true;
			boolean canBeMethod = true;
			boolean canBeType = true;
			if((modifiers & IConstants.AccNative) != 0
				|| (modifiers & IConstants.AccStrictfp) != 0
				|| (modifiers & IConstants.AccSynchronized) != 0) {
				canBeField = false;
				canBeType = false;
			}
			
			if((modifiers & IConstants.AccTransient) != 0
				|| (modifiers & IConstants.AccVolatile) != 0) {
				canBeMethod = false;
				canBeType = false;
			}
			
			if(canBeField) {
				// transient
				if((modifiers & IConstants.AccTransient) == 0) {
					keywords[count++] = Keywords.TRANSIENT;
				}
				
				// volatile
				if((modifiers & IConstants.AccVolatile) == 0) {
					keywords[count++] = Keywords.VOLATILE;
				}
			}
			
			if(canBeMethod) {
				// native
				if((modifiers & IConstants.AccNative) == 0) {
					keywords[count++] = Keywords.NATIVE;
				}
	
				// strictfp
				if((modifiers & IConstants.AccStrictfp) == 0) {
					keywords[count++] = Keywords.STRICTFP;
				}
				
				// synchronized
				if((modifiers & IConstants.AccSynchronized) == 0) {
					keywords[count++] = Keywords.SYNCHRONIZED;
				}
			}
			
			if(canBeType) {
				keywords[count++] = Keywords.CLASS;
				keywords[count++] = Keywords.INTERFACE;
			}
		} else {
			// class
			keywords[count++] = Keywords.CLASS;
			keywords[count++] = Keywords.INTERFACE;
		}
		System.arraycopy(keywords, 0, keywords = new char[count][], 0, count);
		
		findKeywords(token, keywords);
	}

	// Helper method for findMemberTypes(char[], ReferenceBinding, Scope)
	private void findMemberTypes(
		char[] typeName,
		ReferenceBinding[] memberTypes,
		ObjectVector typesFound,
		ReferenceBinding receiverType,
		SourceTypeBinding invocationType,
		boolean staticOnly) {

		// Inherited member types which are hidden by subclasses are filtered out
		// No visibility checks can be performed without the scope & invocationSite
		int typeLength = typeName.length;
		next : for (int m = memberTypes.length; --m >= 0;) {
			ReferenceBinding memberType = memberTypes[m];
			//		if (!wantClasses && memberType.isClass()) continue next;
			//		if (!wantInterfaces && memberType.isInterface()) continue next;
			
			if (staticOnly && !memberType.isStatic()) continue next;
			
			if (isForbidden(memberType)) continue next;
			
			if (typeLength > memberType.sourceName.length)
				continue next;

			if (!CharOperation.prefixEquals(typeName, memberType.sourceName, false
				/* ignore case */
				))
				continue next;

			if (this.options.checkVisibility) {
				if (invocationType != null && !memberType.canBeSeenBy(receiverType, invocationType)) {
					continue next;
				} else if(invocationType == null && !memberType.canBeSeenBy(this.unitScope.fPackage)) {
					continue next;
				}
			}

			for (int i = typesFound.size; --i >= 0;) {
				ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(i);

				if (memberType == otherType)
					continue next;

				if (CharOperation.equals(memberType.sourceName, otherType.sourceName, true)) {

					if (memberType.enclosingType().isSuperclassOf(otherType.enclosingType()))
						continue next;

					if (otherType.enclosingType().isInterface())
						if (memberType.enclosingType()
							.implementsInterface(otherType.enclosingType(), true))
							continue next;

					if (memberType.enclosingType().isInterface())
						if (otherType.enclosingType()
							.implementsInterface(memberType.enclosingType(), true))
							continue next;
				}
			}

			typesFound.add(memberType);

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(typeName, memberType.sourceName);
			relevance += computeRelevanceForExpectingType(memberType);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			if (memberType.isClass()) {
				relevance += computeRelevanceForClass();
				relevance += computeRelevanceForException(memberType.sourceName);
			} else if(memberType.isEnum()) {
				relevance += computeRelevanceForEnum();
			} else if(memberType.isInterface()) {
				relevance += computeRelevanceForInterface();
			}
				
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(memberType.qualifiedPackageName());
				proposal.setSignature(getSignature(memberType));
				proposal.setPackageName(memberType.qualifiedPackageName());
				proposal.setTypeName(memberType.qualifiedSourceName());
				proposal.setCompletion(memberType.sourceName());
				proposal.setFlags(memberType.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	private void findMemberTypes(
		char[] typeName,
		ReferenceBinding receiverType,
		Scope scope,
		SourceTypeBinding typeInvocation,
		boolean staticOnly,
		ObjectVector typesFound) {

		ReferenceBinding currentType = receiverType;
		if (typeName == null)
			return;

		if (currentType.superInterfaces() == null)
			return; // we're trying to find a supertype

		if (this.insideQualifiedReference
			|| typeName.length == 0) { // do not search up the hierarchy

			findMemberTypes(
				typeName,
				currentType.memberTypes(),
				typesFound,
				receiverType,
				typeInvocation,
				staticOnly);
			return;
		}

		ReferenceBinding[][] interfacesToVisit = null;
		int lastPosition = -1;

		do {

			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != NoSuperInterfaces) {

				if (interfacesToVisit == null)
					interfacesToVisit = new ReferenceBinding[5][];

				if (++lastPosition == interfacesToVisit.length)
					System.arraycopy(
						interfacesToVisit,
						0,
						interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
						0,
						lastPosition);
				interfacesToVisit[lastPosition] = itsInterfaces;
			}

			findMemberTypes(
				typeName,
				currentType.memberTypes(),
				typesFound,
				receiverType,
				typeInvocation,
				staticOnly);
			currentType = currentType.superclass();

		} while (currentType != null);

		if (interfacesToVisit != null) {
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++) {

					ReferenceBinding anInterface = interfaces[j];
					if ((anInterface.tagBits & TagBits.InterfaceVisited) == 0) {
						// if interface as not already been visited
						anInterface.tagBits |= TagBits.InterfaceVisited;

						findMemberTypes(
							typeName,
							anInterface.memberTypes(),
							typesFound,
							receiverType,
							typeInvocation,
							staticOnly);

						ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
						if (itsInterfaces != NoSuperInterfaces) {

							if (++lastPosition == interfacesToVisit.length)
								System.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0,
									lastPosition);
							interfacesToVisit[lastPosition] = itsInterfaces;
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];
				for (int j = 0, length = interfaces.length; j < length; j++)
					interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
			}
		}
	}

	private void findIntefacesMethods(
		char[] selector,
		TypeBinding[] argTypes,
		ReferenceBinding receiverType,
		ReferenceBinding[] itsInterfaces,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		boolean isCompletingDeclaration,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed) {

		if (selector == null)
			return;

		if (itsInterfaces != NoSuperInterfaces) {
			ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
			int lastPosition = 0;
			interfacesToVisit[lastPosition] = itsInterfaces;
			
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];

				for (int j = 0, length = interfaces.length; j < length; j++) {
					ReferenceBinding currentType = interfaces[j];

					if ((currentType.tagBits & TagBits.InterfaceVisited) == 0) {
						// if interface as not already been visited
						currentType.tagBits |= TagBits.InterfaceVisited;

						MethodBinding[] methods = currentType.availableMethods();
						if(methods != null) {
							if(isCompletingDeclaration){
	
								findLocalMethodDeclarations(
									selector,
									methods,
									scope,
									methodsFound,
									onlyStaticMethods,
									exactMatch,
									receiverType);
	
							} else {
	
								findLocalMethods(
									selector,
									argTypes,
									methods,
									scope,
									methodsFound,
									onlyStaticMethods,
									exactMatch,
									receiverType,
									invocationSite,
									invocationScope,
									implicitCall,
									superCall,
									canBePrefixed);
							}
						}

						itsInterfaces = currentType.superInterfaces();
						if (itsInterfaces != NoSuperInterfaces) {

							if (++lastPosition == interfacesToVisit.length)
								System.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0,
									lastPosition);
							interfacesToVisit[lastPosition] = itsInterfaces;
						}
					}
				}
			}

			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];

				for (int j = 0, length = interfaces.length; j < length; j++){
					interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
				}
			}
		}
	}
	
	private void findImplicitMessageSends(
		char[] token,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope) {

		if (token == null)
			return;

		boolean staticsOnly = false;
		// need to know if we're in a static context (or inside a constructor)
		ObjectVector methodsFound = new ObjectVector();

		done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found

			switch (scope.kind) {

				case Scope.METHOD_SCOPE :
					// handle the error case inside an explicit constructor call (see MethodScope>>findField)
					MethodScope methodScope = (MethodScope) scope;
					staticsOnly |= methodScope.isStatic | methodScope.isConstructorCall;
					break;

				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) scope;
					SourceTypeBinding enclosingType = classScope.referenceContext.binding;
					findMethods(
						token,
						argTypes,
						enclosingType,
						classScope,
						methodsFound,
						staticsOnly,
						true,
						false,
						invocationSite,
						invocationScope,
						true,
						false,
						true);
					staticsOnly |= enclosingType.isStatic();
					break;

				case Scope.COMPILATION_UNIT_SCOPE :
					break done;
			}
			scope = scope.parent;
		}
	}

	// Helper method for findMethods(char[], TypeBinding[], ReferenceBinding, Scope, ObjectVector, boolean, boolean, boolean)
	private void findLocalMethods(
		char[] methodName,
		TypeBinding[] argTypes,
		MethodBinding[] methods,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		ReferenceBinding receiverType,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed) {

		ObjectVector newMethodsFound =  new ObjectVector();
		// Inherited methods which are hidden by subclasses are filtered out
		// No visibility checks can be performed without the scope & invocationSite

		int methodLength = methodName.length;
		int minArgLength = argTypes == null ? 0 : argTypes.length;

		next : for (int f = methods.length; --f >= 0;) {
			MethodBinding method = methods[f];

			if (method.isSynthetic()) continue next;

			if (method.isDefaultAbstract())	continue next;

			if (method.isConstructor()) continue next;

			//TODO (david) perhaps the relevance of a void method must be lesser than other methods
			//if (expectedTypesPtr > -1 && method.returnType == BaseTypes.VoidBinding) continue next;
			
			if (onlyStaticMethods && !method.isStatic()) continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

			if(superCall && method.isAbstract()) {
				methodsFound.add(new Object[]{method, receiverType});
				continue next;
			}

			if (exactMatch) {
				if (!CharOperation.equals(methodName, method.selector, false /* ignore case */
					))
					continue next;

			} else {

				if (methodLength > method.selector.length)
					continue next;

				if (!CharOperation.prefixEquals(methodName, method.selector, false
					/* ignore case */
					))
					continue next;
			}
			if (minArgLength > method.parameters.length)
				continue next;

			for (int a = minArgLength; --a >= 0;){
				if (argTypes[a] != null){ // can be null if it could not be resolved properly
					if (!argTypes[a].isCompatibleWith(method.parameters[a])) {
						continue next;
					}
				}
			}
			
			boolean prefixRequired = false;
			
			for (int i = methodsFound.size; --i >= 0;) {
				Object[] other = (Object[]) methodsFound.elementAt(i);
				MethodBinding otherMethod = (MethodBinding) other[0];
				ReferenceBinding otherReceiverType = (ReferenceBinding) other[1];
				if (method == otherMethod && receiverType == otherReceiverType)
					continue next;

				if (CharOperation.equals(method.selector, otherMethod.selector, true)
					&& method.areParametersEqual(otherMethod)) {

					if (method.declaringClass.isSuperclassOf(otherMethod.declaringClass))
						continue next;

					if (otherMethod.declaringClass.isInterface()) {
						if(method.declaringClass == scope.getJavaLangObject())
							continue next;
						
						if (method.declaringClass.isInterface())
							continue next;
						
						if (!superCall && method
							.declaringClass
							.implementsInterface(otherMethod.declaringClass, true))
							continue next;
					}

					if (method.declaringClass.isInterface())
						if(otherMethod
							.declaringClass
							.implementsInterface(method.declaringClass,true))
							continue next;
						
					if(receiverType.isAnonymousType()) continue next;
					
					if(!superCall) {
						if(canBePrefixed) {
							prefixRequired = true;
						} else {
							continue next;
						}
					}
				}
			}

			newMethodsFound.add(new Object[]{method, receiverType});
			int length = method.parameters.length;
			char[][] parameterPackageNames = new char[length][];
			char[][] parameterTypeNames = new char[length][];

			for (int i = 0; i < length; i++) {
				TypeBinding type = method.original().parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			char[] completion = CharOperation.NO_CHAR;
			
			int previousStartPosition = this.startPosition;
			
			// nothing to insert - do not want to replace the existing selector & arguments
			if (!exactMatch) {
				if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(')
					completion = method.selector;
				else
					completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
			} else {
				if(prefixRequired && (this.source != null)) {
					completion = CharOperation.subarray(this.source, this.startPosition, this.endPosition);
				} else {
					this.startPosition = this.endPosition;
				}
			}
			
			if(prefixRequired || this.options.forceImplicitQualification){
				char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), method.isStatic());
				completion = CharOperation.concat(prefix,completion,'.');
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += computeRelevanceForExpectingType(method.returnType);
			relevance += computeRelevanceForStatic(onlyStaticMethods, method.isStatic());
			relevance += computeRelevanceForQualification(prefixRequired);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method));
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setName(method.selector);
				proposal.setCompletion(completion);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
			this.startPosition = previousStartPosition;
		}
		
		methodsFound.addAll(newMethodsFound);
	}
	
	// Helper method for findMethods(char[], TypeBinding[], ReferenceBinding, Scope, ObjectVector, boolean, boolean, boolean)
	private void findLocalMethodsOfStaticImports(
		char[] methodName,
		MethodBinding[] methods,
		Scope scope,
		ReferenceBinding receiverType,
		InvocationSite invocationSite) {

		next : for (int f = methods.length; --f >= 0;) {
			MethodBinding method = methods[f];

			if (method.isSynthetic()) continue next;

			if (method.isDefaultAbstract())	continue next;

			if (method.isConstructor()) continue next;

			if (!method.isStatic()) continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

			if (!CharOperation.equals(methodName, method.selector, false /* ignore case */))
				continue next;

			int length = method.parameters.length;
			char[][] parameterPackageNames = new char[length][];
			char[][] parameterTypeNames = new char[length][];

			for (int i = 0; i < length; i++) {
				TypeBinding type = method.original().parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			char[] completion = CharOperation.NO_CHAR;
			
			int previousStartPosition = this.startPosition;
			
			// nothing to insert - do not want to replace the existing selector & arguments
			if (this.source != null
				&& this.source.length > this.endPosition
				&& this.source[this.endPosition] == '(') {
				completion = method.selector;
			} else {
				completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
			}
			
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += computeRelevanceForExpectingType(method.returnType);
			relevance += computeRelevanceForStatic(true, method.isStatic());
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method));
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setName(method.selector);
				proposal.setCompletion(completion);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
			this.startPosition = previousStartPosition;
		}
	}
	
	int computeRelevanceForCaseMatching(char[] token, char[] proposalName){
		if (CharOperation.prefixEquals(token, proposalName, true /* do not ignore case */)) {
			if(CharOperation.equals(token, proposalName, true /* do not ignore case */)) {
				return R_CASE + R_EXACT_NAME;
			} else {
				return R_CASE;
			}
		} else {
			if(CharOperation.equals(token, proposalName, false /* ignore case */)) {
				return R_EXACT_NAME;
			}
			return 0;
		}
	}
	private int computeRelevanceForAnnotation(){
		if(this.assistNodeIsAnnotation) {
			return R_ANNOTATION;
		}
		return 0;
	}
	private int computeRelevanceForClass(){
		if(this.assistNodeIsClass) {
			return R_CLASS;
		}
		return 0;
	}
	private int computeRelevanceForEnum(){
		if(this.assistNodeIsEnum) {
			return R_ENUM;
		}
		return 0;
	}
	private int computeRelevanceForInterface(){
		if(this.assistNodeIsInterface) {
			return R_INTERFACE;
		}
		return 0;
	}
	private int computeRelevanceForQualification(boolean prefixRequired) {
		if(!prefixRequired && !this.insideQualifiedReference) {
			return R_UNQUALIFIED;
		}
		
		if(prefixRequired && this.insideQualifiedReference) {
			return R_QUALIFIED;
		}
		return 0;
	}
	int computeRelevanceForRestrictions(int accessRuleKind) {
		if(accessRuleKind == IAccessRule.K_ACCESSIBLE) {
			return R_NON_RESTRICTED;
		}
		return 0;
	}
	private int computeRelevanceForStatic(boolean onlyStatic, boolean isStatic) {
		if(this.insideQualifiedReference && !onlyStatic && !isStatic) {
			return R_NON_STATIC;
		}
		return 0;
	}
	private int computeRelevanceForStaticOveride(boolean isStatic) {
		return isStatic ? 0 : R_NON_STATIC_OVERIDE;
	}
	private int computeRelevanceForException(char[] proposalName){
		
		if(this.assistNodeIsException &&
			(CharOperation.match(EXCEPTION_PATTERN, proposalName, false) ||
			CharOperation.match(ERROR_PATTERN, proposalName, false))) { 
			return R_EXCEPTION;
		}
		return 0;
	}
	private int computeRelevanceForExpectingType(TypeBinding proposalType){
		if(this.expectedTypes != null && proposalType != null) {
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
                int relevance = R_EXPECTED_TYPE;
				if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), proposalType.qualifiedPackageName()) &&
					CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName())) {
                    relevance = R_EXACT_EXPECTED_TYPE;
				}
				if((this.expectedTypesFilter & SUBTYPE) != 0
					&& proposalType.isCompatibleWith(this.expectedTypes[i])) {
						return relevance;
				}
				if((this.expectedTypesFilter & SUPERTYPE) != 0
					&& this.expectedTypes[i].isCompatibleWith(proposalType)) {
					return relevance;
				}
			}
		} 
		return 0;
	}
	private int computeRelevanceForExpectingType(char[] packageName, char[] typeName){
		if(this.expectedTypes != null) {
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
				if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), packageName) &&
					CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), typeName)) {
					return R_EXACT_EXPECTED_TYPE;
				}
			}
		} 
		return 0;
	}
	int computeRelevanceForInterestingProposal(){
		return computeRelevanceForInterestingProposal(null);
	}
	private int computeRelevanceForInterestingProposal(Binding binding){
		if(this.uninterestingBindings != null) {
			for (int i = 0; i <= this.uninterestingBindingsPtr; i++) {
				if(this.uninterestingBindings[i] == binding) {
					return 0;
				}
			}
		}
		return R_INTERESTING;
	}
	private void computeUninterestingBindings(ASTNode parent, Scope scope){
		if(parent instanceof LocalDeclaration) {
			addUninterestingBindings(((LocalDeclaration)parent).binding);
		} else if (parent instanceof FieldDeclaration) {
			addUninterestingBindings(((FieldDeclaration)parent).binding);
		} 
	}
	// Helper method for findMethods(char[], MethodBinding[], Scope, ObjectVector, boolean, boolean, boolean, TypeBinding)
	private void findLocalMethodDeclarations(
		char[] methodName,
		MethodBinding[] methods,
		Scope scope,
		ObjectVector methodsFound,
		//	boolean noVoidReturnType, how do you know?
		boolean onlyStaticMethods,
		boolean exactMatch,
		ReferenceBinding receiverType) {

		ObjectVector newMethodsFound =  new ObjectVector();
		// Inherited methods which are hidden by subclasses are filtered out
		// No visibility checks can be performed without the scope & invocationSite
		int methodLength = methodName.length;
		next : for (int f = methods.length; --f >= 0;) {

			MethodBinding method = methods[f];
			if (method.isSynthetic())	continue next;
				
			if (method.isDefaultAbstract()) continue next;
			
			if (method.isConstructor()) continue next;
				
			if (method.isFinal()) {
                newMethodsFound.add(method);
                continue next;
            }

			//		if (noVoidReturnType && method.returnType == BaseTypes.VoidBinding) continue next;
			if(method.isStatic()) {
				if(receiverType.isAnonymousType()) continue next;
				
				if(receiverType.isMemberType() && !receiverType.isStatic()) continue next;
				
				if(receiverType.isLocalType()) continue next;
			} else  {
				if(onlyStaticMethods) continue next;
			}

			if (!method.canBeSeenBy(receiverType, FakeInvocationSite , scope)) continue next;

			if (exactMatch) {
				if (!CharOperation.equals(methodName, method.selector, false /* ignore case */
					))
					continue next;

			} else {

				if (methodLength > method.selector.length)
					continue next;

				if (!CharOperation.prefixEquals(methodName, method.selector, false
					/* ignore case */
					))
					continue next;
			}

			for (int i = methodsFound.size; --i >= 0;) {
				MethodBinding otherMethod = (MethodBinding) methodsFound.elementAt(i);
				if (method == otherMethod)
					continue next;

				if (CharOperation.equals(method.selector, otherMethod.selector, true)
					&& method.areParametersEqual(otherMethod)) {
					continue next;
				}
			}

			newMethodsFound.add(method);
			
			int length = method.parameters.length;
			char[][] parameterPackageNames = new char[length][];
			char[][] parameterTypeNames = new char[length][];
			
			for (int i = 0; i < length; i++) {
				TypeBinding type = method.parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}

			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);
			
			StringBuffer completion = new StringBuffer(10);
			// flush uninteresting modifiers
			int insertedModifiers = method.modifiers & ~(IConstants.AccNative | IConstants.AccAbstract);

			if (!exactMatch) {
				if(insertedModifiers != CompilerModifiers.AccDefault){
					ASTNode.printModifiers(insertedModifiers, completion);
				}
				char[] returnPackageName = method.returnType.qualifiedPackageName();
				char[] returnTypeName = method.returnType.qualifiedSourceName();
				if(mustQualifyType(returnPackageName, returnTypeName)) {
					completion.append(CharOperation.concat(returnPackageName, returnTypeName,'.'));
				} else {
					completion.append(method.returnType.sourceName());
				}
				completion.append(' ');
				completion.append(method.selector);
				completion.append('(');

				for(int i = 0; i < length ; i++){
					if(mustQualifyType(parameterPackageNames[i], parameterTypeNames[i])){
						completion.append(CharOperation.concat(parameterPackageNames[i], parameterTypeNames[i], '.'));
					} else {
						completion.append(parameterTypeNames[i]);
					}
					completion.append(' ');
					if(parameterNames != null){
						completion.append(parameterNames[i]);
					} else {
						completion.append('%');
					}
					if(i != (length - 1))
						completion.append(',');	
				}
				completion.append(')');
				
				ReferenceBinding[] exceptions = method.thrownExceptions;
				
				if (exceptions != null && exceptions.length > 0){
					completion.append(' ');
					completion.append(THROWS);
					completion.append(' ');
					for(int i = 0; i < exceptions.length ; i++){
						ReferenceBinding exception = exceptions[i];

						char[] exceptionPackageName = exception.qualifiedPackageName();
						char[] exceptionTypeName = exception.qualifiedSourceName();
						
						if(i != 0){
							completion.append(',');
							completion.append(' ');
						}
						
						if(mustQualifyType(exceptionPackageName, exceptionTypeName)){
							completion.append(CharOperation.concat(exceptionPackageName, exceptionTypeName, '.'));
						} else {
							completion.append(exception.sourceName());
						}
					}
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += computeRelevanceForStaticOveride(method.isStatic());
			if(method.isAbstract()) relevance += R_ABSTRACT_METHOD;
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.METHOD_DECLARATION, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setDeclarationKey(method.declaringClass.computeUniqueKey());
				proposal.setSignature(getSignature(method));
				proposal.setKey(method.computeUniqueKey());
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setCompletion(completion.toString().toCharArray());
				proposal.setName(method.selector);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
		methodsFound.addAll(newMethodsFound);
	}
	private void findMethods(
		char[] selector,
		TypeBinding[] argTypes,
		ReferenceBinding receiverType,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		boolean isCompletingDeclaration,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed) {
		if (selector == null)
			return;
		
		if(isCompletingDeclaration) {
			MethodBinding[] methods = receiverType.availableMethods();
			if (methods != null){
				for (int i = 0; i < methods.length; i++) {
					if(!methods[i].isDefaultAbstract()) {
						methodsFound.add(methods[i]);
					}
				}
			}
		}
		
		ReferenceBinding currentType = receiverType;
		if (receiverType.isInterface()) {
			if(isCompletingDeclaration) {
				findIntefacesMethods(
					selector,
					argTypes,
					receiverType,
					currentType.superInterfaces(),
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					isCompletingDeclaration,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed);
			} else {
				findIntefacesMethods(
					selector,
					argTypes,
					receiverType,
					new ReferenceBinding[]{currentType},
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					isCompletingDeclaration,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed);
			}
			
			currentType = scope.getJavaLangObject();
		} else {
			if(isCompletingDeclaration){
				findIntefacesMethods(
					selector,
					argTypes,
					receiverType,
					currentType.superInterfaces(),
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					isCompletingDeclaration,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed);
				
				currentType = receiverType.superclass();
			}
		}
		boolean hasPotentialDefaultAbstractMethods = true;
		while (currentType != null) {
			
			MethodBinding[] methods = currentType.availableMethods();
			if(methods != null) {
				if(isCompletingDeclaration){
					findLocalMethodDeclarations(
						selector,
						methods,
						scope,
						methodsFound,
						onlyStaticMethods,
						exactMatch,
						receiverType);
				} else{
					findLocalMethods(
						selector,
						argTypes,
						methods,
						scope,
						methodsFound,
						onlyStaticMethods,
						exactMatch,
						receiverType,
						invocationSite,
						invocationScope,
						implicitCall,
						superCall,
						canBePrefixed);
				}
			}
			
			if(hasPotentialDefaultAbstractMethods && currentType.isAbstract()){
				findIntefacesMethods(
					selector,
					argTypes,
					receiverType,
					currentType.superInterfaces(),
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					isCompletingDeclaration,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed);
			} else {
				hasPotentialDefaultAbstractMethods = false;
			}
			currentType = currentType.superclass();
		}
	}
	private char[][] findMethodParameterNames(MethodBinding method, char[][] parameterTypeNames){
		ReferenceBinding bindingType = method.declaringClass;

		char[][] parameterNames = null;
		
		int length = parameterTypeNames.length;

		if (length == 0){
			return CharOperation.NO_CHAR_CHAR;
		}
		// look into the corresponding unit if it is available
		if (bindingType instanceof SourceTypeBinding){
			SourceTypeBinding sourceType = (SourceTypeBinding) bindingType;

			if (sourceType.scope != null){
				TypeDeclaration parsedType;

				if ((parsedType = sourceType.scope.referenceContext) != null){
					AbstractMethodDeclaration methodDecl = parsedType.declarationOf(method.original());

					if (methodDecl != null){
						Argument[] arguments = methodDecl.arguments;
						parameterNames = new char[length][];

						for(int i = 0 ; i < length ; i++){
							parameterNames[i] = arguments[i].name;
						}
					}
				}
			}
		}
		// look into the model		
		if(parameterNames == null){
			char[] compoundName = CharOperation.concatWith(bindingType.compoundName, '.');
			Object type = this.typeCache.get(compoundName);
			
			ISourceType sourceType = null;
			if(type != null) {
				if(type instanceof ISourceType) {
					sourceType = (ISourceType) type;
				}
			} else {
				NameEnvironmentAnswer answer = this.nameEnvironment.findType(bindingType.compoundName);
				if(answer != null && answer.isSourceType()) {
					sourceType = answer.getSourceTypes()[0];
					this.typeCache.put(compoundName, sourceType);
				}
			}
			
			if(sourceType != null) {
				SourceMethod[] sourceMethods = ((SourceTypeElementInfo) sourceType).getMethodHandles();
				int len = sourceMethods.length;
				for(int i = 0; i < len ; i++){
					SourceMethod sourceMethod = sourceMethods[i];
					String[] argTypeSignatures = sourceMethod.getParameterTypes();

					if(argTypeSignatures != null &&
						CharOperation.equals(method.selector,sourceMethod.getElementName().toCharArray()) &&
						equalSignatures(parameterTypeNames, argTypeSignatures)){
						try {
							parameterNames = ((SourceMethodElementInfo) sourceMethod.getElementInfo()).getArgumentNames();
							break;
						} catch (JavaModelException e) {
							// method doesn't exist: ignore
						}
					}
				}
			}
		}
		return parameterNames;
	}
	
	private boolean equalSignatures(char[][] typeNames, String[] typeSignatures) {
		if (typeNames == null || typeSignatures == null)
			return false;
		if (typeNames.length != typeSignatures.length)
			return false;

		for (int i = typeNames.length; --i >= 0;)
			if (!CharOperation.equals(typeNames[i], Signature.toCharArray(typeSignatures[i].toCharArray())))
				return false;
		return true;
	}
	
	private void findNestedTypes(
		char[] typeName,
		SourceTypeBinding currentType,
		Scope scope,
		ObjectVector typesFound) {
		if (typeName == null)
			return;

		int typeLength = typeName.length;

		while (scope != null) { // done when a COMPILATION_UNIT_SCOPE is found

			switch (scope.kind) {

				case Scope.METHOD_SCOPE :
				case Scope.BLOCK_SCOPE :
					BlockScope blockScope = (BlockScope) scope;

					next : for (int i = 0, length = blockScope.subscopeCount; i < length; i++) {

						if (blockScope.subscopes[i] instanceof ClassScope) {
							SourceTypeBinding localType =
								((ClassScope) blockScope.subscopes[i]).referenceContext.binding;

							if (!localType.isAnonymousType()) {
								if (this.isForbidden(localType))
									continue next;
								if (typeLength > localType.sourceName.length)
									continue next;
								if (!CharOperation.prefixEquals(typeName, localType.sourceName, false
									/* ignore case */
									))
									continue next;

								int relevance = computeBaseRelevance();
								relevance += computeRelevanceForInterestingProposal();
								relevance += computeRelevanceForCaseMatching(typeName, localType.sourceName);
								relevance += computeRelevanceForExpectingType(localType);
								relevance += computeRelevanceForException(localType.sourceName);
								relevance += computeRelevanceForClass();
								relevance += computeRelevanceForQualification(false);
								relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for nested type
								
								this.noProposal = false;
								if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
									CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
									proposal.setDeclarationSignature(localType.qualifiedPackageName());
									proposal.setSignature(getSignature(localType));
									proposal.setPackageName(localType.qualifiedPackageName());
									proposal.setTypeName(localType.sourceName);
									proposal.setCompletion(localType.sourceName);
									proposal.setFlags(localType.modifiers);
									proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
									proposal.setRelevance(relevance);
									this.requestor.accept(proposal);
									if(DEBUG) {
										this.printDebug(proposal);
									}
								}
							}
						}
					}
					break;

				case Scope.CLASS_SCOPE :
					findMemberTypes(typeName, scope.enclosingSourceType(), scope, currentType, false, typesFound);
					if (typeLength == 0)
						return; // do not search outside the class scope if no prefix was provided
					break;

				case Scope.COMPILATION_UNIT_SCOPE :
					return;
			}
			scope = scope.parent;
		}
	}

	private void findPackages(CompletionOnPackageReference packageStatement) {

		this.completionToken = CharOperation.concatWith(packageStatement.tokens, '.');
		if (this.completionToken.length == 0)
			return;

		setSourceRange(packageStatement.sourceStart, packageStatement.sourceEnd);
		this.nameEnvironment.findPackages(CharOperation.toLowerCase(this.completionToken), this);
	}

	private void findParameterizedType(TypeReference ref) {
		ReferenceBinding refBinding = (ReferenceBinding) ref.resolvedType;
		if(refBinding != null) {
			char[] packageName = refBinding.qualifiedPackageName();
			char[] typeName = refBinding.qualifiedSourceName();
			
			int accessibility = IAccessRule.K_ACCESSIBLE;
			if(refBinding.hasRestrictedAccess()) {
				AccessRestriction accessRestriction = lookupEnvironment.getAccessRestriction(refBinding);
				if(accessRestriction != null) {
					switch (accessRestriction.getProblemId()) {
						case IProblem.ForbiddenReference:
							switch (this.options.restrictedReferenceFilter) {
								case AssistOptions.FILTER_WARNING:
									return;
								case AssistOptions.FILTER_ERROR:
									if(this.forbiddenReferenceIsError) return;
									break;
								
							}
							accessibility = IAccessRule.K_NON_ACCESSIBLE;
							break;
						case IProblem.DiscouragedReference:
							switch (this.options.restrictedReferenceFilter) {
								case AssistOptions.FILTER_WARNING:
									return;
								case AssistOptions.FILTER_ERROR:
									if(this.discouragedReferenceIsError) return;
									break;
								
							}
							accessibility = IAccessRule.K_DISCOURAGED;
							break;
					}
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(refBinding.sourceName, refBinding.sourceName);
			relevance += computeRelevanceForExpectingType(refBinding);
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(accessibility); // no access restriction for type in the current unit
			
			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(packageName);
				proposal.setSignature(getSignature(refBinding));
				proposal.setPackageName(packageName);
				proposal.setTypeName(typeName);
				proposal.setCompletion(CharOperation.NO_CHAR);
				proposal.setFlags(refBinding.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setRelevance(relevance);
				proposal.setAccessibility(accessibility);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	private void findTypeParameters(char[] token, Scope scope) {
		if (this.compilerOptions.sourceLevel < ClassFileConstants.JDK1_5) return;
		
		TypeParameter[] typeParameters = null;
		while (scope != null) { // done when a COMPILATION_UNIT_SCOPE is found
			typeParameters = null;
			switch (scope.kind) {
				case Scope.METHOD_SCOPE :
					MethodScope methodScope = (MethodScope) scope;
					if(methodScope.referenceContext instanceof MethodDeclaration) {
						MethodDeclaration methodDeclaration = (MethodDeclaration) methodScope.referenceContext;
						typeParameters = methodDeclaration.typeParameters;
					} else if(methodScope.referenceContext instanceof ConstructorDeclaration) {
						ConstructorDeclaration methodDeclaration = (ConstructorDeclaration) methodScope.referenceContext;
						typeParameters = methodDeclaration.typeParameters;
					}
					break;
				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) scope;
					typeParameters = classScope.referenceContext.typeParameters;
					break;
				case Scope.COMPILATION_UNIT_SCOPE :
					return;
			}
			if(typeParameters != null) {
				for (int i = 0; i < typeParameters.length; i++) {
					int typeLength = token.length;
					TypeParameter typeParameter = typeParameters[i];
					
					if(typeParameter.binding == null) continue;
					
					if (typeLength > typeParameter.name.length) continue;
					
					if (!CharOperation.prefixEquals(token, typeParameter.name, false)) continue;
	
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(token, typeParameter.name);
					relevance += computeRelevanceForExpectingType(typeParameter.type == null ? null :typeParameter.type.resolvedType);
					relevance += computeRelevanceForQualification(false);
					relevance += computeRelevanceForException(typeParameter.name);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction fot type parameter
					
					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
						CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
						//proposal.setDeclarationSignature(null);
						proposal.setSignature(getSignature(typeParameter.binding));
						//proposal.setPackageName(null);
						proposal.setTypeName(typeParameter.name);
						proposal.setCompletion(typeParameter.name);
						proposal.setFlags(typeParameter.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}
			scope = scope.parent;
		}
	}
	
	private void findTypesAndPackages(char[] token, Scope scope) {

		if (token == null)
			return;
		
		// do not propose type if completion token is empty
		if (NO_TYPE_COMPLETION_ON_EMPTY_TOKEN && token.length == 0) return;
		
		boolean proposeType = !this.requestor.isIgnored(CompletionProposal.TYPE_REF);
		
		ObjectVector typesFound = new ObjectVector();
		
		if (proposeType && scope.enclosingSourceType() != null) {
			findNestedTypes(token, scope.enclosingSourceType(), scope, typesFound);
			findTypeParameters(token, scope);
		}

		if (proposeType && this.unitScope != null) {
			int typeLength = token.length;
			SourceTypeBinding[] types = this.unitScope.topLevelTypes;

			for (int i = 0, length = types.length; i < length; i++) {
				SourceTypeBinding sourceType = types[i]; 
				
				if (sourceType.sourceName == CompletionParser.FAKE_TYPE_NAME) continue;
				if (sourceType.sourceName == TypeConstants.PACKAGE_INFO_NAME) continue;

				if (typeLength > sourceType.sourceName.length)	continue;
				
				if (!CharOperation.prefixEquals(token, sourceType.sourceName, false))	continue;
				
				this.knownTypes.put(CharOperation.concat(sourceType.qualifiedPackageName(), sourceType.sourceName(), '.'), this);

				if(isForbidden(sourceType)) continue;
					
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(token, sourceType.sourceName);
				relevance += computeRelevanceForExpectingType(sourceType);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for type in the current unit

				if (sourceType.isAnnotationType()) {
					relevance += computeRelevanceForAnnotation();
				} else if (sourceType.isInterface()) {
					relevance += computeRelevanceForInterface();
				} else {
					relevance += computeRelevanceForClass();
					relevance += computeRelevanceForException(sourceType.sourceName);
				}
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
					proposal.setDeclarationSignature(sourceType.qualifiedPackageName());
					proposal.setSignature(getSignature(sourceType));
					proposal.setPackageName(sourceType.qualifiedPackageName());
					proposal.setTypeName(sourceType.sourceName());
					proposal.setCompletion(sourceType.sourceName());
					proposal.setFlags(sourceType.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
		
		if(proposeType) {
			this.findTypesFromStaticImports(token, scope, typesFound);
		}
		
		if (token.length == 0) {
			if(proposeType && this.expectedTypesPtr > -1) {
				next : for (int i = 0; i <= this.expectedTypesPtr; i++) {
					if(this.expectedTypes[i] instanceof ReferenceBinding) {
						ReferenceBinding refBinding = (ReferenceBinding)this.expectedTypes[i];
						
						int accessibility = IAccessRule.K_ACCESSIBLE;
						if(refBinding.hasRestrictedAccess()) {
							AccessRestriction accessRestriction = lookupEnvironment.getAccessRestriction(refBinding);
							if(accessRestriction != null) {
								switch (accessRestriction.getProblemId()) {
									case IProblem.ForbiddenReference:
										switch (this.options.restrictedReferenceFilter) {
											case AssistOptions.FILTER_WARNING:
												return;
											case AssistOptions.FILTER_ERROR:
												if(this.forbiddenReferenceIsError) return;
												break;
											
										}
										accessibility = IAccessRule.K_NON_ACCESSIBLE;
										break;
									case IProblem.DiscouragedReference:
										switch (this.options.restrictedReferenceFilter) {
											case AssistOptions.FILTER_WARNING:
												return;
											case AssistOptions.FILTER_ERROR:
												if(this.discouragedReferenceIsError) return;
												break;
											
										}
										accessibility = IAccessRule.K_DISCOURAGED;
										break;
								}
							}
						}
						
						boolean inSameUnit = this.unitScope.isDefinedInSameUnit(refBinding);
						
						// top level types of the current unit are already proposed.
						if(!inSameUnit || (inSameUnit && refBinding.isMemberType())) {
							char[] packageName = refBinding.qualifiedPackageName();
							char[] typeName = refBinding.sourceName();
							char[] completionName = typeName;
							
							boolean isQualified = false;
							if (!this.insideQualifiedReference && !refBinding.isMemberType()) {
								if (mustQualifyType(packageName, typeName)) {
									if (packageName == null || packageName.length == 0)
										if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
											continue next; // ignore types from the default package from outside it
									completionName = CharOperation.concat(packageName, typeName, '.');
									isQualified = true;
								}
							}
							
							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForInterestingProposal();
							relevance += computeRelevanceForCaseMatching(token, typeName);
							relevance += computeRelevanceForExpectingType(refBinding);
							relevance += computeRelevanceForQualification(isQualified);
							relevance += computeRelevanceForRestrictions(accessibility);
							
							if(refBinding.isClass()) {
								relevance += computeRelevanceForClass();
							} else if(refBinding.isEnum()) {
								relevance += computeRelevanceForEnum();
							} else if(refBinding.isInterface()) {
								relevance += computeRelevanceForInterface();
							}
								
							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
								CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								proposal.setDeclarationSignature(packageName);
								proposal.setSignature(getSignature(refBinding));
								proposal.setPackageName(packageName);
								proposal.setTypeName(typeName);
								proposal.setCompletion(completionName);
								proposal.setFlags(refBinding.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setRelevance(relevance);
								proposal.setAccessibility(accessibility);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
					}
				}
			} 
		} else {
			if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
				findKeywords(token, baseTypes);
			}
			if(proposeType) {
				this.nameEnvironment.findTypes(token, this);
			}
			if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
				this.nameEnvironment.findPackages(token, this);
			}
		}
	}

	private void findTypesAndSubpackages(
		char[] token,
		PackageBinding packageBinding) {

		boolean proposeType = !this.requestor.isIgnored(CompletionProposal.TYPE_REF);
		
		char[] qualifiedName =
			CharOperation.concatWith(packageBinding.compoundName, token, '.');

		if (token == null || token.length == 0) {
			int length = qualifiedName.length;
			System.arraycopy(
				qualifiedName,
				0,
				qualifiedName = new char[length + 1],
				0,
				length);
			qualifiedName[length] = '.';
		}
		
		this.qualifiedCompletionToken = qualifiedName;
		
		if (proposeType && this.unitScope != null) {
			int typeLength = qualifiedName.length;
			SourceTypeBinding[] types = this.unitScope.topLevelTypes;

			for (int i = 0, length = types.length; i < length; i++) {
				SourceTypeBinding sourceType = types[i]; 
	
				char[] qualifiedSourceTypeName = CharOperation.concatWith(sourceType.compoundName, '.');
				
				if (sourceType.sourceName == CompletionParser.FAKE_TYPE_NAME) continue;
				if (sourceType.sourceName == TypeConstants.PACKAGE_INFO_NAME) continue;
				if (typeLength > qualifiedSourceTypeName.length) continue;
				if (!(packageBinding == sourceType.getPackage())) continue;
				if (!CharOperation.prefixEquals(qualifiedName, qualifiedSourceTypeName, false))	continue;
				
				int accessibility = IAccessRule.K_ACCESSIBLE;
				if(sourceType.hasRestrictedAccess()) {
					AccessRestriction accessRestriction = lookupEnvironment.getAccessRestriction(sourceType);
					if(accessRestriction != null) {
						switch (accessRestriction.getProblemId()) {
							case IProblem.ForbiddenReference:
								switch (this.options.restrictedReferenceFilter) {
									case AssistOptions.FILTER_WARNING:
										return;
									case AssistOptions.FILTER_ERROR:
										if(this.forbiddenReferenceIsError) return;
										break;
									
								}
								accessibility = IAccessRule.K_NON_ACCESSIBLE;
								break;
							case IProblem.DiscouragedReference:
								switch (this.options.restrictedReferenceFilter) {
									case AssistOptions.FILTER_WARNING:
										return;
									case AssistOptions.FILTER_ERROR:
										if(this.discouragedReferenceIsError) return;
										break;
									
								}
								accessibility = IAccessRule.K_DISCOURAGED;
								break;
						}
					}
				}
				
				this.knownTypes.put(CharOperation.concat(sourceType.qualifiedPackageName(), sourceType.sourceName(), '.'), this);

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(qualifiedName, qualifiedSourceTypeName);
				relevance += computeRelevanceForExpectingType(sourceType);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(accessibility);
				
				if (sourceType.isAnnotationType()) {
					relevance += computeRelevanceForAnnotation();
				} else if (sourceType.isInterface()) {
					relevance += computeRelevanceForInterface();
				} else {
					relevance += computeRelevanceForClass();
					relevance += computeRelevanceForException(sourceType.sourceName);
				}
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
					CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
					proposal.setDeclarationSignature(sourceType.qualifiedPackageName());
					proposal.setSignature(getSignature(sourceType));
					proposal.setPackageName(sourceType.qualifiedPackageName());
					proposal.setTypeName(sourceType.sourceName());
					proposal.setCompletion(sourceType.sourceName());
					proposal.setFlags(sourceType.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setRelevance(relevance);
					proposal.setAccessibility(accessibility);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
		
		if(proposeType) {
			this.nameEnvironment.findTypes(qualifiedName, this);
		}
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			this.nameEnvironment.findPackages(qualifiedName, this);
		}
	}

	private void findTypesFromStaticImports(char[] token, Scope scope, ObjectVector typesFound) {
		ImportBinding[] importBindings = scope.compilationUnitScope().imports;
		for (int i = 0; i < importBindings.length; i++) {
			ImportBinding importBinding = importBindings[i];
			if(importBinding.isValidBinding() && importBinding.isStatic()) {
				Binding binding = importBinding.resolvedImport;
				if(binding != null && binding.isValidBinding()) {
					if(importBinding.onDemand) {
						if((binding.kind() & Binding.TYPE) != 0) {
							this.findMemberTypes(
									token,
									(ReferenceBinding) binding,
									scope,
									scope.enclosingSourceType(),
									true,
									typesFound);
						}
					} else {
						if ((binding.kind() & Binding.TYPE) != 0) {
							ReferenceBinding typeBinding = (ReferenceBinding) binding;
							int typeLength = token.length;
							
							if (!typeBinding.isStatic()) continue;
							
							if (typeLength > typeBinding.sourceName.length)	continue;
							
							if (!CharOperation.prefixEquals(token, typeBinding.sourceName, false))	continue;
							
							if (typesFound.contains(typeBinding))  continue;
							
							typesFound.add(typeBinding);
							
							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForInterestingProposal();
							relevance += computeRelevanceForCaseMatching(token, typeBinding.sourceName);
							relevance += computeRelevanceForExpectingType(typeBinding);
							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
							
							if (typeBinding.isClass()) {
								relevance += computeRelevanceForClass();
								relevance += computeRelevanceForException(typeBinding.sourceName);
							} else if(typeBinding.isEnum()) {
								relevance += computeRelevanceForEnum();
							} else if(typeBinding.isInterface()) {
								relevance += computeRelevanceForInterface();
							}
								
							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
								CompletionProposal proposal = this.createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								proposal.setDeclarationSignature(typeBinding.qualifiedPackageName());
								proposal.setSignature(getSignature(typeBinding));
								proposal.setPackageName(typeBinding.qualifiedPackageName());
								proposal.setTypeName(typeBinding.qualifiedSourceName());
								proposal.setCompletion(typeBinding.sourceName());
								proposal.setFlags(typeBinding.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setRelevance(relevance);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
					}
				}
			}
		}
	}
	private void findVariablesAndMethods(
		char[] token,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean insideTypeAnnotation,
		boolean insideAnnotationAttribute) {

		if (token == null)
			return;

		// Should local variables hide fields from the receiver type or any of its enclosing types?
		// we know its an implicit field/method access... see BlockScope getBinding/getImplicitMethod

		boolean staticsOnly = false;
		// need to know if we're in a static context (or inside a constructor)
		int tokenLength = token.length;

		ObjectVector localsFound = new ObjectVector();
		ObjectVector fieldsFound = new ObjectVector();
		ObjectVector methodsFound = new ObjectVector();

		Scope currentScope = scope;

		if (!this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
			done1 : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
	
				switch (currentScope.kind) {
	
					case Scope.METHOD_SCOPE :
						// handle the error case inside an explicit constructor call (see MethodScope>>findField)
						MethodScope methodScope = (MethodScope) currentScope;
						staticsOnly |= methodScope.isStatic | methodScope.isConstructorCall;
	
					case Scope.BLOCK_SCOPE :
						BlockScope blockScope = (BlockScope) currentScope;
	
						next : for (int i = 0, length = blockScope.locals.length; i < length; i++) {
							LocalVariableBinding local = blockScope.locals[i];
	
							if (local == null)
								break next;
	
							if (tokenLength > local.name.length)
								continue next;
	
							if (!CharOperation.prefixEquals(token, local.name, false /* ignore case */
								))
								continue next;
	
							if (local.isSecret())
								continue next;
	
							for (int f = 0; f < localsFound.size; f++) {
								LocalVariableBinding otherLocal =
									(LocalVariableBinding) localsFound.elementAt(f);
								if (CharOperation.equals(otherLocal.name, local.name, true))
									continue next;
							}
							localsFound.add(local);
	
							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForInterestingProposal(local);
							relevance += computeRelevanceForCaseMatching(token, local.name);
							relevance += computeRelevanceForExpectingType(local.type);
							relevance += computeRelevanceForQualification(false);
							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for local variable
							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
								CompletionProposal proposal = this.createProposal(CompletionProposal.LOCAL_VARIABLE_REF, this.actualCompletionPosition);
								proposal.setSignature(
									local.type == null
									? createTypeSignature(
											CharOperation.NO_CHAR,
											local.declaration.type.toString().toCharArray())
									: getSignature(local.type));
								if(local.type == null) {
									//proposal.setPackageName(null);
									proposal.setTypeName(local.declaration.type.toString().toCharArray());
								} else {
									proposal.setPackageName(local.type.qualifiedPackageName());
									proposal.setTypeName(local.type.qualifiedSourceName());
								}
								proposal.setName(local.name);
								proposal.setCompletion(local.name);
								proposal.setFlags(local.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setRelevance(relevance);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
						break;
	
					case Scope.COMPILATION_UNIT_SCOPE :
						break done1;
				}
				currentScope = currentScope.parent;
			}
		}
		
		boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF);
		boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF);
		
		staticsOnly = false;
		currentScope = scope;

		if(proposeField || proposeMethod) {
			done2 : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
	
				switch (currentScope.kind) {
					case Scope.METHOD_SCOPE :
						// handle the error case inside an explicit constructor call (see MethodScope>>findField)
						MethodScope methodScope = (MethodScope) currentScope;
						staticsOnly |= methodScope.isStatic | methodScope.isConstructorCall;
						break;
					case Scope.CLASS_SCOPE :
						ClassScope classScope = (ClassScope) currentScope;
						SourceTypeBinding enclosingType = classScope.referenceContext.binding;
						/*				if (tokenLength == 0) { // only search inside the type itself if no prefix was provided
											findFields(token, enclosingType.fields(), classScope, fieldsFound, staticsOnly);
											findMethods(token, enclosingType.methods(), classScope, methodsFound, staticsOnly, false);
											break done;
										} else { */
						if(!insideTypeAnnotation) {
							if(proposeField) {
								findFields(
									token,
									enclosingType,
									classScope,
									fieldsFound,
									localsFound,
									staticsOnly,
									invocationSite,
									invocationScope,
									true,
									true);
							}
							if(proposeMethod && !insideAnnotationAttribute) {
								findMethods(
									token,
									null,
									enclosingType,
									classScope,
									methodsFound,
									staticsOnly,
									false,
									false,
									invocationSite,
									invocationScope,
									true,
									false,
									true);
							}
						}
						staticsOnly |= enclosingType.isStatic();
						insideTypeAnnotation = false;
						//				}
						break;
	
					case Scope.COMPILATION_UNIT_SCOPE :
						break done2;
				}
				currentScope = currentScope.parent;
			}
			
			ImportBinding[] importBindings = scope.compilationUnitScope().imports;
			for (int i = 0; i < importBindings.length; i++) {
				ImportBinding importBinding = importBindings[i];
				if(importBinding.isValidBinding() && importBinding.isStatic()) {
					Binding binding = importBinding.resolvedImport;
					if(binding != null && binding.isValidBinding()) {
						if(importBinding.onDemand) {
							if((binding.kind() & Binding.TYPE) != 0) {
								if(proposeField) {
									findFields(
										token,
										(ReferenceBinding)binding,
										scope,
										fieldsFound,
										localsFound,
										true,
										invocationSite,
										invocationScope,
										true,
										false);
								}
								if(proposeMethod && !insideAnnotationAttribute) {
									findMethods(
										token,
										null,
										(ReferenceBinding)binding,
										scope,
										methodsFound,
										true,
										false,
										false,
										invocationSite,
										invocationScope,
										true,
										false,
										false);
								}
							}
						} else {
							if ((binding.kind() & Binding.FIELD) != 0) {
								if(proposeField) {
										findFields(
												token,
												new FieldBinding[]{(FieldBinding)binding},
												scope,
												fieldsFound,
												localsFound,
												true,
												((FieldBinding)binding).declaringClass,
												invocationSite,
												invocationScope,
												true,
												false);
								}
							} else if ((binding.kind() & Binding.METHOD) != 0) {
								if(proposeMethod && !insideAnnotationAttribute) {
									MethodBinding methodBinding = (MethodBinding)binding;
									if(CharOperation.prefixEquals(token, methodBinding.selector))
										
									findLocalMethodsOfStaticImports(
											methodBinding.selector,
											methodBinding.declaringClass.methods(),
											scope,
											methodBinding.declaringClass,
											invocationSite);
								}
							}
						}
					}
				}
			}
		}
	}
		// Helper method for private void findVariableNames(char[] name, TypeReference type )
	private void findVariableName(
		char[] token,
		char[] qualifiedPackageName,
		char[] qualifiedSourceName,
		char[] sourceName,
		final TypeBinding typeBinding,
		char[][] excludeNames,
		int dim,
		int kind,
		int modifiers){
			
		if(sourceName == null || sourceName.length == 0)
			return;

		// compute variable name for non base type
		final char[] displayName;
		if (dim > 0){
			int l = qualifiedSourceName.length;
			displayName = new char[l+(2*dim)];
			System.arraycopy(qualifiedSourceName, 0, displayName, 0, l);
			for(int i = 0; i < dim; i++){
				displayName[l+(i*2)] = '[';
				displayName[l+(i*2)+1] = ']';
			}
		} else {
			displayName = qualifiedSourceName;
		}
		
		final char[] t = token;
		final char[] q = qualifiedPackageName;
		INamingRequestor namingRequestor = new INamingRequestor() {
			public void acceptNameWithPrefixAndSuffix(char[] name, boolean isFirstPrefix, boolean isFirstSuffix) {
				accept(	name,
					(isFirstPrefix ? R_NAME_FIRST_PREFIX : R_NAME_PREFIX) + (isFirstSuffix ? R_NAME_FIRST_SUFFIX : R_NAME_SUFFIX));
			}

			public void acceptNameWithPrefix(char[] name, boolean isFirstPrefix) {
				accept(name, isFirstPrefix ? R_NAME_FIRST_PREFIX :  R_NAME_PREFIX);
			}

			public void acceptNameWithSuffix(char[] name, boolean isFirstSuffix) {
				accept(name, isFirstSuffix ? R_NAME_FIRST_SUFFIX : R_NAME_SUFFIX);
			}

			public void acceptNameWithoutPrefixAndSuffix(char[] name) {
				accept(name, 0);
			}
			void accept(char[] name, int prefixAndSuffixRelevance){
				if (CharOperation.prefixEquals(t, name, false)) {
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(t, name);
					relevance += prefixAndSuffixRelevance;
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for variable name
					
					// accept result
					CompletionEngine.this.noProposal = false;
					if(!CompletionEngine.this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
						CompletionProposal proposal = CompletionEngine.this.createProposal(CompletionProposal.VARIABLE_DECLARATION, CompletionEngine.this.actualCompletionPosition);
						proposal.setSignature(getSignature(typeBinding));
						proposal.setPackageName(q);
						proposal.setTypeName(displayName);
						proposal.setName(name);
						proposal.setCompletion(name);
						//proposal.setFlags(Flags.AccDefault);
						proposal.setReplaceRange(CompletionEngine.this.startPosition - CompletionEngine.this.offset, CompletionEngine.this.endPosition - CompletionEngine.this.offset);
						proposal.setRelevance(relevance);
						CompletionEngine.this.requestor.accept(proposal);
						if(DEBUG) {
							CompletionEngine.this.printDebug(proposal);
						}
					}
				}
			}
		};
		
		switch (kind) {
			case FIELD :
				InternalNamingConventions.suggestFieldNames(
					this.javaProject,
					qualifiedPackageName,
					qualifiedSourceName,
					dim,
					modifiers,
					excludeNames,
					namingRequestor);
				break;
			case LOCAL :
				InternalNamingConventions.suggestLocalVariableNames(
					this.javaProject,
					qualifiedPackageName,
					qualifiedSourceName,
					dim,
					excludeNames,
					namingRequestor);
				break;
			case ARGUMENT :
				InternalNamingConventions.suggestArgumentNames(
					this.javaProject,
					qualifiedPackageName,
					qualifiedSourceName,
					dim,
					excludeNames,
					namingRequestor);
				break;
		}
	}

	private void findVariableNames(char[] name, TypeReference type , char[][] excludeNames, int kind, int modifiers){

		if(type != null &&
			type.resolvedType != null &&
			type.resolvedType.problemId() == ProblemReasons.NoError){
			TypeBinding tb = type.resolvedType;
			findVariableName(
				name,
				tb.leafComponentType().qualifiedPackageName(),
				tb.leafComponentType().qualifiedSourceName(),
				tb.leafComponentType().sourceName(),
				tb,
				excludeNames,
				type.dimensions(),
				kind,
				modifiers);
		}/*	else {
			char[][] typeName = type.getTypeName();
			findVariableName(
				name,
				NoChar,
				CharOperation.concatWith(typeName, '.'),
				typeName[typeName.length - 1],
				excludeNames,
				type.dimensions());
		}*/
	}
	
	public AssistParser getParser() {

		return this.parser;
	}

	protected void reset() {

		super.reset();
		this.knownPkgs = new HashtableOfObject(10);
		this.knownTypes = new HashtableOfObject(10);
	}
	
	private void setSourceRange(int start, int end) {
		this.setSourceRange(start, end, true);
	}

	private void setSourceRange(int start, int end, boolean emptyTokenAdjstment) {		
		this.startPosition = start;
		if(emptyTokenAdjstment) {
			int endOfEmptyToken = ((CompletionScanner)this.parser.scanner).endOfEmptyToken;
			this.endPosition = endOfEmptyToken > end ? endOfEmptyToken + 1 : end + 1;
		} else {
			this.endPosition = end + 1;
		}
	}
	int computeBaseRelevance(){
		return R_DEFAULT;
	}
	private void computeExpectedTypes(ASTNode parent, ASTNode node, Scope scope){
		
		// default filter
		this.expectedTypesFilter = SUBTYPE;
		
		// find types from parent
		if(parent instanceof AbstractVariableDeclaration) {
			AbstractVariableDeclaration variable = (AbstractVariableDeclaration)parent;
			TypeBinding binding = variable.type.resolvedType;
			if(binding != null) {
				if(!(variable.initialization instanceof ArrayInitializer)) {
					addExpectedType(binding);
				}
			}
		} else if(parent instanceof Assignment) {
			TypeBinding binding = ((Assignment)parent).lhs.resolvedType;
			if(binding != null) {
				addExpectedType(binding);
			}
		} else if(parent instanceof ReturnStatement) {
			if(scope.methodScope().referenceContext instanceof AbstractMethodDeclaration) {
				MethodBinding methodBinding = ((AbstractMethodDeclaration) scope.methodScope().referenceContext).binding;
				TypeBinding binding = methodBinding  == null ? null : methodBinding.returnType;
				if(binding != null) {
					addExpectedType(binding);
				}
			}
		} else if(parent instanceof CastExpression) {
			Expression e = ((CastExpression)parent).type;
			TypeBinding binding = e.resolvedType;
			if(binding != null){
				addExpectedType(binding);
				this.expectedTypesFilter = SUBTYPE | SUPERTYPE;
			}
		} else if(parent instanceof MessageSend) {
			MessageSend messageSend = (MessageSend) parent;

			if(messageSend.actualReceiverType instanceof ReferenceBinding) {
				ReferenceBinding binding = (ReferenceBinding)messageSend.actualReceiverType;
				boolean isStatic = messageSend.receiver.isTypeReference();
				
				while(binding != null) {	
					computeExpectedTypesForMessageSend(
						binding,
						messageSend.selector,
						messageSend.arguments,
						(ReferenceBinding)messageSend.actualReceiverType,
						scope,
						messageSend,
						isStatic);
					computeExpectedTypesForMessageSendForInterface(
						binding,
						messageSend.selector,
						messageSend.arguments,
						(ReferenceBinding)messageSend.actualReceiverType,
						scope,
						messageSend,
						isStatic);
					binding = binding.superclass();
				}
			}
		} else if(parent instanceof AllocationExpression) {
			AllocationExpression allocationExpression = (AllocationExpression) parent;
			
			ReferenceBinding binding = (ReferenceBinding)allocationExpression.type.resolvedType;

			if(binding != null) {	
				computeExpectedTypesForAllocationExpression(
					binding,
					allocationExpression.arguments,
					scope,
					allocationExpression);
			}
		} else if(parent instanceof OperatorExpression) {
			int operator = (parent.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
			if(parent instanceof ConditionalExpression) {
				// for future use
			} else if(parent instanceof InstanceOfExpression) {
				InstanceOfExpression e = (InstanceOfExpression) parent;
				TypeBinding binding = e.expression.resolvedType;
				if(binding != null){
					addExpectedType(binding);
					this.expectedTypesFilter = SUBTYPE | SUPERTYPE;
				}
			} else if(parent instanceof BinaryExpression) {
				switch(operator) {
					case OperatorIds.PLUS :
						addExpectedType(BaseTypes.ShortBinding);
						addExpectedType(BaseTypes.IntBinding);
						addExpectedType(BaseTypes.LongBinding);
						addExpectedType(BaseTypes.FloatBinding);
						addExpectedType(BaseTypes.DoubleBinding);
						addExpectedType(BaseTypes.CharBinding);
						addExpectedType(BaseTypes.ByteBinding);
						addExpectedType(scope.getJavaLangString());
						break;
					case OperatorIds.AND_AND :
					case OperatorIds.OR_OR :
					case OperatorIds.XOR :
						addExpectedType(BaseTypes.BooleanBinding);
						break;
					default :
						addExpectedType(BaseTypes.ShortBinding);
						addExpectedType(BaseTypes.IntBinding);
						addExpectedType(BaseTypes.LongBinding);
						addExpectedType(BaseTypes.FloatBinding);
						addExpectedType(BaseTypes.DoubleBinding);
						addExpectedType(BaseTypes.CharBinding);
						addExpectedType(BaseTypes.ByteBinding);
						break;
				}
				BinaryExpression binaryExpression = (BinaryExpression) parent;
				if(operator == OperatorIds.LESS) {
					if(binaryExpression.left instanceof SingleNameReference){
						SingleNameReference name = (SingleNameReference) binaryExpression.left;
						Binding b = scope.getBinding(name.token, Binding.VARIABLE | Binding.TYPE, name, false);
						if(b instanceof ReferenceBinding) {
							TypeVariableBinding[] typeVariableBindings =((ReferenceBinding)b).typeVariables();
							if(typeVariableBindings != null && typeVariableBindings.length > 0) {
								addExpectedType(typeVariableBindings[0].firstBound);
							}
							
						}
					}
				}
			} else if(parent instanceof UnaryExpression) {
				switch(operator) {
					case OperatorIds.NOT :
						addExpectedType(BaseTypes.BooleanBinding);
						break;
					case OperatorIds.TWIDDLE :
						addExpectedType(BaseTypes.ShortBinding);
						addExpectedType(BaseTypes.IntBinding);
						addExpectedType(BaseTypes.LongBinding);
						addExpectedType(BaseTypes.CharBinding);
						addExpectedType(BaseTypes.ByteBinding);
						break;
					case OperatorIds.PLUS :
					case OperatorIds.MINUS :
					case OperatorIds.PLUS_PLUS :
					case OperatorIds.MINUS_MINUS :
						addExpectedType(BaseTypes.ShortBinding);
						addExpectedType(BaseTypes.IntBinding);
						addExpectedType(BaseTypes.LongBinding);
						addExpectedType(BaseTypes.FloatBinding);
						addExpectedType(BaseTypes.DoubleBinding);
						addExpectedType(BaseTypes.CharBinding);
						addExpectedType(BaseTypes.ByteBinding);
						break;
				}
			}
		} else if(parent instanceof ArrayReference) {
			addExpectedType(BaseTypes.ShortBinding);
			addExpectedType(BaseTypes.IntBinding);
			addExpectedType(BaseTypes.LongBinding);
		} else if(parent instanceof ParameterizedSingleTypeReference) {
			ParameterizedSingleTypeReference ref = (ParameterizedSingleTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			int length = ref.typeArguments == null ? 0 : ref.typeArguments.length;
			if(typeVariables != null && typeVariables.length >= length) {
				int index = length - 1;
				while(index > -1 && ref.typeArguments[index] != node) index--;
				addExpectedType(typeVariables[index].firstBound);
			}
		} else if(parent instanceof ParameterizedQualifiedTypeReference) {
			ParameterizedQualifiedTypeReference ref = (ParameterizedQualifiedTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			TypeReference[][] arguments = ref.typeArguments;
			if(typeVariables != null) {
				int iLength = arguments == null ? 0 : arguments.length;
				done: for (int i = 0; i < iLength; i++) {
					int jLength = arguments[i] == null ? 0 : arguments[i].length;
					for (int j = 0; j < jLength; j++) {
						if(arguments[i][j] == node && typeVariables.length > j) {
							addExpectedType(typeVariables[j].firstBound);
							break done;
						}
					}
				}
			}
		} else if(parent instanceof MemberValuePair) {
			MemberValuePair memberValuePair = (MemberValuePair) parent;
			if(memberValuePair.binding != null) {
				addExpectedType(memberValuePair.binding.returnType);
			}
		} else if (parent instanceof NormalAnnotation) {
			NormalAnnotation annotation = (NormalAnnotation) parent;
			MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
			if(memberValuePairs == null || memberValuePairs.length == 0) {
				if(annotation.resolvedType instanceof ReferenceBinding) {
					MethodBinding[] methodBindings =
						((ReferenceBinding)annotation.resolvedType).availableMethods();
					if(methodBindings != null &&
							methodBindings.length == 1 &&
							CharOperation.equals(methodBindings[0].selector, VALUE)) {
						addExpectedType(methodBindings[0].returnType);
					}
				}
			}
		}
		
		if(this.expectedTypesPtr + 1 != this.expectedTypes.length) {
			System.arraycopy(this.expectedTypes, 0, this.expectedTypes = new TypeBinding[this.expectedTypesPtr + 1], 0, this.expectedTypesPtr + 1);
		}
	}
	
	private void computeExpectedTypesForAllocationExpression(
		ReferenceBinding binding,
		Expression[] arguments,
		Scope scope,
		InvocationSite invocationSite) {
			
		MethodBinding[] methods = binding.availableMethods();
		nextMethod : for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];
			
			if (!method.isConstructor()) continue nextMethod;
			
			if (method.isSynthetic()) continue nextMethod;
			
			if (this.options.checkVisibility && !method.canBeSeenBy(invocationSite, scope)) continue nextMethod;
			
			TypeBinding[] parameters = method.parameters;
			if(parameters.length < arguments.length)
				continue nextMethod;
				
			int length = arguments.length - 1;
			
			for (int j = 0; j < length; j++) {
				Expression argument = arguments[j];
				TypeBinding argType = argument.resolvedType;
				if(argType != null && !argType.isCompatibleWith(parameters[j]))
					continue nextMethod;
			}
			
			TypeBinding expectedType = method.parameters[arguments.length - 1];
			if(expectedType != null) {
				addExpectedType(expectedType);
			}
		}
	}
	
	private void computeExpectedTypesForMessageSendForInterface(
		ReferenceBinding binding,
		char[] selector,
		Expression[] arguments,
		ReferenceBinding receiverType,
		Scope scope,
		InvocationSite invocationSite,
		boolean isStatic) {

		ReferenceBinding[] itsInterfaces = binding.superInterfaces();
		if (itsInterfaces != NoSuperInterfaces) {
			ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
			int lastPosition = 0;
			interfacesToVisit[lastPosition] = itsInterfaces;
			
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];

				for (int j = 0, length = interfaces.length; j < length; j++) {
					ReferenceBinding currentType = interfaces[j];

					if ((currentType.tagBits & TagBits.InterfaceVisited) == 0) {
						// if interface as not already been visited
						currentType.tagBits |= TagBits.InterfaceVisited;
	
						computeExpectedTypesForMessageSend(
							currentType,
							selector,
							arguments,
							receiverType,
							scope,
							invocationSite,
							isStatic);

						itsInterfaces = currentType.superInterfaces();
						if (itsInterfaces != NoSuperInterfaces) {

							if (++lastPosition == interfacesToVisit.length)
								System.arraycopy(
									interfacesToVisit,
									0,
									interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
									0,
									lastPosition);
							interfacesToVisit[lastPosition] = itsInterfaces;
						}
					}
				}
			}
			
			// bit reinitialization
			for (int i = 0; i <= lastPosition; i++) {
				ReferenceBinding[] interfaces = interfacesToVisit[i];

				for (int j = 0, length = interfaces.length; j < length; j++){
					interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
				}
			}
		}
	}
	
	private void computeExpectedTypesForMessageSend(
		ReferenceBinding binding,
		char[] selector,
		Expression[] arguments,
		ReferenceBinding receiverType,
		Scope scope,
		InvocationSite invocationSite,
		boolean isStatic) {
			
		MethodBinding[] methods = binding.availableMethods();
		nextMethod : for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];
			
			if (method.isSynthetic()) continue nextMethod;

			if (method.isDefaultAbstract())	continue nextMethod;

			if (method.isConstructor()) continue nextMethod;

			if (isStatic && !method.isStatic()) continue nextMethod;
			
			if (this.options.checkVisibility && !method.canBeSeenBy(receiverType, invocationSite, scope)) continue nextMethod;
			
			if(!CharOperation.equals(method.selector, selector)) continue nextMethod;
			
			TypeBinding[] parameters = method.parameters;
			if(parameters.length < arguments.length)
				continue nextMethod;
				
			int length = arguments.length - 1;
			
			for (int j = 0; j < length; j++) {
				Expression argument = arguments[j];
				TypeBinding argType = argument.resolvedType;
				if(argType != null && !argType.isCompatibleWith(parameters[j]))
					continue nextMethod;
			}
				
			TypeBinding expectedType = method.parameters[arguments.length - 1];
			if(expectedType != null) {
				addExpectedType(expectedType);
			}
		}
	}
	private void addExpectedType(TypeBinding type){
		if (type == null || !type.isValidBinding()) return;

		int length = this.expectedTypes.length;
		if (++this.expectedTypesPtr >= length)
			System.arraycopy(this.expectedTypes, 0, this.expectedTypes = new TypeBinding[length * 2], 0, length);
		this.expectedTypes[this.expectedTypesPtr] = type;
	}
	private void addForbiddenBindings(Binding binding){
		if (binding == null) return;

		int length = this.forbbidenBindings.length;
		if (++this.forbbidenBindingsPtr >= length)
			System.arraycopy(this.forbbidenBindings, 0, this.forbbidenBindings = new Binding[length * 2], 0, length);
		this.forbbidenBindings[this.forbbidenBindingsPtr] = binding;
	}
	private void addUninterestingBindings(Binding binding){
		if (binding == null) return;

		int length = this.uninterestingBindings.length;
		if (++this.uninterestingBindingsPtr >= length)
			System.arraycopy(this.uninterestingBindings, 0, this.uninterestingBindings = new Binding[length * 2], 0, length);
		this.uninterestingBindings[this.uninterestingBindingsPtr] = binding;
	}

	private Scope computeForbiddenBindings(ASTNode astNode, ASTNode astNodeParent, Scope scope) {
		if(scope instanceof ClassScope) {
			TypeDeclaration typeDeclaration = ((ClassScope)scope).referenceContext;
			if(typeDeclaration.superclass == astNode) {
				this.addForbiddenBindings(typeDeclaration.binding);
				return scope.parent;
			}
			TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
			int length = superInterfaces == null ? 0 : superInterfaces.length;
			for (int i = 0; i < length; i++) {
				if(superInterfaces[i] == astNode) {
					this.addForbiddenBindings(typeDeclaration.binding);
					return scope.parent;
				}
			}
		}
//		else if(scope instanceof MethodScope) {
//			MethodScope methodScope = (MethodScope) scope;
//			if(methodScope.insideTypeAnnotation) {
//				return methodScope.parent.parent;
//			}
//		}
		return scope;
	}
	private char[] computePrefix(SourceTypeBinding declarationType, SourceTypeBinding invocationType, boolean isStatic){
		
		StringBuffer completion = new StringBuffer(10);

		if (isStatic) {
			completion.append(declarationType.sourceName());
			
		} else if (declarationType == invocationType) {
			completion.append(THIS);
			
		} else {
			
			if (!declarationType.isNestedType()) {
				
				completion.append(declarationType.sourceName());
				completion.append('.');
				completion.append(THIS);

			} else if (!declarationType.isAnonymousType()) {
				
				completion.append(declarationType.sourceName());
				completion.append('.');
				completion.append(THIS);
				
			}
		}
		
		return completion.toString().toCharArray();
	}
	
	private void proposeNewMethod(char[] token, ReferenceBinding reference) {
		if(!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for new method
			
			CompletionProposal proposal = this.createProposal(CompletionProposal.POTENTIAL_METHOD_DECLARATION, this.actualCompletionPosition);
			proposal.setDeclarationSignature(getSignature(reference));
			proposal.setSignature(
					createMethodSignature(
							CharOperation.NO_CHAR_CHAR,
							CharOperation.NO_CHAR_CHAR,
							CharOperation.NO_CHAR,
							VOID));
			proposal.setDeclarationPackageName(reference.qualifiedPackageName());
			proposal.setDeclarationTypeName(reference.qualifiedSourceName());
			
			//proposal.setPackageName(null);
			proposal.setTypeName(VOID);
			proposal.setName(token);
			//proposal.setParameterPackageNames(null);
			//proposal.setParameterTypeNames(null);
			//proposal.setPackageName(null);
			proposal.setCompletion(token);
			proposal.setFlags(Flags.AccPublic);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}
	private boolean isForbidden(Binding binding) {
		for (int i = 0; i <= this.forbbidenBindingsPtr; i++) {
			if(this.forbbidenBindings[i] == binding) {
				return true;
			}
		}
		return false;
	}
	private boolean isValidParent(ASTNode parent, ASTNode node, Scope scope){
		
		if(parent instanceof ParameterizedSingleTypeReference) {
			ParameterizedSingleTypeReference ref = (ParameterizedSingleTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			int length = ref.typeArguments == null ? 0 : ref.typeArguments.length;
			int nodeIndex = -1;
			for(int i = length - 1 ; i > -1 ; i--) {
				if(node == ref.typeArguments[i]) {
					nodeIndex = i;
					break;
				}
			}
			if(nodeIndex > -1 && (typeVariables == null || typeVariables.length < nodeIndex + 1)) {
				TypeBinding[] typeBindings = new TypeBinding[nodeIndex + 1];
				for(int i = 0; i < nodeIndex; i++) {
					typeBindings[i] = ref.typeArguments[i].resolvedType;
				}
				typeBindings[nodeIndex] = scope.getJavaLangObject();
				if(typeVariables == null || typeVariables.length == 0) {
					scope.problemReporter().nonGenericTypeCannotBeParameterized(ref, ref.resolvedType, typeBindings);
				} else {
					scope.problemReporter().incorrectArityForParameterizedType(ref, ref.resolvedType, typeBindings);
				}
				return false;
			}
		} else if(parent instanceof ParameterizedQualifiedTypeReference) {
			ParameterizedQualifiedTypeReference ref = (ParameterizedQualifiedTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			TypeReference[][] arguments = ref.typeArguments;
			int iLength = arguments == null ? 0 : arguments.length;
			done: for (int i = 0; i < iLength; i++) {
				int jLength = arguments[i] == null ? 0 : arguments[i].length;
				for (int j = 0; j < jLength; j++) {
					if(arguments[i][j] == node && (typeVariables == null || typeVariables.length <= j)) {
						TypeBinding[] typeBindings = new TypeBinding[j + 1];
						for(int k = 0; k < j; k++) {
							typeBindings[k] = ref.typeArguments[i][k].resolvedType;
						}
						typeBindings[j] = scope.getJavaLangObject();
						if(typeVariables == null || typeVariables.length == 0) {
							scope.problemReporter().nonGenericTypeCannotBeParameterized(ref, ref.resolvedType, typeBindings);
						} else {
							scope.problemReporter().incorrectArityForParameterizedType(ref, ref.resolvedType, typeBindings);
						}
						return false;
					}
				}
			}
		}
		return true;
	}
	
	public static char[] createNonGenericTypeSignature(char[] qualifiedPackageName, char[] qualifiedTypeName) {
		return Signature.createCharArrayTypeSignature(
				CharOperation.concat(
						qualifiedPackageName,
						CharOperation.replaceOnCopy(qualifiedTypeName, '.', '$'), '.'), true);
	}
	public static char[] createTypeSignature(char[] qualifiedPackageName, char[] qualifiedTypeName) {
		char[] name = new char[qualifiedTypeName.length];
		System.arraycopy(qualifiedTypeName, 0, name, 0, qualifiedTypeName.length);
		
		int depth = 0;
		int length = name.length;
		lastDotLookup: for (int i = length -1; i >= 0; i--) {
			switch (name[i]) {
				case '.':
					if (depth == 0 && name[i - 1] != '>') {
						name[i] = '$';
					}
					break;
				case '<':
					depth--;
					break;
				case '>':
					depth++;
					break;
			}
		}
		return Signature.createCharArrayTypeSignature(
				CharOperation.concat(
						qualifiedPackageName,
						name, '.'), true);
	}
	
	public static char[] createMethodSignature(char[][] parameterPackageNames, char[][] parameterTypeNames, char[] returnPackagename, char[] returnTypeName) {
		char[][] parameterTypeSignature = new char[parameterTypeNames.length][];
		for (int i = 0; i < parameterTypeSignature.length; i++) {
			parameterTypeSignature[i] = 
				Signature.createCharArrayTypeSignature(
						CharOperation.concat(
								parameterPackageNames[i],
								CharOperation.replaceOnCopy(parameterTypeNames[i], '.', '$'), '.'), true);
		}
		
		char[] returnTypeSignature =
			returnTypeName == null || returnTypeName.length == 0
			? Signature.createCharArrayTypeSignature(VOID, true)
			: Signature.createCharArrayTypeSignature(
					CharOperation.concat(
							returnPackagename,
							CharOperation.replaceOnCopy(returnTypeName, '.', '$'), '.'), true);
			
		return Signature.createMethodSignature(
				parameterTypeSignature,
				returnTypeSignature);
	}
	
	protected CompletionProposal createProposal(int kind, int compteionOffset) {
		CompletionProposal proposal = CompletionProposal.create(kind, compteionOffset);
		proposal.nameLookup = this.nameEnvironment.nameLookup;
		proposal.completionEngine = this;
		return proposal;
	}
	
	protected void printDebug(IProblem error) {
		if(CompletionEngine.DEBUG) {
			System.out.print("COMPLETION - completionFailure("); //$NON-NLS-1$
			System.out.print(error);
			System.out.println(")"); //$NON-NLS-1$
		}
	}
	
	protected void printDebug(CompletionProposal proposal){
		StringBuffer buffer = new StringBuffer();
		buffer.append("COMPLETION - "); //$NON-NLS-1$
		switch(proposal.getKind()) {
			case CompletionProposal.ANONYMOUS_CLASS_DECLARATION :
				buffer.append("ANONYMOUS_CLASS_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.FIELD_REF :
				buffer.append("FIELD_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.KEYWORD :
				buffer.append("KEYWORD"); //$NON-NLS-1$
				break;
			case CompletionProposal.LABEL_REF :
				buffer.append("LABEL_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.LOCAL_VARIABLE_REF :
				buffer.append("LOCAL_VARIABLE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_DECLARATION :
				buffer.append("METHOD_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_REF :
				buffer.append("METHOD_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.PACKAGE_REF :
				buffer.append("PACKAGE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.TYPE_REF :
				buffer.append("TYPE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.VARIABLE_DECLARATION :
				buffer.append("VARIABLE_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.POTENTIAL_METHOD_DECLARATION :
				buffer.append("POTENTIAL_METHOD_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_NAME_REFERENCE :
				buffer.append("METHOD_NAME_REFERENCE"); //$NON-NLS-1$
				break;
			case CompletionProposal.ANNOTATION_ATTRIBUTE_REF :
				buffer.append("ANNOTATION_ATTRIBUT_REF"); //$NON-NLS-1$
				break;
			default :
				buffer.append("PROPOSAL"); //$NON-NLS-1$
				break;
				
		}
		
		buffer.append("{\n");//$NON-NLS-1$
		buffer.append("\tCompletion[").append(proposal.getCompletion() == null ? "null".toCharArray() : proposal.getCompletion()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		buffer.append("\tDeclarationSignature[").append(proposal.getDeclarationSignature() == null ? "null".toCharArray() : proposal.getDeclarationSignature()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		buffer.append("\tDeclarationKey[").append(proposal.getDeclarationKey() == null ? "null".toCharArray() : proposal.getDeclarationKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		buffer.append("\tSignature[").append(proposal.getSignature() == null ? "null".toCharArray() : proposal.getSignature()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		buffer.append("\tKey[").append(proposal.getKey() == null ? "null".toCharArray() : proposal.getKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
//		buffer.append("\tDeclarationPackage[").append(proposal.getDeclarationPackageName() == null ? "null".toCharArray() : proposal.getDeclarationPackageName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
//		buffer.append("\tDeclarationType[").append(proposal.getDeclarationTypeName() == null ? "null".toCharArray() : proposal.getDeclarationTypeName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
//		buffer.append("\tPackage[").append(proposal.getPackageName() == null ? "null".toCharArray() : proposal.getPackageName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
//		buffer.append("\tType[").append(proposal.getTypeName() == null ? "null".toCharArray() : proposal.getTypeName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		buffer.append("\tName[").append(proposal.getName() == null ? "null".toCharArray() : proposal.getName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		
		buffer.append("\tFlags[");//$NON-NLS-1$
		int flags = proposal.getFlags();
		buffer.append(Flags.toString(flags));
		if((flags & Flags.AccInterface) != 0) buffer.append("interface ");//$NON-NLS-1$
		if((flags & Flags.AccEnum) != 0) buffer.append("enum ");//$NON-NLS-1$
		buffer.append("]\n"); //$NON-NLS-1$
		
		buffer.append("\tCompletionLocation[").append(proposal.getCompletionLocation()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("\tReplaceStart[").append(proposal.getReplaceStart()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("-ReplaceEnd[").append(proposal.getReplaceEnd()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("\tTokenStart[").append(proposal.getTokenStart()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("-TokenEnd[").append(proposal.getTokenEnd()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("\tRelevance[").append(proposal.getRelevance()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$

		buffer.append("}\n");//$NON-NLS-1$
		System.out.println(buffer.toString());
	}
}
