/******************************************************************************* * Copyright (c) 2007, 2013 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.ui.editor.util; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.ICompilationUnit; 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.ITypeHierarchy; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; import org.eclipse.jdt.internal.corext.util.TypeFilter; import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jdt.ui.text.java.CompletionProposalComparator; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IContentAssistContext; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IContentAssistProposalRecorder; import org.springframework.ide.eclipse.core.SpringCore; import org.springframework.ide.eclipse.core.java.JdtUtils; /** * Utility class provides methods to trigger content assist for package and class content assist requests. * @author Christian Dupuis * @author Torsten Juergeleit * @since 1.3.6 */ @SuppressWarnings("restriction") public class BeansJavaCompletionUtils { public static final int FLAG_INTERFACE = 1 << 2; public static final int FLAG_CLASS = 1 << 3; public static final int FLAG_PACKAGE = 1 << 4; private static final String CLASS_NAME = "_xxx"; private static final String CLASS_SOURCE_END = "\n" + " }\n" + "}"; private static final String CLASS_SOURCE_START = "public class " + CLASS_NAME + " {\n" + " public void main(String[] args) {\n" + " "; private static CompletionProposalComparator COMPARATOR = new CompletionProposalComparator(); private static ILabelProvider JAVA_LABEL_PROVIDER = new JavaElementLabelProvider( JavaElementLabelProvider.SHOW_DEFAULT | JavaElementLabelProvider.SHOW_POST_QUALIFIED); /** * Add class and package content assist proposals that match the given <code>prefix</code>. */ public static void addClassValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder) { addClassValueProposals(context, recorder, FLAG_PACKAGE | FLAG_CLASS); } /** * Add interface content assist proposals that match the given <code>prefix</code>. */ public static void addInterfaceValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder) { addClassValueProposals(context, recorder, FLAG_PACKAGE | FLAG_INTERFACE); } /** * Add package content assist proposals that match the given <code>prefix</code>. */ public static void addPackageValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder) { addClassValueProposals(context, recorder, FLAG_PACKAGE); } /** * Add class and package content assist proposals that match the given <code>prefix</code>. */ public static void addClassValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder, int flags) { String prefix = context.getMatchString(); if (prefix == null || prefix.length() == 0) { return; } try { IFile file = context.getFile(); if (file != null && file.exists()) { ICompilationUnit unit = createSourceCompilationUnit(file, prefix); char enclosingChar = (prefix.lastIndexOf('$') > 0 ? '$' : '.'); prefix = prefix.replace('$', '.'); // Code completion below only provides public and protected inner classes; therefore // we manually add the private inner classes if possible if (prefix.lastIndexOf('.') > 0) { String rootClass = prefix.substring(0, prefix.lastIndexOf('.')); IType type = JdtUtils.getJavaType(file.getProject(), rootClass); if (type != null) { for (IType innerType : type.getTypes()) { if (Flags.isPrivate(innerType.getFlags()) && innerType.getFullyQualifiedName('.').startsWith(prefix)) { recorder.recordProposal(JAVA_LABEL_PROVIDER.getImage(innerType), 10, JAVA_LABEL_PROVIDER .getText(innerType), innerType.getFullyQualifiedName(enclosingChar), innerType); } } } } String sourceStart = CLASS_SOURCE_START + prefix; String packageName = null; int dot = prefix.lastIndexOf('.'); if (dot > -1) { packageName = prefix.substring(0, dot); sourceStart = "package " + packageName + ";\n" + sourceStart; } String source = sourceStart + CLASS_SOURCE_END; setContents(unit, source); BeansJavaCompletionProposalCollector collector = new BeansJavaCompletionProposalCollector(unit, flags); unit.codeComplete(sourceStart.length(), collector, DefaultWorkingCopyOwner.PRIMARY); IJavaCompletionProposal[] props = collector.getJavaCompletionProposals(); ICompletionProposal[] proposals = order(props); for (ICompletionProposal comProposal : proposals) { processJavaCompletionProposal(recorder, comProposal, packageName, enclosingChar); } } } catch (Exception e) { // do nothing } } /** * Add class assist proposals that match the given <code>prefix</code> and are part of the sub class hierarchy of * the given <code>typeName</code>. * @param request the {@link ContentAssistRequest} to add the proposals * @param prefix the prefix * @param typeName the super class of the request proposals */ public static void addTypeHierachyAttributeValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder, String typeName, int flags) { final String prefix = context.getMatchString(); if (prefix == null || prefix.length() == 0) { return; } IFile file = context.getFile(); if (file != null && file.exists()) { IType type = JdtUtils.getJavaType(file.getProject(), typeName); try { if (type != null && file.getProject().hasNature(JavaCore.NATURE_ID)) { // Make sure that JDT's type filter preferences are applied if (!TypeFilter.isFiltered(type)) { ITypeHierarchy hierachy = type.newTypeHierarchy(JavaCore.create(file.getProject()), new NullProgressMonitor()); IType[] types = hierachy.getAllSubtypes(type); Map<String, IType> sortMap = new HashMap<String, IType>(); for (IType foundType : types) { if ((foundType.getFullyQualifiedName().startsWith(prefix) || foundType.getElementName() .startsWith(prefix)) && !sortMap.containsKey(foundType.getFullyQualifiedName()) && !Flags.isAbstract(foundType.getFlags())) { boolean accepted = false; if ((flags & BeansJavaCompletionUtils.FLAG_CLASS) != 0 && !Flags.isInterface(foundType.getFlags())) { accepted = true; } else if ((flags & BeansJavaCompletionUtils.FLAG_INTERFACE) != 0 && Flags.isInterface(foundType.getFlags())) { accepted = true; } if (accepted) { recorder.recordProposal(JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CLASS), 10, foundType.getElementName() + " - " + foundType.getPackageFragment().getElementName(), foundType .getFullyQualifiedName(), foundType); sortMap.put(foundType.getFullyQualifiedName(), foundType); } } } } } } catch (JavaModelException e) { } catch (CoreException e) { } } } @SuppressWarnings("deprecation") private static ICompilationUnit createSourceCompilationUnit(IFile file, String prefix) throws JavaModelException { IProgressMonitor progressMonitor = BeansEditorUtils.getProgressMonitor(); IJavaProject project = JavaCore.create(file.getProject()); IPackageFragment root = getPackageFragment(project, prefix); ICompilationUnit unit = root.getCompilationUnit("_xxx.java").getWorkingCopy( CompilationUnitHelper.getInstance().getWorkingCopyOwner(), CompilationUnitHelper.getInstance().getProblemRequestor(), progressMonitor); progressMonitor.done(); return unit; } private static IPackageFragment getPackageFragment(IJavaProject project, String prefix) throws JavaModelException { int dot = prefix.lastIndexOf('.'); if (dot > -1) { String packageName = prefix.substring(0, dot); for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) { IPackageFragment p = root.getPackageFragment(packageName); if (p != null && p.exists()) { return p; } } IPackageFragment[] packages = project.getPackageFragments(); for (IPackageFragment p : packages) { if (p.getElementName().equals(packageName)) return p; } } else { for (IPackageFragmentRoot p : project.getAllPackageFragmentRoots()) { if (p.getKind() == IPackageFragmentRoot.K_SOURCE) { return p.getPackageFragment(""); } } } return project.getPackageFragments()[0]; } /** * Order the given proposals. */ @SuppressWarnings("unchecked") private static ICompletionProposal[] order(ICompletionProposal[] proposals) { Arrays.sort(proposals, COMPARATOR); return proposals; } private static void processJavaCompletionProposal(IContentAssistProposalRecorder recorder, ICompletionProposal comProposal, String packageName, char enclosingChar) { if (comProposal instanceof JavaCompletionProposal) { JavaCompletionProposal prop = (JavaCompletionProposal) comProposal; recorder.recordProposal(prop.getImage(), prop.getRelevance(), prop.getDisplayString(), prop .getReplacementString(), prop.getJavaElement()); } else if (comProposal instanceof LazyJavaTypeCompletionProposal) { LazyJavaTypeCompletionProposal prop = (LazyJavaTypeCompletionProposal) comProposal; if (prop.getQualifiedTypeName().equals(packageName + "." + CLASS_NAME) || prop.getQualifiedTypeName().equals(CLASS_NAME)) { return; } if (prop.getJavaElement() instanceof IType) { // Make sure that JDT's type filter preferences are applied if (TypeFilter.isFiltered((IType) prop.getJavaElement())) { return; } String replacementString = ((IType) prop.getJavaElement()).getFullyQualifiedName(enclosingChar); recorder.recordProposal(prop.getImage(), prop.getRelevance(), prop.getDisplayString(), replacementString, prop.getJavaElement()); } } else if (comProposal instanceof LazyJavaCompletionProposal) { LazyJavaCompletionProposal prop = (LazyJavaCompletionProposal) comProposal; recorder.recordProposal(prop.getImage(), prop.getRelevance(), prop.getDisplayString(), prop .getReplacementString(), prop.getJavaElement()); } } /** * Set contents of the compilation unit to the translated jsp text. * @param the ICompilationUnit on which to set the buffer contents */ private static void setContents(ICompilationUnit cu, String source) { if (cu == null) return; synchronized (cu) { IBuffer buffer; try { buffer = cu.getBuffer(); } catch (JavaModelException e) { SpringCore.log(e); buffer = null; } if (buffer != null) buffer.setContents(source); } } }