blob: 1be8de4a7c0cf5fea8a99c3bd48109f3d8c87eab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Oracle Corporation.
* 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:
* Cameron Bateman/Oracle - initial API and implementation
*
********************************************************************************/
package org.eclipse.jst.jsf.core.internal.contentassist.el;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory;
import org.eclipse.jst.jsf.context.resolver.structureddocument.internal.ITextRegionContextResolver;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
import org.eclipse.jst.jsf.context.symbol.IMethodSymbol;
import org.eclipse.jst.jsf.context.symbol.IObjectSymbol;
import org.eclipse.jst.jsf.context.symbol.ISymbol;
import org.eclipse.jst.jsf.context.symbol.provider.IContentProposalProvider;
import org.eclipse.jst.jsf.context.symbol.provider.ProposalCreationFactoryAdapter;
import org.eclipse.jst.jsf.context.symbol.provider.IContentProposalProvider.IProposalCreationFactory;
import org.eclipse.jst.jsf.designtime.resolver.ISymbolContextResolver;
import org.eclipse.jst.jsf.designtime.resolver.StructuredDocumentSymbolResolverFactory;
import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
import org.eclipse.swt.graphics.Image;
/**
* A completion strategy for function completions like:
*
* v a r .
* ^
*
* @author cbateman
*
*/
public class FunctionCompletionStrategy extends ContentAssistStrategy
{
/**
* @param value
* @param proposalStart
*/
public FunctionCompletionStrategy(final String value, final String proposalStart)
{
super(ContentAssistStrategy.PREFIX_TYPE_DOT_COMPLETION, value, proposalStart);
}
@Override
public List<ICompletionProposal> getProposals(final IStructuredDocumentContext context)
{
List<ICompletionProposal> completionList = Collections.EMPTY_LIST;
final ISymbolContextResolver symbolResolver =
StructuredDocumentSymbolResolverFactory.getInstance().
getSymbolContextResolver(context);
final ISymbol symbol = SymbolResolveUtil.getSymbolForVariableSuffixExpr(context, getValue(), false);
// if we get a completion symbol, get it's proposals
if (symbol instanceof IObjectSymbol)
{
final List expectedMethodBindings = new ArrayList();
final ISymbol[] suffixes = getSymbols((IObjectSymbol) symbol,
context,
symbolResolver,
expectedMethodBindings);
final ComposedAdapterFactory factory =
new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
final IProposalCreationFactory creationInfo =
new MyProposalFactory(context, getProposalStart().length(),
expectedMethodBindings);
completionList = new ArrayList<ICompletionProposal>();
for (final ISymbol propSymbol : suffixes) {
final Object provider =
factory.adapt(propSymbol, IContentProposalProvider.class);
if (provider instanceof IContentProposalProvider)
{
final ICompletionProposal[] proposal =
((IContentProposalProvider) provider).
getProposals(propSymbol, creationInfo);
if (proposal != null)
{
addProposalsMatchingProposalStart(completionList,
proposal);
}
}
}
}
return Collections.unmodifiableList(completionList);
}
private ISymbol[] getSymbols(final IObjectSymbol symbol,
final IStructuredDocumentContext context,
final ISymbolContextResolver symbolResolver,
final List expectedMethodBindings)
{
final List symbols = new ArrayList();
if (SymbolResolveUtil.isMethodBindingExpected(context, expectedMethodBindings))
{
symbols.addAll(Arrays.asList(
symbolResolver.getMethods(symbol)));
}
symbols.addAll(Arrays.asList(symbolResolver.getProperties(symbol)));
return (ISymbol[]) symbols.toArray(ISymbol.EMPTY_SYMBOL_ARRAY);
}
private static class MyProposalFactory extends ProposalCreationFactoryAdapter
{
private final static int DEFAULT_RELEVANCE = 1;
private final static int HIGH_RELEVANCE = 2;
private final static int NORMAL_RELEVANCE = 1;
private final static int LOW_RELEVANCE = 0;
private final List _expectedMethodBindings;
private final IStructuredDocumentContext _context;
/**
* @param context
* @param replacementLength
* @param expectedMethodBindings
*/
public MyProposalFactory(final IStructuredDocumentContext context, final int replacementLength,
final List expectedMethodBindings) {
super(context.getDocumentPosition() - replacementLength, replacementLength);
_context = context;
_expectedMethodBindings = expectedMethodBindings;
}
@Override
public ICompletionProposal createProposal(final String replacementText,
final String displayText,
final String additionalText,
final Image displayImage,
final Object target)
{
int replacementOffset = _replacementOffset;
int replacementLength = _replacementLength;
// TODO: I regard this as a bit of hack, but until we write our
// proposal implementation, it's basically the only way I can
// see to do this
// if it's an array, we must check if we need to replace a
// preceding '.'
if (replacementText.startsWith("["))
{
ITextRegionContextResolver textResolver =
IStructuredDocumentContextResolverFactory.INSTANCE.getTextRegionResolver(_context);
if (textResolver.getRegionType().equals(DOMJSPRegionContexts.JSP_VBL_CLOSE))
{
textResolver =
IStructuredDocumentContextResolverFactory.
INSTANCE.getTextRegionResolver(textResolver.getPreviousContext());
}
final String regionText = textResolver.getRegionText();
final int regionStart = textResolver.getStartOffset();
if (DOMJSPRegionContexts.JSP_VBL_CONTENT.equals(textResolver.getRegionType())
&& regionText != null
&& regionStart != -1
&& regionStart < _context.getDocumentPosition())
{
final int relativeOffset = _context.getDocumentPosition() - regionStart - 1;
if (regionText.charAt(relativeOffset) == '.')
{
// we must replace a length of 1 (the dot)
// at an offset on prior
replacementOffset--;
replacementLength = 1;
}
}
}
return createDefaultProposal(replacementText,
replacementOffset,
replacementLength,
replacementText.length(),
displayImage,
displayText,
null,
additionalText,
getRelevance(target, DEFAULT_RELEVANCE));
}
private int getRelevance(final Object target, final int defaultRelevance)
{
// if method bindings are expected, then list exact signature
// matches top most. Still list non-matching methods, but put
// them at the bottom
if (_expectedMethodBindings.size() > 0)
{
if (target instanceof IMethodSymbol)
{
final IMethodSymbol methodSymbol = (IMethodSymbol) target;
for (final Iterator it = _expectedMethodBindings.iterator();
it.hasNext();)
{
final String methodType = (String) it.next();
// we have a match, so push to the top
if (methodType.equals(methodSymbol.getSignature()))
{
return HIGH_RELEVANCE;
}
}
// if we get out of the loop, then this method doesn't
// match the expected signature
return LOW_RELEVANCE;
}
// non-method targets have normal relevance when mb expected
return NORMAL_RELEVANCE;
}
// otherwise, simply return the default for all
return defaultRelevance;
}
}
}