/******************************************************************************* * Copyright (c) 2016 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.completions.constructors; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.ui.text.java.FillArgumentNamesCompletionProposalCollector; import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; import org.springsource.ide.eclipse.commons.completions.CompletionsActivator; /** * Computes constructor proposals for the the case of "List<String> l = new <Ctrl-Space>", e.g. no prefix case * * @author Alex Boyko * */ @SuppressWarnings("restriction") public class NoPrefixConstructorProposalComputer implements IJavaCompletionProposalComputer{ private static final String NEW_KEYWORD = "new"; private static final long JAVA_CODE_ASSIST_TIMEOUT= Long.getLong("org.eclipse.jdt.ui.codeAssistTimeout", 5000).longValue(); // ms //$NON-NLS-1$ @Override public void sessionStarted() { // Nothing to do } @Override public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { if (context instanceof JavaContentAssistInvocationContext) { JavaContentAssistInvocationContext jdtContext = (JavaContentAssistInvocationContext) context; try { String prefix =jdtContext.computeIdentifierPrefix().toString(); /* * No prefix, "new" keyword preceding and certain type expected? Compute constructors based on expected type. */ if (prefix.isEmpty() && isNewKeywordPreceeding(jdtContext) && jdtContext.getExpectedType() != null) { List<ICompletionProposal> proposals = new ArrayList<>(); proposals.addAll(Arrays.asList(doComputeCompletionProposals(jdtContext, createTimeoutProgressMonitor(JAVA_CODE_ASSIST_TIMEOUT)))); return proposals; } } catch (BadLocationException e) { CompletionsActivator.log(e); } } return Collections.emptyList(); } /** * Checks whether "new" keyword is preceding the content assist invocation position * @param context * @return */ private boolean isNewKeywordPreceeding(JavaContentAssistInvocationContext context) { IDocument document= context.getDocument(); if (document == null) return false; int end = skipWhiteSpaceBackward(document, context.getInvocationOffset()); if (end >= NEW_KEYWORD.length() - 1) { try { if (NEW_KEYWORD.equals(document.get(end - 2, NEW_KEYWORD.length()))) { end = end - 2; if (end == 0) { return true; } else { return !Character.isJavaIdentifierPart(document.getChar(end - 1)); } } } catch (BadLocationException e) { // ignore, shouldn't happen } } return false; } private int skipWhiteSpaceBackward(IDocument document, int end) { int start = end; while (--start >= 0) { try { if (Character.isJavaIdentifierPart(document.getChar(start))) break; } catch (BadLocationException e) { // ignore cannot be bad location } } return start; } private IJavaCompletionProposal[] doComputeCompletionProposals(JavaContentAssistInvocationContext context, IProgressMonitor monitor) { /* * Setup completion proposals collector to gather constructors related proposals */ CompletionProposalCollector collector = createCollector(context); collector.setIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, false); collector.setIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, false); collector.setAllowsRequiredProposals(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true); collector.setAllowsRequiredProposals(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true); try { JavaProject javaProject = (JavaProject) context.getProject(); SearchableEnvironment searchableEnvironment = new SearchableEnvironment(javaProject, new ICompilationUnit[] { context.getCompilationUnit() }); org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit)context.getCompilationUnit(); /* * Create special completion engine to collect constructor proposals */ ConstructorCompletionEngine engine = new ConstructorCompletionEngine(cu, collector, searchableEnvironment, javaProject, DefaultWorkingCopyOwner.PRIMARY, monitor); /* * Gather completion proposals */ engine.complete(cu, context.getInvocationOffset(), null, context.getExpectedType()); } catch (JavaModelException e) { CompletionsActivator.log(e); } catch (OperationCanceledException e) { /* * Timeout has occurred. This proposal computer took too much time */ CompletionsActivator.log(new Status(IStatus.WARNING, CompletionsActivator.PLUGIN_ID, "Constructor completion proposal timed out", e)); } return collector.getJavaCompletionProposals(); } private CompletionProposalCollector createCollector(JavaContentAssistInvocationContext context) { if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES)) return new FillArgumentNamesCompletionProposalCollector(context); else return new CompletionProposalCollector(context.getCompilationUnit(), true); } private static IProgressMonitor createTimeoutProgressMonitor(final long timeout) { return new IProgressMonitor() { private long fEndTime; @Override public void beginTask(String name, int totalWork) { fEndTime= System.currentTimeMillis() + timeout; } @Override public boolean isCanceled() { return fEndTime <= System.currentTimeMillis(); } @Override public void done() { } @Override public void internalWorked(double work) { } @Override public void setCanceled(boolean value) { } @Override public void setTaskName(String name) { } @Override public void subTask(String name) { } @Override public void worked(int work) { } }; } @Override public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { // Nothing to do return null; } @Override public String getErrorMessage() { // Nothing to do return null; } @Override public void sessionEnded() { // Nothing to do } }