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