Java tutorial
/******************************************************************************* * Copyright (c) 2007, 2014 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.pde.api.tools.ui.internal.completion; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.CompletionContext; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.jface.resource.ImageDescriptor; 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.eclipse.pde.api.tools.internal.JavadocTagManager; import org.eclipse.pde.api.tools.internal.builder.TagValidator; import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; import org.eclipse.pde.api.tools.internal.provisional.IApiJavadocTag; import org.eclipse.pde.api.tools.internal.util.Util; import org.eclipse.pde.api.tools.ui.internal.ApiUIPlugin; import org.eclipse.swt.graphics.Image; /** * This class creates completion proposals to Javadoc header blocks for the * Javadoc tags contributed via the apiJavadocTags extension point. <br> * <br> * Any changes to the visibility logic here also must be updated in the * {@link TagValidator} * * @see TagValidator * @see IApiJavadocTag * @see JavadocTagManager * @see APIToolsJavadocCompletionProposal * * @since 1.0.0 */ public class APIToolsJavadocCompletionProposalComputer implements IJavaCompletionProposalComputer { private String fErrorMessage = null; private Image fImageHandle = null; private ASTParser fParser = null; HashMap<String, Boolean> fExistingTags = null; /** * Collects all of the existing API Tools Javadoc tags form a given Javadoc * node */ class TagCollector extends ASTVisitor { /* * (non-Javadoc) * @see * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom * .Javadoc) */ @Override public boolean visit(Javadoc node) { Set<String> tagnames = ApiPlugin.getJavadocTagManager().getAllTagNames(); List<TagElement> tags = node.tags(); if (fExistingTags == null) { fExistingTags = new HashMap<String, Boolean>(tags.size()); } String name = null; for (TagElement tag : tags) { name = tag.getTagName(); if (name == null) { continue; } if (tagnames.contains(name)) { // only add existing API tools tags fExistingTags.put(name, Boolean.valueOf(tag.fragments().isEmpty())); } } return false; } } /* * (non-Javadoc) * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer# * computeCompletionProposals * (org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext, * org.eclipse.core.runtime.IProgressMonitor) */ @Override public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { JavaContentAssistInvocationContext jcontext = null; if (context instanceof JavaContentAssistInvocationContext) { jcontext = (JavaContentAssistInvocationContext) context; IJavaProject project = jcontext.getProject(); if (Util.isApiProject(project)) { CompletionContext corecontext = jcontext.getCoreContext(); if (corecontext.isInJavadoc()) { return computeJavadocProposals(jcontext, corecontext); } } } return Collections.EMPTY_LIST; } /** * Computes all of the Javadoc completion proposals * * @param jcontext * @param corecontext * @return the complete list of Javadoc completion proposals or an empty * list, never <code>null</code> * @since 1.0.500 */ List<ICompletionProposal> computeJavadocProposals(JavaContentAssistInvocationContext jcontext, CompletionContext corecontext) { ICompilationUnit cunit = jcontext.getCompilationUnit(); if (cunit != null) { try { int offset = jcontext.getInvocationOffset(); IJavaElement element = cunit.getElementAt(offset); if (!isVisible(element)) { return Collections.EMPTY_LIST; } ImageDescriptor imagedesc = jcontext.getLabelProvider() .createImageDescriptor(org.eclipse.jdt.core.CompletionProposal .create(org.eclipse.jdt.core.CompletionProposal.JAVADOC_BLOCK_TAG, offset)); fImageHandle = (imagedesc == null ? null : imagedesc.createImage()); int type = getType(element); int member = IApiJavadocTag.MEMBER_NONE; switch (element.getElementType()) { case IJavaElement.METHOD: { IMethod method = (IMethod) element; member = IApiJavadocTag.MEMBER_METHOD; if (method.isConstructor()) { member = IApiJavadocTag.MEMBER_CONSTRUCTOR; } break; } case IJavaElement.FIELD: { member = IApiJavadocTag.MEMBER_FIELD; break; } default: break; } IApiJavadocTag[] tags = ApiPlugin.getJavadocTagManager().getTagsForType(type, member); int tagcount = tags.length; if (tagcount > 0) { ArrayList<ICompletionProposal> list = null; collectExistingTags(element, jcontext); String completiontext = null; int tokenstart = corecontext.getTokenStart(); int length = offset - tokenstart; for (int i = 0; i < tagcount; i++) { if (!acceptTag(tags[i], element)) { continue; } completiontext = tags[i].getCompleteTag(type, member); if (appliesToContext(jcontext.getDocument(), completiontext, tokenstart, (length > 0 ? length : 1))) { if (list == null) { list = new ArrayList<ICompletionProposal>(tagcount - i); } list.add(new APIToolsJavadocCompletionProposal(corecontext, completiontext, tags[i].getTagName(), fImageHandle)); } } if (list != null) { return list; } } } catch (JavaModelException e) { fErrorMessage = e.getMessage(); } } return Collections.EMPTY_LIST; } /** * Returns if the given {@link IJavaElement} is externally visible <br> * <br> * Changes to the logic here must also be made in the {@link TagValidator} * to ensure the visibility is computed equally. * * @see TagValidator * @param element * @return <code>true</code> if the given element is visible * <code>false</code> otherwise * @throws JavaModelException if a model lookup fails */ boolean isVisible(IJavaElement element) throws JavaModelException { if (element != null) { switch (element.getElementType()) { case IJavaElement.FIELD: case IJavaElement.METHOD: { IMember member = (IMember) element; int flags = member.getFlags(); IType type = member.getDeclaringType(); if (Flags.isPublic(flags) || Flags.isProtected(flags) || (type != null && type.isInterface())) { return isVisible(type); } break; } case IJavaElement.TYPE: { IType type = (IType) element; int flags = type.getFlags(); if (type.isLocal() && !type.isAnonymous() || Flags.isPrivate(flags)) { return false; } if (type.isMember()) { if ((Flags.isPublic(flags) && Flags.isStatic(flags)) || Flags.isPublic(flags) || Flags.isProtected(flags) || type.isInterface()) { return isVisible(type.getDeclaringType()); } } else { return Flags.isPublic(flags) || type.isInterface(); } break; } default: { break; } } } return false; } /** * Method to post process returned flags from the * {@link org.eclipse.pde.api.tools.internal.JavadocTagManager} * * @param tag the tag to process * @param element the {@link IJavaElement} the tag will appear on * @return true if the tag should be included in completion proposals, false * otherwise */ private boolean acceptTag(IApiJavadocTag tag, IJavaElement element) throws JavaModelException { if (fExistingTags != null) { Boolean fragments = fExistingTags.get(tag.getTagName()); // if the tag has a fragment don't overwrite / propose again if (fragments != null && Boolean.FALSE.equals(fragments)) { return false; } } switch (element.getElementType()) { case IJavaElement.TYPE: { IType type = (IType) element; int flags = type.getFlags(); String tagname = tag.getTagName(); if (Flags.isAbstract(flags)) { return !tagname.equals(JavadocTagManager.TAG_NOINSTANTIATE); } if (Flags.isFinal(flags)) { return !tagname.equals(JavadocTagManager.TAG_NOEXTEND); } break; } case IJavaElement.METHOD: { IMethod method = (IMethod) element; if (Flags.isFinal(method.getFlags()) || Flags.isStatic(method.getFlags())) { return !tag.getTagName().equals(JavadocTagManager.TAG_NOOVERRIDE); } IType type = method.getDeclaringType(); if (type != null && Flags.isFinal(type.getFlags())) { return !tag.getTagName().equals(JavadocTagManager.TAG_NOOVERRIDE); } break; } default: break; } return true; } /** * Returns the type of the enclosing type. * * @param element java element * @return TYPE_INTERFACE, TYPE_CLASS, TYPE_ENUM, TYPE_ANNOTATION or -1 * @throws JavaModelException */ private int getType(IJavaElement element) throws JavaModelException { IJavaElement lelement = element; while (lelement != null && lelement.getElementType() != IJavaElement.TYPE) { lelement = lelement.getParent(); } if (lelement instanceof IType) { IType type = (IType) lelement; if (type.isAnnotation()) { return IApiJavadocTag.TYPE_ANNOTATION; } else if (type.isInterface()) { return IApiJavadocTag.TYPE_INTERFACE; } else if (type.isEnum()) { return IApiJavadocTag.TYPE_ENUM; } } return IApiJavadocTag.TYPE_CLASS; } /** * Collects the existing tags on the {@link IJavaElement} we have been * activated on * * @param element * @param jcontext * @throws JavaModelException * @throws BadLocationException */ private void collectExistingTags(IJavaElement element, JavaContentAssistInvocationContext jcontext) throws JavaModelException { if (element instanceof IMember) { IMember member = (IMember) element; ICompilationUnit cunit = jcontext.getCompilationUnit(); if (cunit != null) { if (cunit.isWorkingCopy()) { cunit.reconcile(ICompilationUnit.NO_AST, false, false, null, null); } fParser.setSource(member.getSource().toCharArray()); fParser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS); Map<String, String> options = element.getJavaProject().getOptions(true); options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); fParser.setCompilerOptions(options); fParser.setStatementsRecovery(false); fParser.setResolveBindings(false); fParser.setBindingsRecovery(false); ASTNode ast = fParser.createAST(null); TagCollector collector = new TagCollector(); if (ast.getNodeType() == ASTNode.TYPE_DECLARATION) { TypeDeclaration typeDeclaration = (TypeDeclaration) ast; List<BodyDeclaration> bodyDeclarations = typeDeclaration.bodyDeclarations(); if (bodyDeclarations.size() == 1) { // only one element should be there as we are parsing a // specific member BodyDeclaration bodyDeclaration = bodyDeclarations.iterator().next(); Javadoc javadoc = bodyDeclaration.getJavadoc(); if (javadoc != null) { javadoc.accept(collector); } } } } } } /** * Determines if the specified completion applies to the current offset * context in the document * * @param document * @param completiontext * @param offset * @return true if the completion applies, false otherwise */ private boolean appliesToContext(IDocument document, String completiontext, int tokenstart, int length) { if (length > completiontext.length()) { return false; } try { String prefix = document.get(tokenstart, length); return prefix.equals(completiontext.substring(0, length)); } catch (BadLocationException e) { ApiUIPlugin.log(e); } return false; } /* * (non-Javadoc) * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer# * computeContextInformation * (org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext, * org.eclipse.core.runtime.IProgressMonitor) */ @Override public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { return Collections.EMPTY_LIST; } /* * (non-Javadoc) * @see * org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#getErrorMessage * () */ @Override public String getErrorMessage() { return fErrorMessage; } /* * (non-Javadoc) * @see * org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionEnded * () */ @Override public void sessionEnded() { if (fImageHandle != null) { fImageHandle.dispose(); } fParser = null; if (fExistingTags != null) { fExistingTags.clear(); fExistingTags = null; } fErrorMessage = null; } /* * (non-Javadoc) * @see * org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionStarted * () */ @Override public void sessionStarted() { fParser = ASTParser.newParser(AST.JLS8); fErrorMessage = null; } }