/******************************************************************************* * Copyright (c) 2012 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.frameworks.ui.internal.contentassist; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext; import org.eclipse.jdt.internal.corext.refactoring.TypeContextChecker; import org.eclipse.jdt.internal.ui.dialogs.FilteredTypesSelectionDialog; import org.eclipse.jdt.internal.ui.refactoring.contentassist.CompletionContextRequestor; import org.eclipse.jdt.internal.ui.refactoring.contentassist.ControlContentAssistHelper; import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.window.Window; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.dialogs.SearchPattern; import org.springsource.ide.eclipse.commons.frameworks.core.FrameworkCoreActivator; import org.springsource.ide.eclipse.commons.frameworks.core.internal.commands.JavaParameterDescriptor; import org.springsource.ide.eclipse.commons.frameworks.core.internal.java.FrameworksJavaUtils; import org.springsource.ide.eclipse.commons.frameworks.ui.internal.parameters.editors.IProjectSelectionChangeListener; /** * This adapter allows callers to adapt UI controls to support Java content * assist and Java type browsing. * <p> * The controls are specified by a handler passed in by some UI component that * requires Java content assist and, optionally, Java type browsing. These UI * components are typically wizard pages and dialogues. * </p> * <p> * The adapter specifies content assist functionality that is added to a text * control where Java type names are entered * </p> * <p> * The adapter, also optionally specifies functionality that is added to a * browse button. * </p> * <p> * Subclasses can override the content assist getter to return content assist * providers more specific to their domain. * </p> * <p> * IMPORTANT: This content assist adapter is using an older Java content assist. * As of 3.6, it is using the same content assist as found in the Java New Type * wizard. A longer term TODO is to move away from the old Java content assist * and adopt the newer platform content assist. * </p> * @author Nieraj Singh */ public class JavaContentAssistUIAdapter implements IProjectSelectionChangeListener { private IJavaContentAssistHandler javaContentAssistHandler; private IJavaProject javaProject; private JavaParameterDescriptor javaParameter; private IContentAssistProcessor processor; /** * The java parameter contains the type of java to search for or use in * content assist. Must not be null {@link IllegalArgumentException} is * thrown if the parameter is null. * * * @param javaParameter * must not be null */ public JavaContentAssistUIAdapter(JavaParameterDescriptor javaParameter) { Assert.isLegal(javaParameter != null); this.javaParameter = javaParameter; } public static boolean isNotifierValid(IJavaContentAssistHandler adapt) { return adapt != null && adapt.getShell() != null && adapt.getJavaTextControl() != null; } /** * Adapts a java int type to a Java search type. Returns -1 if it cannot * adapt * * @param type * @return Java search type or -1 if it cannot adapt * @see IJavaSearchConstants */ private static int adaptToJavaSearchType(int type) { switch (type) { case JavaParameterDescriptor.FLAG_INTERFACE: return IJavaSearchConstants.INTERFACE; case JavaParameterDescriptor.FLAG_CLASS: return IJavaSearchConstants.CLASS; case JavaParameterDescriptor.FLAG_PACKAGE: return IJavaSearchConstants.PACKAGE; } return -1; } protected void applyContentAssist(Text text) { processor = createContentAssistProcessor(); if (processor != null) { ControlContentAssistHelper.createTextContentAssistant(text, processor); reconfigureContentAssistProcessor(); } } /** * Typically this method is not invoked by the subclass definition. It is is * normally invoked by UI components that wish their UI controls adapted to * the content assist and java type search mechanisms defined in this * adapter. * * @param handler */ public void adapt(IJavaContentAssistHandler handler) { this.javaContentAssistHandler = handler; Assert.isLegal(isNotifierValid(handler)); Button browse = handler.getBrowseButtonControl(); final Text text = handler.getJavaTextControl(); applyContentAssist(text); text.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { notifyTextSet(text.getText()); } }); if (browse != null) { browse.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { browseButtonPressed(); } public void widgetDefaultSelected(SelectionEvent e) { browseButtonPressed(); } }); } } /** * Handles the browse button pressing. */ protected void browseButtonPressed() { if (javaProject == null || javaParameter == null) { return; } int type = javaParameter.getJavaElementType(); BrowseButtonHandler browseHandler = null; switch (type) { case JavaParameterDescriptor.FLAG_CLASS: case JavaParameterDescriptor.FLAG_INTERFACE: browseHandler = new TypeBrowseButtonHandler( javaContentAssistHandler, javaParameter, javaProject); break; case JavaParameterDescriptor.FLAG_PACKAGE: browseHandler = new PackageBrowseButtonHandler( javaContentAssistHandler, javaParameter, javaProject); break; } if (browseHandler != null) { browseHandler.browseButtonPressed(); } } protected void notifyTextSet(String text) { javaContentAssistHandler.handleJavaTypeSelection(text); } /** * * @return for content assist support, this must not be null. Otherwise no * content assist support is added to the handler's text control */ protected IContentAssistProcessor createContentAssistProcessor() { if (javaParameter == null) { return null; } int type = javaParameter.getJavaElementType(); switch (type) { case JavaParameterDescriptor.FLAG_CLASS: case JavaParameterDescriptor.FLAG_INTERFACE: return new JavaTypeCompletionProcessor(false, false, true); case JavaParameterDescriptor.FLAG_PACKAGE: return new JavaProjectPackageCompletionProcessor(); } return null; } protected void reconfigureContentAssistProcessor() { if (javaProject == null) { return; } if (processor instanceof JavaTypeCompletionProcessor) { JavaTypeCompletionProcessor javaProcessor = (JavaTypeCompletionProcessor) processor; IPackageFragmentRoot[] roots = FrameworksJavaUtils .getFirstEncounteredSourcePackageFragmentRoots(javaProject); // Search for the first default package fragment in the package // fragment roots // of the first encountered source class path entry in the Java // project. // It seems that this works even when looking for proposals outside // of the package // fragment or Java project. It is a working "hack", but this should // be made extensible // such that specific domains can provide their own context if (roots != null) { IPackageFragment fragment = null; for (IPackageFragmentRoot root : roots) { try { IJavaElement[] members = root.getChildren(); if (members != null) { for (IJavaElement element : members) { if (element instanceof IPackageFragment) { IPackageFragment frag = (IPackageFragment) element; if (frag.isDefaultPackage()) { fragment = frag; break; } } } } if (fragment != null) { break; } } catch (JavaModelException e) { FrameworkCoreActivator.logError( e.getLocalizedMessage(), e); } } if (fragment != null) { final IPackageFragment packFrag = fragment; javaProcessor .setCompletionContextRequestor(new CompletionContextRequestor() { public StubTypeContext getStubTypeContext() { return getContentAssistTypeContext( packFrag, JavaTypeCompletionProcessor.DUMMY_CLASS_NAME); } }); } } } else if (processor instanceof JavaProjectPackageCompletionProcessor) { ((JavaProjectPackageCompletionProcessor) processor) .setProject(javaProject); } } protected StubTypeContext getContentAssistTypeContext( IPackageFragment frag, String typeName) { if (javaParameter == null) { return null; } int type = javaParameter.getJavaElementType(); switch (type) { case JavaParameterDescriptor.FLAG_CLASS: return TypeContextChecker.createSuperClassStubTypeContext(typeName, null, frag); case JavaParameterDescriptor.FLAG_INTERFACE: return TypeContextChecker.createSuperInterfaceStubTypeContext( typeName, null, frag); } return null; } /* * (non-Javadoc) * * @see com.springsource.sts.frameworks.ui.internal.parameters.editors. * IProjectSelectionChangeListener * #projectSelectionChanged(org.eclipse.core.resources.IProject) */ public void projectSelectionChanged(IProject project) { if (project == null) { javaProject = null; return; } this.javaProject = JavaCore.create(project); reconfigureContentAssistProcessor(); } public static abstract class BrowseButtonHandler { private IJavaContentAssistHandler handler; private JavaParameterDescriptor descriptor; private IJavaProject project; /** * * @param handler cannot be null * @param descriptor cannot be null * @param project cannot be null */ public BrowseButtonHandler(IJavaContentAssistHandler handler, JavaParameterDescriptor descriptor, IJavaProject project) { this.handler = handler; this.descriptor = descriptor; this.project = project; } protected IJavaContentAssistHandler getHandler() { return handler; } protected JavaParameterDescriptor getJavaParameterDescriptor() { return descriptor; } protected IJavaProject getJavaProject() { return project; } protected void setValueInTextControl(String value) { Text text = getHandler().getJavaTextControl(); if (text == null || text.isDisposed()) { return; } text.setText(value); } /** * Set the value in the Text control and notify the handler that the * value was set. * @param value to set in text control and notify handler */ protected void setValue(String value) { setValueInTextControl(value); getHandler().handleJavaTypeSelection(value); } abstract public void browseButtonPressed(); } public static class PackageBrowseButtonHandler extends BrowseButtonHandler { public PackageBrowseButtonHandler(IJavaContentAssistHandler handler, JavaParameterDescriptor descriptor, IJavaProject project) { super(handler, descriptor, project); } public void browseButtonPressed() { IPackageFragment[] projectFragments = getPackageFragments(); if (projectFragments == null) { return; } Shell shell = getHandler().getShell(); ElementListSelectionDialog dialog = new ElementListSelectionDialog( shell, new JavaElementLabelProvider( JavaElementLabelProvider.SHOW_DEFAULT)); dialog.setIgnoreCase(false); dialog.setTitle(getJavaParameterDescriptor().getName()); dialog.setMessage("Select a source package"); dialog.setEmptyListMessage("No source packages to select in given project"); dialog.setElements(projectFragments); dialog.setHelpAvailable(false); if (dialog.open() == Window.OK) { String dialogValue = ((IPackageFragment) dialog.getFirstResult()) .getElementName(); setValue(dialogValue); } } protected IPackageFragment[] getPackageFragments() { IPackageFragmentRoot[] roots; try { roots = getJavaProject().getAllPackageFragmentRoots(); if (roots == null) { return null; } } catch (JavaModelException e1) { return null; } Text textControl = getHandler().getJavaTextControl(); String pattern = textControl != null ? textControl.getText() : null; SearchPattern searchPattern = pattern != null ? new SearchPattern() : null; if (searchPattern != null) { searchPattern.setPattern(pattern); } List<IPackageFragment> packageFragments = new ArrayList<IPackageFragment>(); for (IPackageFragmentRoot root : roots) { try { // To filter out all other package fragments from // dependencies, // ONLY check for source root types. A similar thing is done // for the Java new type wizard. If this needs to be changed // comment out the CPE_SOURCE check below. IClasspathEntry entry = root.getRawClasspathEntry(); if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) { continue; } IJavaElement[] children = root.getChildren(); for (IJavaElement child : children) { // Check for duplicates if (child instanceof IPackageFragment) { String name = ((IPackageFragment) child) .getElementName(); // If no pattern specified, or it matches a pattern // add the fragment. Do not add if a patter is specified // but it does not match if (searchPattern == null || searchPattern.matches(name)) { packageFragments.add((IPackageFragment) child); } } } } catch (JavaModelException e) { // ignore } } return packageFragments .toArray(new IPackageFragment[packageFragments.size()]); } } public static class TypeBrowseButtonHandler extends BrowseButtonHandler { public TypeBrowseButtonHandler(IJavaContentAssistHandler handler, JavaParameterDescriptor descriptor, IJavaProject project) { super(handler, descriptor, project); } private static final String TITLE = "Select a type"; public void browseButtonPressed() { Text textControl = getHandler().getJavaTextControl(); String pattern = textControl != null && !textControl.isDisposed()? textControl.getText() : null; Shell shell = getHandler().getShell(); int javaSearchType = adaptToJavaSearchType(getJavaParameterDescriptor() .getJavaElementType()); if (javaSearchType == -1) { return; } IJavaElement[] elements = new IJavaElement[] { getJavaProject() }; IJavaSearchScope scope = SearchEngine .createJavaSearchScope(elements); FilteredTypesSelectionDialog dialog = new FilteredTypesSelectionDialog( shell, false, null, scope, javaSearchType); dialog.setTitle(getJavaParameterDescriptor().getName()); dialog.setMessage(TITLE); dialog.setInitialPattern(pattern); if (dialog.open() == Window.OK) { IType type = (IType) dialog.getFirstResult(); if (type != null) { String qualifiedName = type.getFullyQualifiedName(); setValue(qualifiedName); } } } } }