/*******************************************************************************
 * Copyright (c) 2000, 2023 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.ui.text.java;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.CompletionContext;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
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.IProblem;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.CollectionsUtil;
import org.eclipse.jdt.internal.corext.util.TypeFilter;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.preferences.formatter.FormatterProfileManager;
import org.eclipse.jdt.internal.ui.text.java.AnnotationAtttributeProposalInfo;
import org.eclipse.jdt.internal.ui.text.java.AnonymousTypeCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.AnonymousTypeProposalInfo;
import org.eclipse.jdt.internal.ui.text.java.FieldProposalInfo;
import org.eclipse.jdt.internal.ui.text.java.GetterSetterCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.JavaFieldWithCastedReceiverCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.JavaLambdaCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyModuleCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyPackageCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.MethodDeclarationCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.MethodProposalInfo;
import org.eclipse.jdt.internal.ui.text.java.OverrideCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.ProposalContextInformation;
import org.eclipse.jdt.internal.ui.text.java.RelevanceComputer;
import org.eclipse.jdt.internal.ui.text.javadoc.JavadocInlineTagCompletionProposal;
import org.eclipse.jdt.internal.ui.text.javadoc.JavadocLinkTypeCompletionProposal;
import org.eclipse.jdt.internal.ui.viewsupport.ImageDescriptorRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.swt.graphics.Image;

/**
 * Java UI implementation of <code>CompletionRequestor</code>. Produces
 * {@link IJavaCompletionProposal}s from the proposal descriptors received via
 * the <code>CompletionRequestor</code> interface.
 * <p>
 * The lifecycle of a <code>CompletionProposalCollector</code> instance is very
 * simple:
 * </p>
 * <pre>
 * ICompilationUnit unit= ...
 * int offset= ...
 *
 * CompletionProposalCollector collector= new CompletionProposalCollector(unit);
 * unit.codeComplete(offset, collector);
 * IJavaCompletionProposal[] proposals= collector.getJavaCompletionProposals();
 * String errorMessage= collector.getErrorMessage();
 *
 * &#x2f;&#x2f; display &#x2f; process proposals
 * </pre>
 * <p>
 * Note that after a code completion operation, the collector will store any
 * received proposals, which may require a considerable amount of memory, so the
 * collector should not be kept as a reference after a completion operation.
 * </p>
 * <p>
 * Clients may instantiate or subclass.
 * </p>
 * @since 3.1
 */
public class CompletionProposalCollector extends CompletionRequestor {
	/** Triggers for method proposals without parameters. Do not modify. */
	protected final static char[] METHOD_TRIGGERS= new char[] { ';', ',', '.', '\t', '[', ' ' };
	/** Triggers for method proposals. Do not modify. */
	protected final static char[] METHOD_WITH_ARGUMENTS_TRIGGERS= new char[] { '(', '-', ' ' };
	/** Triggers for types. Do not modify. */
	protected final static char[] TYPE_TRIGGERS= new char[] { '.', '\t', '[', '(', ' ' };
	/** Triggers for variables. Do not modify. */
	protected final static char[] VAR_TRIGGER= new char[] { '\t', ' ', '=', ';', '.' };

	private final CompletionProposalLabelProvider fLabelProvider= new CompletionProposalLabelProvider();
	private final ImageDescriptorRegistry fRegistry= JavaPlugin.getImageDescriptorRegistry();

	private final List<IJavaCompletionProposal> fJavaProposals= new ArrayList<>();
	private final List<IJavaCompletionProposal> fKeywords= new ArrayList<>();
	private final Set<String> fSuggestedMethodNames= new HashSet<>();

	private final ICompilationUnit fCompilationUnit;
	private final IJavaProject fJavaProject;
	private int fUserReplacementLength;

	private CompletionContext fContext;
	private IProblem fLastProblem;

	/* performance instrumentation */
	private long fStartTime;
	private long fUITime;

	/**
	 * The UI invocation context or <code>null</code>.
	 *
	 * @since 3.2
	 */
	private JavaContentAssistInvocationContext fInvocationContext;

	private boolean fIsTestCodeExcluded;

	/**
	 * Creates a new instance ready to collect proposals. If the passed
	 * <code>ICompilationUnit</code> is not contained in an
	 * {@link IJavaProject}, no javadoc will be available as
	 * {@link org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo() additional info}
	 * on the created proposals.
	 *
	 * @param cu the compilation unit that the result collector will operate on
	 */
	public CompletionProposalCollector(ICompilationUnit cu) {
		this(cu.getJavaProject(), cu, false);
	}

	/**
	 * Creates a new instance ready to collect proposals. Note that proposals
	 * for anonymous types and method declarations are not created when using
	 * this constructor, as those need to know the compilation unit that they
	 * are created on. Use
	 * {@link CompletionProposalCollector#CompletionProposalCollector(ICompilationUnit)}
	 * instead to get all proposals.
	 * <p>
	 * If the passed Java project is <code>null</code>, no javadoc will be
	 * available as
	 * {@link org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo() additional info}
	 * on the created (e.g. method and type) proposals.
	 * </p>
	 * @param project the project that the result collector will operate on, or
	 *        <code>null</code>
	 */
	public CompletionProposalCollector(IJavaProject project) {
		this(project, null, false);
	}

	private CompletionProposalCollector(IJavaProject project, ICompilationUnit cu, boolean ignoreAll) {
		super(ignoreAll);
		fJavaProject= project;
		fCompilationUnit= cu;

		fIsTestCodeExcluded = cu != null && !isTestSource(project, cu);

		fUserReplacementLength= -1;
		if (!ignoreAll) {
			setRequireExtendedContext(true);
		}
	}

	private boolean isTestSource(IJavaProject project, ICompilationUnit cu) {
		try {
			IClasspathEntry[] resolvedClasspath= project.getResolvedClasspath(true);
			final IPath resourcePath= cu.getResource().getFullPath();
			for (IClasspathEntry e : resolvedClasspath) {
				if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
					if (e.isTest()) {
						if (e.getPath().isPrefixOf(resourcePath)) {
							return true;
						}
					}
				}
			}
		} catch (JavaModelException e) {
			return false;
		}
		return false;
	}

	/**
	 * @since 3.14
	 */
	@Override
	public boolean isTestCodeExcluded() {
		return fIsTestCodeExcluded;
	}

	/**
	 * Creates a new instance ready to collect proposals. If the passed
	 * <code>ICompilationUnit</code> is not contained in an
	 * {@link IJavaProject}, no javadoc will be available as
	 * {@link org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo() additional info}
	 * on the created proposals.
	 *
	 * @param cu the compilation unit that the result collector will operate on
	 * @param ignoreAll <code>true</code> to ignore all kinds of completion proposals
	 * @since 3.4
	 */
	public CompletionProposalCollector(ICompilationUnit cu, boolean ignoreAll) {
		this(cu.getJavaProject(), cu, ignoreAll);
	}

	@Override
	public void setIgnored(int completionProposalKind, boolean ignore) {
		super.setIgnored(completionProposalKind, ignore);
		if (completionProposalKind == CompletionProposal.METHOD_DECLARATION && !ignore) {
			setRequireExtendedContext(true);
		}
	}

	/**
	 * Sets the invocation context.
	 * <p>
	 * Subclasses may extend.
	 * </p>
	 *
	 * @param context the invocation context
	 * @see #getInvocationContext()
	 * @since 3.2
	 */
	public void setInvocationContext(JavaContentAssistInvocationContext context) {
		Assert.isNotNull(context);
		fInvocationContext= context;
		context.setCollector(this);
	}

	/**
	 * Returns the invocation context. If none has been set via
	 * {@link #setInvocationContext(JavaContentAssistInvocationContext)}, a new one is created.
	 *
	 * @return invocationContext the invocation context
	 * @since 3.2
	 */
	protected final JavaContentAssistInvocationContext getInvocationContext() {
		if (fInvocationContext == null) {
			ICompilationUnit cu= getCompilationUnit();
			if (cu != null)
				setInvocationContext(new JavaContentAssistInvocationContext(cu));
			else
				setInvocationContext(new JavaContentAssistInvocationContext(fJavaProject));
		}
		return fInvocationContext;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Subclasses may replace, but usually should not need to. Consider
	 * replacing
	 * {@linkplain #createJavaCompletionProposal(CompletionProposal) createJavaCompletionProposal}
	 * instead.
	 * </p>
	 */
	@Override
	public void accept(CompletionProposal proposal) {
		long start= JavaPlugin.DEBUG_RESULT_COLLECTOR ? System.currentTimeMillis() : 0;
		try {
			if (isIgnored(proposal.getKind())) {
				return;
			}
			getDeclaringType(proposal);
			if (proposal.getKind() == CompletionProposal.POTENTIAL_METHOD_DECLARATION) {
				acceptPotentialMethodDeclaration(proposal);
			} else {
				IJavaCompletionProposal javaProposal= createJavaCompletionProposal(proposal);
				if (javaProposal != null) {
					fJavaProposals.add(javaProposal);
					if (proposal.getKind() == CompletionProposal.KEYWORD)
						fKeywords.add(javaProposal);
				}
			}
		} catch (IllegalArgumentException e) {
			// all signature processing method may throw IAEs
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=84657
			// don't abort, but log and show all the valid proposals
			JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "Exception when processing proposal for: " + String.valueOf(proposal.getCompletion()), e)); //$NON-NLS-1$
		}

		if (JavaPlugin.DEBUG_RESULT_COLLECTOR) fUITime += System.currentTimeMillis() - start;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Subclasses may extend, but usually should not need to.
	 * </p>
	 * @see #getContext()
	 */
	@Override
	public void acceptContext(CompletionContext context) {
		fContext= context;
		fLabelProvider.setContext(context);
	}

	/**
	 * {@inheritDoc}
	 *
	 * Subclasses may extend, but must call the super implementation.
	 */
	@Override
	public void beginReporting() {
		if (JavaPlugin.DEBUG_RESULT_COLLECTOR) {
			fStartTime= System.currentTimeMillis();
			fUITime= 0;
		}

		fLastProblem= null;
		fJavaProposals.clear();
		fKeywords.clear();
		fSuggestedMethodNames.clear();
	}

	/**
	 * {@inheritDoc}
	 *
	 * Subclasses may extend, but must call the super implementation.
	 */
	@Override
	public void completionFailure(IProblem problem) {
		fLastProblem= problem;
	}

	/**
	 * {@inheritDoc}
	 *
	 * Subclasses may extend, but must call the super implementation.
	 */
	@Override
	public void endReporting() {
		if (JavaPlugin.DEBUG_RESULT_COLLECTOR) {
			long total= System.currentTimeMillis() - fStartTime;
			System.err.println("Core Collector (core):\t" + (total - fUITime)); //$NON-NLS-1$
			System.err.println("Core Collector (ui):\t" + fUITime); //$NON-NLS-1$
		}
	}

	/**
	 * Returns an error message about any error that may have occurred during
	 * code completion, or the empty string if none.
	 * <p>
	 * Subclasses may replace or extend.
	 * </p>
	 * @return an error message or the empty string
	 */
	public String getErrorMessage() {
		if (fLastProblem != null)
			return fLastProblem.getMessage();
		return ""; //$NON-NLS-1$
	}

	/**
	 * Returns the unsorted list of received proposals.
	 *
	 * @return the unsorted list of received proposals
	 */
	public final IJavaCompletionProposal[] getJavaCompletionProposals() {
		return CollectionsUtil.toArray(fJavaProposals, IJavaCompletionProposal.class);
	}

	/**
	 * Returns the unsorted list of received keyword proposals.
	 *
	 * @return the unsorted list of received keyword proposals
	 */
	public final IJavaCompletionProposal[] getKeywordCompletionProposals() {
		return CollectionsUtil.toArray(fKeywords, IJavaCompletionProposal.class);
	}

	/**
	 * If the replacement length is set, it overrides the length returned from
	 * the content assist infrastructure. Use this setting if code assist is
	 * called with a none empty selection.
	 *
	 * @param length the new replacement length, relative to the code assist
	 *        offset. Must be equal to or greater than zero.
	 */
	public final void setReplacementLength(int length) {
		Assert.isLegal(length >= 0);
		fUserReplacementLength= length;
	}

	/**
	 * Computes the relevance for a given <code>CompletionProposal</code>.
	 * <p>
	 * Subclasses may replace, but usually should not need to.
	 * </p>
	 * @param proposal the proposal to compute the relevance for
	 * @return the relevance for <code>proposal</code>
	 */
	protected int computeRelevance(CompletionProposal proposal) {
		return RelevanceComputer.computeRelevance(proposal);
	}

	/**
	 * Creates a new java completion proposal from a core proposal. This may
	 * involve computing the display label and setting up some context.
	 * <p>
	 * This method is called for every proposal that will be displayed to the
	 * user, which may be hundreds. Implementations should therefore defer as
	 * much work as possible: Labels should be computed lazily to leverage
	 * virtual table usage, and any information only needed when
	 * <em>applying</em> a proposal should not be computed yet.
	 * </p>
	 * <p>
	 * Implementations may return <code>null</code> if a proposal should not
	 * be included in the list presented to the user.
	 * </p>
	 * <p>
	 * Subclasses may extend or replace this method.
	 * </p>
	 *
	 * @param proposal the core completion proposal to create a UI proposal for
	 * @return the created java completion proposal, or <code>null</code> if
	 *         no proposal should be displayed
	 */
	protected IJavaCompletionProposal createJavaCompletionProposal(CompletionProposal proposal) {
		switch (proposal.getKind()) {
			case CompletionProposal.KEYWORD:
				return createKeywordProposal(proposal);
			case CompletionProposal.PACKAGE_REF:
				return createPackageProposal(proposal);
			case CompletionProposal.MODULE_REF:
		case CompletionProposal.MODULE_DECLARATION:
			return createModuleProposal(proposal);
		case CompletionProposal.TYPE_REF:
				return createTypeProposal(proposal);
			case CompletionProposal.JAVADOC_TYPE_REF:
				return createJavadocLinkTypeProposal(proposal);
			case CompletionProposal.FIELD_REF:
			case CompletionProposal.JAVADOC_FIELD_REF:
			case CompletionProposal.JAVADOC_VALUE_REF:
				return createFieldProposal(proposal);
			case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER:
				return createFieldWithCastedReceiverProposal(proposal);
			case CompletionProposal.METHOD_REF:
			case CompletionProposal.CONSTRUCTOR_INVOCATION:
			case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER:
			case CompletionProposal.METHOD_NAME_REFERENCE:
			case CompletionProposal.JAVADOC_METHOD_REF:
				return createMethodReferenceProposal(proposal);
			case CompletionProposal.METHOD_DECLARATION:
				return createMethodDeclarationProposal(proposal);
			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION:
				return createAnonymousTypeProposal(proposal, getInvocationContext());
			case CompletionProposal.ANONYMOUS_CLASS_DECLARATION:
				return createAnonymousTypeProposal(proposal, null);
			case CompletionProposal.LABEL_REF:
				return createLabelProposal(proposal);
			case CompletionProposal.LOCAL_VARIABLE_REF:
			case CompletionProposal.VARIABLE_DECLARATION:
				return createLocalVariableProposal(proposal);
			case CompletionProposal.ANNOTATION_ATTRIBUTE_REF:
				return createAnnotationAttributeReferenceProposal(proposal);
			case CompletionProposal.JAVADOC_BLOCK_TAG:
			case CompletionProposal.JAVADOC_PARAM_REF:
				return createJavadocSimpleProposal(proposal);
			case CompletionProposal.JAVADOC_INLINE_TAG:
				return createJavadocInlineTagProposal(proposal);
			case CompletionProposal.LAMBDA_EXPRESSION:
				return createLambdaProposal(proposal);
			case CompletionProposal.POTENTIAL_METHOD_DECLARATION:
			default:
				return null;
		}
	}

	/**
	 * Creates the context information for a given method reference proposal.
	 * The passed proposal must be of kind {@link CompletionProposal#METHOD_REF}.
	 *
	 * @param methodProposal the method proposal for which to create context information
	 * @return the context information for <code>methodProposal</code>
	 */
	protected final IContextInformation createMethodContextInformation(CompletionProposal methodProposal) {
		Assert.isTrue(methodProposal.getKind() == CompletionProposal.METHOD_REF);
		return new ProposalContextInformation(methodProposal);
	}

	/**
	 * Returns the compilation unit that the receiver operates on, or
	 * <code>null</code> if the <code>IJavaProject</code> constructor was
	 * used to create the receiver.
	 *
	 * @return the compilation unit that the receiver operates on, or
	 *         <code>null</code>
	 */
	protected final ICompilationUnit getCompilationUnit() {
		return fCompilationUnit;
	}

	/**
	 * Returns the <code>CompletionContext</code> for this completion operation.

	 * @return the <code>CompletionContext</code> for this completion operation
	 * @see CompletionRequestor#acceptContext(CompletionContext)
	 */
	protected final CompletionContext getContext() {
		return fContext;
	}

	/**
	 * Returns a cached image for the given descriptor.
	 *
	 * @param descriptor the image descriptor to get an image for, may be
	 *        <code>null</code>
	 * @return the image corresponding to <code>descriptor</code>
	 */
	protected final Image getImage(ImageDescriptor descriptor) {
		return (descriptor == null) ? null : fRegistry.get(descriptor);
	}

	/**
	 * Returns the proposal label provider used by the receiver.
	 *
	 * @return the proposal label provider used by the receiver
	 */
	protected final CompletionProposalLabelProvider getLabelProvider() {
		return fLabelProvider;
	}

	/**
	 * Returns the replacement length of a given completion proposal. The
	 * replacement length is usually the difference between the return values of
	 * <code>proposal.getReplaceEnd</code> and
	 * <code>proposal.getReplaceStart</code>, but this behavior may be
	 * overridden by calling {@link #setReplacementLength(int)}.
	 *
	 * @param proposal the completion proposal to get the replacement length for
	 * @return the replacement length for <code>proposal</code>
	 */
	protected final int getLength(CompletionProposal proposal) {
		int start= proposal.getReplaceStart();
		int end= proposal.getReplaceEnd();
		int length;
		if (fUserReplacementLength == -1) {
			length= end - start;
		} else {
			length= fUserReplacementLength;
			// extend length to begin at start
			int behindCompletion= proposal.getCompletionLocation() + 1;
			if (start < behindCompletion) {
				length+= behindCompletion - start;
			}
		}
		return length;
	}

	/**
	 * Returns <code>true</code> if <code>proposal</code> is filtered, e.g.
	 * should not be proposed to the user, <code>false</code> if it is valid.
	 * <p>
	 * Subclasses may extends this method. The default implementation filters
	 * proposals set to be ignored via
	 * {@linkplain CompletionRequestor#setIgnored(int, boolean) setIgnored} and
	 * types set to be ignored in the preferences.
	 * </p>
	 *
	 * @param proposal the proposal to filter
	 * @return <code>true</code> to filter <code>proposal</code>,
	 *         <code>false</code> to let it pass
	 */
	protected boolean isFiltered(CompletionProposal proposal) {
		if (isIgnored(proposal.getKind()))
			return true;
		char[] declaringType= getDeclaringType(proposal);
		return declaringType!= null && TypeFilter.isFiltered(declaringType);
	}

	@Override
	public boolean isIgnored(char[] fullTypeName) {
		return fullTypeName!= null && TypeFilter.isFiltered(fullTypeName);
	}

	/**
	 * Returns the type signature of the declaring type of a
	 * <code>CompletionProposal</code>, or <code>null</code> for proposals
	 * that do not have a declaring type. The return value is <em>not</em>
	 * <code>null</code> for proposals of the following kinds:
	 * <ul>
	 * <li>METHOD_DECLARATION</li>
	 * <li>METHOD_NAME_REFERENCE</li>
	 * <li>METHOD_REF</li>
	 * <li>ANNOTATION_ATTRIBUTE_REF</li>
	 * <li>POTENTIAL_METHOD_DECLARATION</li>
	 * <li>ANONYMOUS_CLASS_DECLARATION</li>
	 * <li>FIELD_REF</li>
	 * <li>PACKAGE_REF (returns the package name, but no type)</li>
	 * <li>MODULE_REF (returns the module name, but no type)</li>
	 * <li>MODULE_DECLARATION (returns the possible name of the module that is being declared, but no type)</li>
	 * <li>TYPE_REF</li>
	 * </ul>
	 *
	 * @param proposal the completion proposal to get the declaring type for
	 * @return the type signature of the declaring type, or <code>null</code> if there is none
	 * @see Signature#toCharArray(char[])
	 */
	protected final char[] getDeclaringType(CompletionProposal proposal) {
		switch (proposal.getKind()) {
			case CompletionProposal.METHOD_DECLARATION:
			case CompletionProposal.METHOD_NAME_REFERENCE:
			case CompletionProposal.JAVADOC_METHOD_REF:
			case CompletionProposal.METHOD_REF:
			case CompletionProposal.CONSTRUCTOR_INVOCATION:
			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION:
			case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER:
			case CompletionProposal.ANNOTATION_ATTRIBUTE_REF:
			case CompletionProposal.POTENTIAL_METHOD_DECLARATION:
			case CompletionProposal.ANONYMOUS_CLASS_DECLARATION:
			case CompletionProposal.FIELD_REF:
			case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER:
			case CompletionProposal.JAVADOC_FIELD_REF:
			case CompletionProposal.JAVADOC_VALUE_REF:
			case CompletionProposal.LAMBDA_EXPRESSION:
				char[] declaration= proposal.getDeclarationSignature();
				// special methods may not have a declaring type: methods defined on arrays etc.
				// Currently known: class literals don't have a declaring type - use Object
				if (declaration == null)
					return "java.lang.Object".toCharArray(); //$NON-NLS-1$
				return Signature.toCharArray(declaration);
			case CompletionProposal.PACKAGE_REF:
			case CompletionProposal.MODULE_REF:
			case CompletionProposal.MODULE_DECLARATION:
				return proposal.getDeclarationSignature();
			case CompletionProposal.JAVADOC_TYPE_REF:
			case CompletionProposal.TYPE_REF:
				return Signature.toCharArray(proposal.getSignature());
			case CompletionProposal.LOCAL_VARIABLE_REF:
			case CompletionProposal.VARIABLE_DECLARATION:
			case CompletionProposal.KEYWORD:
			case CompletionProposal.LABEL_REF:
			case CompletionProposal.JAVADOC_BLOCK_TAG:
			case CompletionProposal.JAVADOC_INLINE_TAG:
			case CompletionProposal.JAVADOC_PARAM_REF:
				return null;
			default:
				Assert.isTrue(false);
				return null;
		}
	}

	private void acceptPotentialMethodDeclaration(CompletionProposal proposal) {
		try {
			IJavaElement enclosingElement= null;
			if (getContext().isExtended()) {
				enclosingElement= getContext().getEnclosingElement();
			} else if (fCompilationUnit != null) {
				// kept for backward compatibility: CU is not reconciled at this moment, information is missing (bug 70005)
				enclosingElement= fCompilationUnit.getElementAt(proposal.getCompletionLocation() + 1);
			}
			if (enclosingElement == null)
				return;
			IType type= (IType) enclosingElement.getAncestor(IJavaElement.TYPE);
			if (type != null) {
				String prefix= String.valueOf(proposal.getName());
				int completionStart= proposal.getReplaceStart();
				int completionEnd= proposal.getReplaceEnd();
				int relevance= computeRelevance(proposal);

				GetterSetterCompletionProposal.evaluateProposals(type, prefix, completionStart, completionEnd - completionStart, relevance + 2, fSuggestedMethodNames, fJavaProposals);
				MethodDeclarationCompletionProposal.evaluateProposals(type, prefix, completionStart, completionEnd - completionStart, relevance, fSuggestedMethodNames, fJavaProposals);
			}
		} catch (CoreException e) {
			JavaPlugin.log(e);
		}
	}

	private IJavaCompletionProposal createAnnotationAttributeReferenceProposal(CompletionProposal proposal) {
		StyledString displayString= fLabelProvider.createLabelWithTypeAndDeclaration(proposal);
		ImageDescriptor descriptor= fLabelProvider.createMethodImageDescriptor(proposal);
		String completion= String.valueOf(proposal.getCompletion());
		JavaCompletionProposal javaProposal= new JavaCompletionProposal(completion, proposal.getReplaceStart(), getLength(proposal), getImage(descriptor), displayString, computeRelevance(proposal));
		if (fJavaProject != null)
			javaProposal.setProposalInfo(new AnnotationAtttributeProposalInfo(fJavaProject, proposal));
		return javaProposal;
	}

	private IJavaCompletionProposal createAnonymousTypeProposal(CompletionProposal proposal, JavaContentAssistInvocationContext invocationContext) {
		if (fCompilationUnit == null || fJavaProject == null)
			return null;

		char[] declarationKey= proposal.getDeclarationKey();
		if (declarationKey == null)
			return null;

		try {
			IJavaElement element= fJavaProject.findElement(new String(declarationKey), null);
			if (!(element instanceof IType))
				return null;

			IType type= (IType) element;

			String completion= String.valueOf(proposal.getCompletion());
			int start= proposal.getReplaceStart();
			int length= getLength(proposal);
			int relevance= computeRelevance(proposal);

			StyledString label= fLabelProvider.createAnonymousTypeLabel(proposal);

			JavaCompletionProposal javaProposal= new AnonymousTypeCompletionProposal(fJavaProject, fCompilationUnit, invocationContext, start, length, completion, label, String.valueOf(proposal
					.getDeclarationSignature()), type, relevance);
			javaProposal.setProposalInfo(new AnonymousTypeProposalInfo(fJavaProject, proposal));
			return javaProposal;
		} catch (JavaModelException e) {
			return null;
		}
	}

	private IJavaCompletionProposal createFieldProposal(CompletionProposal proposal) {
		String completion= String.valueOf(proposal.getCompletion());
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);
		StyledString label= fLabelProvider.createStyledLabel(proposal);
		Image image= getImage(fLabelProvider.createFieldImageDescriptor(proposal));
		int relevance= computeRelevance(proposal);

		JavaCompletionProposal javaProposal= new JavaCompletionProposal(completion, start, length, image, label, relevance, getContext().isInJavadoc(), getInvocationContext());
		if (fJavaProject != null)
			javaProposal.setProposalInfo(new FieldProposalInfo(fJavaProject, proposal));

		javaProposal.setTriggerCharacters(VAR_TRIGGER);

		return javaProposal;
	}

	/**
	 * Creates the Java completion proposal for the JDT Core
	 * {@link CompletionProposal#FIELD_REF_WITH_CASTED_RECEIVER} proposal.
	 *
	 * @param proposal the JDT Core proposal
	 * @return the Java completion proposal
	 * @since 3.4
	 */
	private IJavaCompletionProposal createFieldWithCastedReceiverProposal(CompletionProposal proposal) {
		String completion= String.valueOf(proposal.getCompletion());
		completion= CodeFormatterUtil.format(CodeFormatter.K_EXPRESSION, completion, 0, "\n", FormatterProfileManager.getProjectSettings(fJavaProject)); //$NON-NLS-1$
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);
		StyledString label= fLabelProvider.createStyledLabel(proposal);
		Image image= getImage(fLabelProvider.createFieldImageDescriptor(proposal));
		int relevance= computeRelevance(proposal);

		JavaCompletionProposal javaProposal= new JavaFieldWithCastedReceiverCompletionProposal(completion, start, length, image, label, relevance, getContext().isInJavadoc(), getInvocationContext(), proposal);
		if (fJavaProject != null)
			javaProposal.setProposalInfo(new FieldProposalInfo(fJavaProject, proposal));

		javaProposal.setTriggerCharacters(VAR_TRIGGER);

		return javaProposal;
	}

	private IJavaCompletionProposal createJavadocSimpleProposal(CompletionProposal javadocProposal) {
		// TODO do better with javadoc proposals
//		String completion= String.valueOf(proposal.getCompletion());
//		int start= proposal.getReplaceStart();
//		int length= getLength(proposal);
//		String label= fLabelProvider.createSimpleLabel(proposal);
//		Image image= getImage(fLabelProvider.createImageDescriptor(proposal));
//		int relevance= computeRelevance(proposal);
//
//		JavaCompletionProposal javaProposal= new JavaCompletionProposal(completion, start, length, image, label, relevance);
//		if (fJavaProject != null)
//			javaProposal.setProposalInfo(new FieldProposalInfo(fJavaProject, proposal));
//
//		javaProposal.setTriggerCharacters(VAR_TRIGGER);
//
//		return javaProposal;
		LazyJavaCompletionProposal proposal = new LazyJavaCompletionProposal(javadocProposal, getInvocationContext());
//		adaptLength(proposal, javadocProposal);
		return proposal;
	}

	private IJavaCompletionProposal createJavadocInlineTagProposal(CompletionProposal javadocProposal) {
		LazyJavaCompletionProposal proposal= new JavadocInlineTagCompletionProposal(javadocProposal, getInvocationContext());
		adaptLength(proposal, javadocProposal);
		return proposal;
	}

	private IJavaCompletionProposal createKeywordProposal(CompletionProposal proposal) {
		String completion= String.valueOf(proposal.getCompletion());
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);
		StyledString label= fLabelProvider.createSimpleLabel(proposal);
		int relevance= computeRelevance(proposal);
		return new JavaCompletionProposal(completion, start, length, null, label, relevance);
	}

	private IJavaCompletionProposal createLabelProposal(CompletionProposal proposal) {
		String completion= String.valueOf(proposal.getCompletion());
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);
		StyledString label= fLabelProvider.createSimpleLabel(proposal);
		int relevance= computeRelevance(proposal);

		return new JavaCompletionProposal(completion, start, length, null, label, relevance);
	}

	private IJavaCompletionProposal createLocalVariableProposal(CompletionProposal proposal) {
		String completion= String.valueOf(proposal.getCompletion());
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);
		Image image= getImage(fLabelProvider.createLocalImageDescriptor(proposal));
		StyledString label= fLabelProvider.createSimpleLabelWithType(proposal);
		int relevance= computeRelevance(proposal);
		final JavaCompletionProposal javaProposal= new JavaCompletionProposal(completion, start, length, image, label, relevance, false, getInvocationContext());
		javaProposal.setTriggerCharacters(VAR_TRIGGER);
		return javaProposal;
	}

	private IJavaCompletionProposal createMethodDeclarationProposal(CompletionProposal proposal) {
		if (fCompilationUnit == null || fJavaProject == null)
			return null;

		String name= String.valueOf(proposal.getName());
		String[] paramTypes= Signature.getParameterTypes(String.valueOf(proposal.getSignature()));
		for (int index= 0; index < paramTypes.length; index++)
			paramTypes[index]= Signature.toString(paramTypes[index]);
		int start= proposal.getReplaceStart();
		int length= getLength(proposal);

		StyledString label= fLabelProvider.createOverrideMethodProposalLabel(proposal);

		JavaCompletionProposal javaProposal= new OverrideCompletionProposal(fJavaProject, fCompilationUnit, name, paramTypes, start, length, label, String.valueOf(proposal.getCompletion()));
		javaProposal.setImage(getImage(fLabelProvider.createMethodImageDescriptor(proposal)));
		javaProposal.setProposalInfo(new MethodProposalInfo(fJavaProject, proposal));
		javaProposal.setRelevance(computeRelevance(proposal));

		fSuggestedMethodNames.add(name);
		return javaProposal;
	}

	private IJavaCompletionProposal createMethodReferenceProposal(CompletionProposal methodProposal) {
		LazyJavaCompletionProposal proposal= new JavaMethodCompletionProposal(methodProposal, getInvocationContext());
		adaptLength(proposal, methodProposal);
		return proposal;
	}

	private void adaptLength(LazyJavaCompletionProposal proposal, CompletionProposal coreProposal) {
		if (fUserReplacementLength != -1) {
			proposal.setReplacementLength(getLength(coreProposal));
		}
	}

	private IJavaCompletionProposal createModuleProposal(CompletionProposal proposal) {
		return new LazyModuleCompletionProposal(proposal, getInvocationContext());
	}

	private IJavaCompletionProposal createPackageProposal(CompletionProposal proposal) {
		return new LazyPackageCompletionProposal(proposal, getInvocationContext());
	}

	private IJavaCompletionProposal createTypeProposal(CompletionProposal typeProposal) {
		LazyJavaCompletionProposal proposal= new LazyJavaTypeCompletionProposal(typeProposal, getInvocationContext());
		adaptLength(proposal, typeProposal);
		return proposal;
	}

	private IJavaCompletionProposal createJavadocLinkTypeProposal(CompletionProposal typeProposal) {
		LazyJavaCompletionProposal proposal= new JavadocLinkTypeCompletionProposal(typeProposal, getInvocationContext());
		adaptLength(proposal, typeProposal);
		return proposal;
	}

	private IJavaCompletionProposal createLambdaProposal(CompletionProposal orginal) {
		LazyJavaCompletionProposal proposal= new JavaLambdaCompletionProposal(orginal, getInvocationContext());
		adaptLength(proposal, orginal);
		return proposal;
	}

}
