content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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.wst.jsdt.internal.ui.text.java;
  
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.contentassist.IContextInformation;
  import org.eclipse.swt.graphics.Image;
  import org.eclipse.wst.jsdt.core.CompletionProposal;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.JavaScriptCore;
  import org.eclipse.wst.jsdt.core.Signature;
  import org.eclipse.wst.jsdt.core.formatter.DefaultCodeFormatterConstants;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.ui.text.java.JavaContentAssistInvocationContext;
  
  
  public class LazyJavaCompletionProposal extends AbstractJavaCompletionProposal {
  	
  	protected static final String LPAREN= "("; //$NON-NLS-1$
  	protected static final String RPAREN= ")"; //$NON-NLS-1$
  	protected static final String COMMA= ","; //$NON-NLS-1$
  	protected static final String SPACE= " "; //$NON-NLS-1$
  	
  	protected static final class FormatterPrefs {
  		/* Methods & constructors */
  		public final boolean beforeOpeningParen;
  		public final boolean afterOpeningParen;
  		public final boolean beforeComma;
  		public final boolean afterComma;
  		public final boolean beforeClosingParen;
  		public final boolean inEmptyList;
  		
  		/* type parameters */
  		public final boolean beforeOpeningBracket;
  		public final boolean afterOpeningBracket;
  		public final boolean beforeTypeArgumentComma;
  		public final boolean afterTypeArgumentComma;
  		public final boolean beforeClosingBracket;
  	
  		FormatterPrefs(IJavaScriptProject project) {
  			beforeOpeningParen= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_PAREN_IN_METHOD_INVOCATION, false);
  			afterOpeningParen= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_PAREN_IN_METHOD_INVOCATION, false);
  			beforeComma= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_METHOD_INVOCATION_ARGUMENTS, false);
  			afterComma= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_INVOCATION_ARGUMENTS, true);
  			beforeClosingParen= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_PAREN_IN_METHOD_INVOCATION, false);
  			inEmptyList= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BETWEEN_EMPTY_PARENS_IN_METHOD_INVOCATION, false);
  			
  			beforeOpeningBracket= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, false);
  			afterOpeningBracket= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, false);
  			beforeTypeArgumentComma= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_PARAMETERIZED_TYPE_REFERENCE, false);
  			afterTypeArgumentComma= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_PARAMETERIZED_TYPE_REFERENCE, true);
  			beforeClosingBracket= getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, false);
  		}
  	
  		protected final boolean getCoreOption(IJavaScriptProject project, String key, boolean def) {
  			String option= getCoreOption(project, key);
  			if (JavaScriptCore.INSERT.equals(option))
  				return true;
  			if (JavaScriptCore.DO_NOT_INSERT.equals(option))
  				return false;
  			return def;
  		}
  	
  		protected final String getCoreOption(IJavaScriptProject project, String key) {
  			if (project == null)
  				return JavaScriptCore.getOption(key);
  			return project.getOption(key, true);
  		}
  	}
  
  	private boolean fDisplayStringComputed;
  	private boolean fReplacementStringComputed;
  	private boolean fReplacementOffsetComputed;
  	private boolean fReplacementLengthComputed;
  	private boolean fCursorPositionComputed;
  	private boolean fImageComputed;
  	private boolean fContextInformationComputed;
  	private boolean fProposalInfoComputed;
  	private boolean fTriggerCharactersComputed;
  	private boolean fSortStringComputed;
  	private boolean fRelevanceComputed;
  	private FormatterPrefs fFormatterPrefs;
  
  	/**
  	 * The core proposal wrapped by this completion proposal.
  	 */
  	protected final CompletionProposal fProposal;
  	protected int fContextInformationPosition;
  	
  	public LazyJavaCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) {
  		super(context);
  		Assert.isNotNull(proposal);
  		Assert.isNotNull(context);
  		Assert.isNotNull(context.getCoreContext());
  		fProposal= proposal;
  	}
  
  	/*
  	 * @see ICompletionProposalExtension#getTriggerCharacters()
  	 */
  	public final char[] getTriggerCharacters() {
  		if (!fTriggerCharactersComputed)
  			setTriggerCharacters(computeTriggerCharacters());
  		return super.getTriggerCharacters();
  	}
  	
  	protected char[] computeTriggerCharacters() {
  		return new char[0];
  	}
  
  	/**
  	 * Sets the trigger characters.
  	 * @param triggerCharacters The set of characters which can trigger the application of this completion proposal
  	 */
  	public final void setTriggerCharacters(char[] triggerCharacters) {
  		fTriggerCharactersComputed= true;
  		super.setTriggerCharacters(triggerCharacters);
  	}
  
  	/**
  	 * Sets the proposal info.
  	 * @param proposalInfo The additional information associated with this proposal or <code>null</code>
  	 */
  	public final void setProposalInfo(ProposalInfo proposalInfo) {
  		fProposalInfoComputed= true;
  		super.setProposalInfo(proposalInfo);
  	}
  
  	/**
  	 * Returns the additional proposal info, or <code>null</code> if none
  	 * exists.
  	 *
  	 * @return the additional proposal info, or <code>null</code> if none
  	 *         exists
  	 */
  	protected final ProposalInfo getProposalInfo() {
  		if (!fProposalInfoComputed)
  			setProposalInfo(computeProposalInfo());
  		return super.getProposalInfo();
  	}
  
  	protected ProposalInfo computeProposalInfo() {
  		return null;
  	}
  
  	/**
  	 * Sets the cursor position relative to the insertion offset. By default this is the length of the completion string
  	 * (Cursor positioned after the completion)
  	 * @param cursorPosition The cursorPosition to set
  	 */
  	public final void setCursorPosition(int cursorPosition) {
  		fCursorPositionComputed= true;
  		super.setCursorPosition(cursorPosition);
  	}
  	
  	protected final int getCursorPosition() {
  		if (!fCursorPositionComputed)
  			setCursorPosition(computeCursorPosition());
  		return super.getCursorPosition();
  	}
  
  	protected int computeCursorPosition() {
  		return getReplacementString().length();
  	}
  
  	/*
  	 * @see org.eclipse.wst.jsdt.internal.ui.text.java.AbstractJavaCompletionProposal#isInJavadoc()
  	 */
  	protected final boolean isInJavadoc() {
  		return fInvocationContext.getCoreContext().isInJsdoc();
  	}
  
  	/*
  	 * @see ICompletionProposal#getContextInformation()
  	 */
  	public final IContextInformation getContextInformation() {
  		if (!fContextInformationComputed)
  			setContextInformation(computeContextInformation());
  		return super.getContextInformation();
  	}
  
  	protected IContextInformation computeContextInformation() {
  		return null;
  	}
  
  	/**
  	 * Sets the context information.
  	 * @param contextInformation The context information associated with this proposal
  	 */
  	public final void setContextInformation(IContextInformation contextInformation) {
  		fContextInformationComputed= true;
  		super.setContextInformation(contextInformation);
  	}
  	
  	/*
  	 * @see ICompletionProposal#getDisplayString()
  	 */
  	public final String getDisplayString() {
  		if (!fDisplayStringComputed)
  			setDisplayString(computeDisplayString());
  		return super.getDisplayString();
  	}
  
  	protected final void setDisplayString(String string) {
  		fDisplayStringComputed= true;
  		super.setDisplayString(string);
  	}
  
  	protected String computeDisplayString() {
  		return fInvocationContext.getLabelProvider().createLabel(fProposal);
  	}
  
  	/*
  	 * @see ICompletionProposal#getAdditionalProposalInfo()
  	 */
  	public final String getAdditionalProposalInfo() {
  		return super.getAdditionalProposalInfo();
  	}
  
  	/*
  	 * @see ICompletionProposalExtension#getContextInformationPosition()
  	 */
  	public final int getContextInformationPosition() {
  		if (getContextInformation() == null)
  			return getReplacementOffset() - 1;
  		return getReplacementOffset() + getCursorPosition();
  	}
  
  	/**
  	 * Gets the replacement offset.
  	 * @return Returns a int
  	 */
  	public final int getReplacementOffset() {
  		if (!fReplacementOffsetComputed)
  			setReplacementOffset(fProposal.getReplaceStart());
  		return super.getReplacementOffset();
  	}
  
  	/**
  	 * Sets the replacement offset.
  	 * @param replacementOffset The replacement offset to set
  	 */
  	public final void setReplacementOffset(int replacementOffset) {
  		fReplacementOffsetComputed= true;
  		super.setReplacementOffset(replacementOffset);
  	}
  
  	/*
  	 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getCompletionOffset()
  	 */
  	public final int getPrefixCompletionStart(IDocument document, int completionOffset) {
  		return getReplacementOffset();
  	}
  
  	/**
  	 * Gets the replacement length.
  	 * @return Returns a int
  	 */
  	public final int getReplacementLength() {
  		if (!fReplacementLengthComputed)
  			setReplacementLength(fProposal.getReplaceEnd() - fProposal.getReplaceStart());
  		return super.getReplacementLength();
  	}
  
  	/**
  	 * Sets the replacement length.
  	 * @param replacementLength The replacementLength to set
  	 */
  	public final void setReplacementLength(int replacementLength) {
  		fReplacementLengthComputed= true;
  		super.setReplacementLength(replacementLength);
  	}
  
  	/**
  	 * Gets the replacement string.
  	 * @return Returns a String
  	 */
  	public final String getReplacementString() {
  		if (!fReplacementStringComputed)
  			setReplacementString(computeReplacementString());
  		return super.getReplacementString();
  	}
  
  	protected String computeReplacementString() {
  		return String.valueOf(fProposal.getCompletion());
  	}
  
  	/**
  	 * Sets the replacement string.
  	 * @param replacementString The replacement string to set
  	 */
  	public final void setReplacementString(String replacementString) {
  		fReplacementStringComputed= true;
  		super.setReplacementString(replacementString);
  	}
  
  	/*
  	 * @see ICompletionProposal#getImage()
  	 */
  	public final Image getImage() {
  		if (!fImageComputed)
  			setImage(computeImage());
  		return super.getImage();
  	}
  
  	protected Image computeImage() {
  		return JavaScriptPlugin.getImageDescriptorRegistry().get(fInvocationContext.getLabelProvider().createImageDescriptor(fProposal));
  	}
  
  	/**
  	 * Sets the image.
  	 * @param image The image to set
  	 */
  	public final void setImage(Image image) {
  		fImageComputed= true;
  		super.setImage(image);
  	}
  
  	/*
  	 * @see org.eclipse.wst.jsdt.internal.ui.text.java.AbstractJavaCompletionProposal#isValidPrefix(java.lang.String)
  	 */
  	public boolean isValidPrefix(String prefix) {
  		if (super.isValidPrefix(prefix))
  			return true;
  		
  		if (fProposal.getKind() == CompletionProposal.METHOD_NAME_REFERENCE) {
  			// static imports - includes package & type name
  			StringBuffer buf= new StringBuffer();
  			buf.append(Signature.toCharArray(fProposal.getDeclarationSignature()));
  			buf.append('.');
  			buf.append(getDisplayString());
  			return isPrefix(prefix, buf.toString());
  		}
  		
  		return false;
  	}
  	
  	/**
  	 * Gets the proposal's relevance.
  	 * @return Returns a int
  	 */
  	public final int getRelevance() {
  		if (!fRelevanceComputed)
  			setRelevance(computeRelevance());
  		return super.getRelevance();
  	}
  
  	/**
  	 * Sets the proposal's relevance.
  	 * @param relevance The relevance to set
  	 */
  	public final void setRelevance(int relevance) {
  		fRelevanceComputed= true;
  		super.setRelevance(relevance);
  	}
  
  	protected int computeRelevance() {
  		final int baseRelevance= fProposal.getRelevance() * 16;
  		switch (fProposal.getKind()) {
  			case CompletionProposal.PACKAGE_REF:
  				return baseRelevance + 0;
  			case CompletionProposal.LABEL_REF:
  				return baseRelevance + 1;
  			case CompletionProposal.KEYWORD:
  				return baseRelevance + 2;
  			case CompletionProposal.TYPE_REF:
  			case CompletionProposal.ANONYMOUS_CLASS_DECLARATION:
  				return baseRelevance + 3;
  			case CompletionProposal.METHOD_REF:
  			case CompletionProposal.METHOD_NAME_REFERENCE:
  			case CompletionProposal.METHOD_DECLARATION:
  				return baseRelevance + 4;
  			case CompletionProposal.POTENTIAL_METHOD_DECLARATION:
  				return baseRelevance + 4 /* + 99 */;
  			case CompletionProposal.FIELD_REF:
  				return baseRelevance + 5;
  			case CompletionProposal.LOCAL_VARIABLE_REF:
  			case CompletionProposal.VARIABLE_DECLARATION:
  				return baseRelevance + 6;
  			default:
  				return baseRelevance;
  		}
  	}
  	
  	public final String getSortString() {
  		if (!fSortStringComputed)
  			setSortString(computeSortString());
  		return super.getSortString();
  	}
  
  	protected final void setSortString(String string) {
  		fSortStringComputed= true;
  		super.setSortString(string);
  	}
  
  	protected String computeSortString() {
  		return getDisplayString();
  	}
  
  	protected FormatterPrefs getFormatterPrefs() {
  		if (fFormatterPrefs == null) {
  			IJavaScriptUnit cu= fInvocationContext.getCompilationUnit();
  			fFormatterPrefs= new FormatterPrefs(cu == null ? null : cu.getJavaScriptProject());
  		}
  		return fFormatterPrefs;
  	}
  
  	/**
  	 * Overrides the default context information position. Ignored if set to zero.
  	 * 
  	 * @param contextInformationPosition the replaced position.
  	 */
  	public void setContextInformationPosition(int contextInformationPosition) {
  		fContextInformationPosition= contextInformationPosition;
  	}
  }
  