| /******************************************************************************* |
| * Copyright (c) 2000, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Genady Beryozkin <eclipse@genady.org> - [hovering] tooltip for constant string does not show constant value - https://bugs.eclipse.org/bugs/show_bug.cgi?id=85382 |
| * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.text.java.hover; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| |
| import org.osgi.framework.Bundle; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Drawable; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Platform; |
| |
| import org.eclipse.core.resources.IFile; |
| |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.internal.text.html.BrowserInformationControl; |
| import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; |
| import org.eclipse.jface.internal.text.html.BrowserInput; |
| import org.eclipse.jface.internal.text.html.HTMLPrinter; |
| import org.eclipse.jface.internal.text.html.HTMLTextPresenter; |
| import org.eclipse.jface.resource.ColorRegistry; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| |
| import org.eclipse.jface.text.AbstractReusableInformationControlCreator; |
| import org.eclipse.jface.text.DefaultInformationControl; |
| import org.eclipse.jface.text.IInformationControl; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.IInformationControlExtension4; |
| import org.eclipse.jface.text.IInputChangedListener; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.TextPresentation; |
| |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchSite; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| |
| import org.eclipse.ui.editors.text.EditorsUI; |
| |
| import org.eclipse.jdt.core.IAnnotatable; |
| import org.eclipse.jdt.core.IAnnotation; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.ILocalVariable; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMemberValuePair; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IOrdinaryClassFile; |
| import org.eclipse.jdt.core.IPackageDeclaration; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.ISourceReference; |
| import org.eclipse.jdt.core.ITypeParameter; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ConstructorInvocation; |
| import org.eclipse.jdt.core.dom.IAnnotationBinding; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.IMemberValuePairBinding; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.LambdaExpression; |
| import org.eclipse.jdt.core.dom.NodeFinder; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SuperConstructorInvocation; |
| import org.eclipse.jdt.core.manipulation.SharedASTProviderCore; |
| |
| import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; |
| import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| import org.eclipse.jdt.ui.actions.OpenAttachedJavadocAction; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.JavaPluginImages; |
| import org.eclipse.jdt.internal.ui.actions.OpenBrowserUtil; |
| import org.eclipse.jdt.internal.ui.actions.SimpleSelectionProvider; |
| import org.eclipse.jdt.internal.ui.infoviews.JavadocView; |
| import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; |
| import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart; |
| import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; |
| import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer; |
| import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; |
| |
| |
| /** |
| * Provides Javadoc as hover info for Java elements. |
| * |
| * @since 2.1 |
| */ |
| public class JavadocHover extends AbstractJavaEditorTextHover { |
| |
| public static final String CONSTANT_VALUE_SEPARATOR= " : "; //$NON-NLS-1$ |
| |
| public static class FallbackInformationPresenter extends HTMLTextPresenter { |
| public FallbackInformationPresenter() { |
| super(false); |
| } |
| |
| @Override |
| public String updatePresentation(Drawable drawable, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) { |
| String warningInfo= JavaHoverMessages.JavadocHover_fallback_warning; |
| String warning= super.updatePresentation(drawable, warningInfo, presentation, maxWidth, maxHeight); |
| presentation.clear(); |
| |
| String content= super.updatePresentation(drawable, hoverInfo, presentation, maxWidth, maxHeight); |
| return content + "\n\n" + warning; //$NON-NLS-1$ |
| } |
| } |
| /** |
| * Action to go back to the previous input in the hover control. |
| * |
| * @since 3.4 |
| */ |
| private static final class BackAction extends Action { |
| private final BrowserInformationControl fInfoControl; |
| |
| public BackAction(BrowserInformationControl infoControl) { |
| fInfoControl= infoControl; |
| setText(JavaHoverMessages.JavadocHover_back); |
| ISharedImages images= PlatformUI.getWorkbench().getSharedImages(); |
| setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK)); |
| setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED)); |
| |
| update(); |
| } |
| |
| @Override |
| public void run() { |
| BrowserInformationControlInput previous= (BrowserInformationControlInput) fInfoControl.getInput().getPrevious(); |
| if (previous != null) { |
| fInfoControl.setInput(previous); |
| } |
| } |
| |
| public void update() { |
| BrowserInformationControlInput current= fInfoControl.getInput(); |
| |
| if (current != null && current.getPrevious() != null) { |
| BrowserInput previous= current.getPrevious(); |
| setToolTipText(Messages.format(JavaHoverMessages.JavadocHover_back_toElement_toolTip, BasicElementLabels.getJavaElementName(previous.getInputName()))); |
| setEnabled(true); |
| } else { |
| setToolTipText(JavaHoverMessages.JavadocHover_back); |
| setEnabled(false); |
| } |
| } |
| } |
| |
| /** |
| * Action to go forward to the next input in the hover control. |
| * |
| * @since 3.4 |
| */ |
| private static final class ForwardAction extends Action { |
| private final BrowserInformationControl fInfoControl; |
| |
| public ForwardAction(BrowserInformationControl infoControl) { |
| fInfoControl= infoControl; |
| setText(JavaHoverMessages.JavadocHover_forward); |
| ISharedImages images= PlatformUI.getWorkbench().getSharedImages(); |
| setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD)); |
| setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED)); |
| |
| update(); |
| } |
| |
| @Override |
| public void run() { |
| BrowserInformationControlInput next= (BrowserInformationControlInput) fInfoControl.getInput().getNext(); |
| if (next != null) { |
| fInfoControl.setInput(next); |
| } |
| } |
| |
| public void update() { |
| BrowserInformationControlInput current= fInfoControl.getInput(); |
| |
| if (current != null && current.getNext() != null) { |
| setToolTipText(Messages.format(JavaHoverMessages.JavadocHover_forward_toElement_toolTip, BasicElementLabels.getJavaElementName(current.getNext().getInputName()))); |
| setEnabled(true); |
| } else { |
| setToolTipText(JavaHoverMessages.JavadocHover_forward_toolTip); |
| setEnabled(false); |
| } |
| } |
| } |
| |
| /** |
| * Action that shows the current hover contents in the Javadoc view. |
| * |
| * @since 3.4 |
| */ |
| private static final class ShowInJavadocViewAction extends Action { |
| private final BrowserInformationControl fInfoControl; |
| |
| public ShowInJavadocViewAction(BrowserInformationControl infoControl) { |
| fInfoControl= infoControl; |
| setText(JavaHoverMessages.JavadocHover_showInJavadoc); |
| setImageDescriptor(JavaPluginImages.DESC_OBJS_JAVADOCTAG); //TODO: better image |
| } |
| |
| /* |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| JavadocBrowserInformationControlInput infoInput= (JavadocBrowserInformationControlInput) fInfoControl.getInput(); //TODO: check cast |
| fInfoControl.notifyDelayedInputChange(null); |
| fInfoControl.dispose(); //FIXME: should have protocol to hide, rather than dispose |
| try { |
| JavadocView view= (JavadocView) JavaPlugin.getActivePage().showView(JavaUI.ID_JAVADOC_VIEW); |
| view.setInput(infoInput); |
| } catch (PartInitException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| /** |
| * Action that opens the current hover input element. |
| * |
| * @since 3.4 |
| */ |
| private static final class OpenDeclarationAction extends Action { |
| private final BrowserInformationControl fInfoControl; |
| |
| public OpenDeclarationAction(BrowserInformationControl infoControl) { |
| fInfoControl= infoControl; |
| setText(JavaHoverMessages.JavadocHover_openDeclaration); |
| JavaPluginImages.setLocalImageDescriptors(this, "goto_input.png"); //$NON-NLS-1$ //TODO: better images |
| } |
| |
| /* |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| JavadocBrowserInformationControlInput infoInput= (JavadocBrowserInformationControlInput) fInfoControl.getInput(); //TODO: check cast |
| fInfoControl.notifyDelayedInputChange(null); |
| fInfoControl.dispose(); //FIXME: should have protocol to hide, rather than dispose |
| |
| try { |
| //FIXME: add hover location to editor navigation history? |
| openDeclaration(infoInput.getElement()); |
| } catch (PartInitException e) { |
| JavaPlugin.log(e); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| |
| /** |
| * Presenter control creator. |
| * |
| * @since 3.3 |
| */ |
| public static final class PresenterControlCreator extends AbstractReusableInformationControlCreator { |
| |
| private final IWorkbenchSite fSite; |
| |
| /** |
| * Creates a new PresenterControlCreator. |
| * |
| * @param site the site or <code>null</code> if none |
| * @since 3.6 |
| */ |
| public PresenterControlCreator(IWorkbenchSite site) { |
| fSite= site; |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell) |
| */ |
| @Override |
| public IInformationControl doCreateInformationControl(Shell parent) { |
| if (BrowserInformationControl.isAvailable(parent)) { |
| ToolBarManager tbm= new ToolBarManager(SWT.FLAT); |
| String font= PreferenceConstants.APPEARANCE_JAVADOC_FONT; |
| BrowserInformationControl iControl= new BrowserInformationControl(parent, font, tbm); |
| |
| final BackAction backAction= new BackAction(iControl); |
| backAction.setEnabled(false); |
| tbm.add(backAction); |
| final ForwardAction forwardAction= new ForwardAction(iControl); |
| tbm.add(forwardAction); |
| forwardAction.setEnabled(false); |
| |
| final ShowInJavadocViewAction showInJavadocViewAction= new ShowInJavadocViewAction(iControl); |
| tbm.add(showInJavadocViewAction); |
| final OpenDeclarationAction openDeclarationAction= new OpenDeclarationAction(iControl); |
| tbm.add(openDeclarationAction); |
| |
| final SimpleSelectionProvider selectionProvider= new SimpleSelectionProvider(); |
| if (fSite != null) { |
| OpenAttachedJavadocAction openAttachedJavadocAction= new OpenAttachedJavadocAction(fSite); |
| openAttachedJavadocAction.setSpecialSelectionProvider(selectionProvider); |
| openAttachedJavadocAction.setImageDescriptor(JavaPluginImages.DESC_ELCL_OPEN_BROWSER); |
| openAttachedJavadocAction.setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_OPEN_BROWSER); |
| selectionProvider.addSelectionChangedListener(openAttachedJavadocAction); |
| selectionProvider.setSelection(new StructuredSelection()); |
| tbm.add(openAttachedJavadocAction); |
| } |
| |
| IInputChangedListener inputChangeListener= new IInputChangedListener() { |
| @Override |
| public void inputChanged(Object newInput) { |
| backAction.update(); |
| forwardAction.update(); |
| if (newInput == null) { |
| selectionProvider.setSelection(new StructuredSelection()); |
| } else if (newInput instanceof BrowserInformationControlInput) { |
| BrowserInformationControlInput input= (BrowserInformationControlInput) newInput; |
| Object inputElement= input.getInputElement(); |
| selectionProvider.setSelection(new StructuredSelection(inputElement)); |
| boolean isJavaElementInput= inputElement instanceof IJavaElement; |
| showInJavadocViewAction.setEnabled(isJavaElementInput); |
| openDeclarationAction.setEnabled(isJavaElementInput); |
| } |
| } |
| }; |
| iControl.addInputChangeListener(inputChangeListener); |
| |
| tbm.update(true); |
| |
| addLinkListener(iControl); |
| return iControl; |
| |
| } else { |
| return new DefaultInformationControl(parent, true); |
| } |
| } |
| } |
| |
| |
| /** |
| * Hover control creator. |
| * |
| * @since 3.3 |
| */ |
| public static final class HoverControlCreator extends AbstractReusableInformationControlCreator implements IPropertyChangeListener { |
| /** |
| * The information presenter control creator. |
| * @since 3.4 |
| */ |
| private final IInformationControlCreator fInformationPresenterControlCreator; |
| /** |
| * <code>true</code> to use the additional info affordance, <code>false</code> to use the hover affordance. |
| */ |
| private final boolean fAdditionalInfoAffordance; |
| |
| /** |
| * @param informationPresenterControlCreator control creator for enriched hover |
| * @since 3.4 |
| */ |
| public HoverControlCreator(IInformationControlCreator informationPresenterControlCreator) { |
| this(informationPresenterControlCreator, false); |
| } |
| |
| /** |
| * @param informationPresenterControlCreator control creator for enriched hover |
| * @param additionalInfoAffordance <code>true</code> to use the additional info affordance, <code>false</code> to use the hover affordance |
| * @since 3.4 |
| */ |
| public HoverControlCreator(IInformationControlCreator informationPresenterControlCreator, boolean additionalInfoAffordance) { |
| fInformationPresenterControlCreator= informationPresenterControlCreator; |
| fAdditionalInfoAffordance= additionalInfoAffordance; |
| } |
| |
| private BrowserInformationControl iControl; |
| /* |
| * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell) |
| */ |
| @Override |
| public IInformationControl doCreateInformationControl(Shell parent) { |
| String tooltipAffordanceString= fAdditionalInfoAffordance ? JavaPlugin.getAdditionalInfoAffordanceString() : EditorsUI.getTooltipAffordanceString(); |
| if (BrowserInformationControl.isAvailable(parent)) { |
| String font= PreferenceConstants.APPEARANCE_JAVADOC_FONT; |
| iControl= new BrowserInformationControl(parent, font, tooltipAffordanceString) { |
| /* |
| * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator() |
| */ |
| @Override |
| public IInformationControlCreator getInformationPresenterControlCreator() { |
| return fInformationPresenterControlCreator; |
| } |
| }; |
| |
| JFaceResources.getColorRegistry().addListener(this); // So propertyChange() method is triggered in context of IPropertyChangeListener |
| setHoverColors(); |
| |
| addLinkListener(iControl); |
| return iControl; |
| } else { |
| return new DefaultInformationControl(parent, tooltipAffordanceString) { |
| @Override |
| public IInformationControlCreator getInformationPresenterControlCreator() { |
| return new IInformationControlCreator() { |
| @Override |
| public IInformationControl createInformationControl(Shell parentShell) { |
| return new DefaultInformationControl(parentShell, (ToolBarManager) null, new FallbackInformationPresenter()); |
| } |
| }; |
| } |
| }; |
| } |
| } |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| String property= event.getProperty(); |
| if (iControl != null && |
| (property.equals("org.eclipse.jdt.ui.Javadoc.foregroundColor") //$NON-NLS-1$ |
| || property.equals("org.eclipse.jdt.ui.Javadoc.backgroundColor"))) { //$NON-NLS-1$ |
| setHoverColors(); |
| } |
| } |
| |
| private void setHoverColors() { |
| ColorRegistry registry = JFaceResources.getColorRegistry(); |
| Color fgRGB = registry.get("org.eclipse.jdt.ui.Javadoc.foregroundColor"); //$NON-NLS-1$ |
| Color bgRGB = registry.get("org.eclipse.jdt.ui.Javadoc.backgroundColor"); //$NON-NLS-1$ |
| iControl.setForegroundColor(fgRGB); |
| iControl.setBackgroundColor(bgRGB); |
| } |
| |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| super.widgetDisposed(e); |
| //Called when active editor is closed. |
| JFaceResources.getColorRegistry().removeListener(this); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractReusableInformationControlCreator#canReuse(org.eclipse.jface.text.IInformationControl) |
| */ |
| @Override |
| public boolean canReuse(IInformationControl control) { |
| if (!super.canReuse(control)) |
| return false; |
| |
| if (control instanceof IInformationControlExtension4) { |
| String tooltipAffordanceString= fAdditionalInfoAffordance ? JavaPlugin.getAdditionalInfoAffordanceString() : EditorsUI.getTooltipAffordanceString(); |
| ((IInformationControlExtension4)control).setStatusText(tooltipAffordanceString); |
| } |
| |
| return true; |
| } |
| } |
| |
| private static final long LABEL_FLAGS= JavaElementLabels.ALL_FULLY_QUALIFIED |
| | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_ANNOTATIONS | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS |
| | JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.M_PRE_TYPE_PARAMETERS | JavaElementLabels.T_TYPE_PARAMETERS |
| | JavaElementLabels.USE_RESOLVED; |
| private static final long LOCAL_VARIABLE_FLAGS= LABEL_FLAGS & ~JavaElementLabels.F_FULLY_QUALIFIED | JavaElementLabels.F_POST_QUALIFIED; |
| private static final long TYPE_PARAMETER_FLAGS= LABEL_FLAGS | JavaElementLabels.TP_POST_QUALIFIED; |
| private static final long PACKAGE_FLAGS= LABEL_FLAGS & ~JavaElementLabels.ALL_FULLY_QUALIFIED; |
| |
| /** |
| * The style sheet (css). |
| * @since 3.4 |
| */ |
| private static String fgStyleSheet; |
| |
| /** |
| * The hover control creator. |
| * |
| * @since 3.2 |
| */ |
| private IInformationControlCreator fHoverControlCreator; |
| /** |
| * The presentation control creator. |
| * |
| * @since 3.2 |
| */ |
| private IInformationControlCreator fPresenterControlCreator; |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover#getInformationPresenterControlCreator() |
| * @since 3.1 |
| */ |
| @Override |
| public IInformationControlCreator getInformationPresenterControlCreator() { |
| if (fPresenterControlCreator == null) |
| fPresenterControlCreator= new PresenterControlCreator(getSite()); |
| return fPresenterControlCreator; |
| } |
| |
| private IWorkbenchSite getSite() { |
| IEditorPart editor= getEditor(); |
| if (editor == null) { |
| IWorkbenchPage page= JavaPlugin.getActivePage(); |
| if (page != null) |
| editor= page.getActiveEditor(); |
| } |
| if (editor != null) |
| return editor.getSite(); |
| |
| return null; |
| } |
| |
| /* |
| * @see ITextHoverExtension#getHoverControlCreator() |
| * @since 3.2 |
| */ |
| @Override |
| public IInformationControlCreator getHoverControlCreator() { |
| if (fHoverControlCreator == null) |
| fHoverControlCreator= new HoverControlCreator(getInformationPresenterControlCreator()); |
| return fHoverControlCreator; |
| } |
| |
| |
| public static IEditorPart openDeclaration(IJavaElement element) throws PartInitException, JavaModelException { |
| if (!(element instanceof IPackageFragment)) { |
| return JavaUI.openInEditor(element); |
| } |
| |
| IPackageFragment packageFragment= (IPackageFragment) element; |
| ITypeRoot typeRoot; |
| IPackageFragmentRoot root= (IPackageFragmentRoot) packageFragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); |
| if (root.getKind() == IPackageFragmentRoot.K_BINARY) { |
| typeRoot= packageFragment.getClassFile(JavaModelUtil.PACKAGE_INFO_CLASS); |
| } else { |
| typeRoot= packageFragment.getCompilationUnit(JavaModelUtil.PACKAGE_INFO_JAVA); |
| } |
| |
| // open the package-info file in editor if one exists |
| if (typeRoot.exists()) |
| return JavaUI.openInEditor(typeRoot); |
| |
| // open the package.html file in editor if one exists |
| Object[] nonJavaResources= packageFragment.getNonJavaResources(); |
| for (Object nonJavaResource : nonJavaResources) { |
| if (nonJavaResource instanceof IFile) { |
| IFile file= (IFile) nonJavaResource; |
| if (file.exists() && JavaModelUtil.PACKAGE_HTML.equals(file.getName())) { |
| return EditorUtility.openInEditor(file, true); |
| } |
| } |
| } |
| |
| // select the package in the Package Explorer if there is no associated package Javadoc file |
| PackageExplorerPart view= (PackageExplorerPart) JavaPlugin.getActivePage().showView(JavaUI.ID_PACKAGES); |
| view.tryToReveal(packageFragment); |
| return null; |
| } |
| |
| private static void addLinkListener(final BrowserInformationControl control) { |
| control.addLocationListener(JavaElementLinks.createLocationListener(new JavaElementLinks.ILinkHandler() { |
| @Override |
| public void handleJavadocViewLink(IJavaElement linkTarget) { |
| control.notifyDelayedInputChange(null); |
| control.setVisible(false); |
| control.dispose(); //FIXME: should have protocol to hide, rather than dispose |
| try { |
| JavadocView view= (JavadocView) JavaPlugin.getActivePage().showView(JavaUI.ID_JAVADOC_VIEW); |
| view.setInput(linkTarget); |
| } catch (PartInitException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| |
| @Override |
| public void handleInlineJavadocLink(IJavaElement linkTarget) { |
| JavadocBrowserInformationControlInput hoverInfo= getHoverInfo(new IJavaElement[] { linkTarget }, null, null, (JavadocBrowserInformationControlInput) control.getInput()); |
| if (control.hasDelayedInputChangeListener()) |
| control.notifyDelayedInputChange(hoverInfo); |
| else |
| control.setInput(hoverInfo); |
| } |
| |
| @Override |
| public void handleDeclarationLink(IJavaElement linkTarget) { |
| control.notifyDelayedInputChange(null); |
| control.dispose(); //FIXME: should have protocol to hide, rather than dispose |
| try { |
| //FIXME: add hover location to editor navigation history? |
| openDeclaration(linkTarget); |
| } catch (PartInitException e) { |
| JavaPlugin.log(e); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| |
| @Override |
| public boolean handleExternalLink(URL url, Display display) { |
| control.notifyDelayedInputChange(null); |
| control.dispose(); //FIXME: should have protocol to hide, rather than dispose |
| |
| // Open attached Javadoc links |
| OpenBrowserUtil.open(url, display); |
| |
| return true; |
| } |
| |
| @Override |
| public void handleTextSet() { |
| } |
| })); |
| } |
| |
| /** |
| * @deprecated see {@link org.eclipse.jface.text.ITextHover#getHoverInfo(ITextViewer, IRegion)} |
| */ |
| @Override |
| @Deprecated |
| public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { |
| JavadocBrowserInformationControlInput info= (JavadocBrowserInformationControlInput) getHoverInfo2(textViewer, hoverRegion); |
| return info != null ? info.getHtml() : null; |
| } |
| |
| /* |
| * @see org.eclipse.jface.text.ITextHoverExtension2#getHoverInfo2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) |
| */ |
| @Override |
| public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { |
| return internalGetHoverInfo(textViewer, hoverRegion); |
| } |
| |
| private JavadocBrowserInformationControlInput internalGetHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { |
| IJavaElement[] elements= getJavaElementsAt(textViewer, hoverRegion); |
| if (elements == null || elements.length == 0) |
| return null; |
| |
| return getHoverInfo(elements, getEditorInputJavaElement(), hoverRegion, null); |
| } |
| |
| /** |
| * Returns the first package with a valid Javadoc when there are multiple packages with the same |
| * name in the project. If no package could be found with a valid Javadoc then returns the first |
| * package in the array. If the array does not contain package, then return the array unaltered. |
| * |
| * @param elements array from which to filter duplicate packages |
| * @return the first package with a valid Javadoc. If no package is found with a valid Javadoc |
| * then return the first element in the array if the element is of type |
| * IPackageFragment, else return the elements array unaltered |
| * @since 3.9 |
| */ |
| private static IJavaElement[] filterDuplicatePackages(IJavaElement[] elements) { |
| if (elements.length <= 1 || !(elements[0] instanceof IPackageFragment)) { |
| return elements; |
| } |
| |
| for (IJavaElement element : elements) { |
| try { |
| if (element instanceof IPackageFragment) { |
| IPackageFragment packageFragment= (IPackageFragment) element; |
| if (JavadocContentAccess2.getHTMLContent(packageFragment) != null) |
| return new IJavaElement[] { packageFragment }; |
| } |
| } catch (CoreException e) { |
| //ignore the exception and consider the next element to process |
| } |
| } |
| |
| return new IJavaElement[] { elements[0] }; |
| } |
| |
| /** |
| * Computes the hover info. |
| * |
| * @param elements the resolved elements |
| * @param editorInputElement the editor input, or <code>null</code> |
| * @param hoverRegion the text range of the hovered word, or <code>null</code> |
| * @param previousInput the previous input, or <code>null</code> |
| * @return the HTML hover info for the given element(s) or <code>null</code> if no information is available |
| * @since 3.4 |
| */ |
| public static JavadocBrowserInformationControlInput getHoverInfo(IJavaElement[] elements, ITypeRoot editorInputElement, IRegion hoverRegion, JavadocBrowserInformationControlInput previousInput) { |
| StringBuilder buffer= new StringBuilder(); |
| boolean hasContents= false; |
| String base= null; |
| IJavaElement element= null; |
| int leadingImageWidth= 0; |
| |
| elements= filterDuplicatePackages(elements); |
| |
| if (elements.length > 1) { |
| for (IJavaElement el : elements) { |
| HTMLPrinter.startBulletList(buffer); |
| IJavaElement curr= el; |
| if (curr instanceof IMember || curr.getElementType() == IJavaElement.LOCAL_VARIABLE) { |
| String label= JavaElementLabels.getElementLabel(curr, getHeaderFlags(curr)); |
| String link; |
| try { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, curr); |
| link= JavaElementLinks.createLink(uri, label); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| link= label; |
| } |
| HTMLPrinter.addBullet(buffer, link); |
| hasContents= true; |
| } |
| HTMLPrinter.endBulletList(buffer); |
| } |
| } else { |
| element= elements[0]; |
| |
| if (element instanceof IPackageFragment || element instanceof IMember |
| || element instanceof ILocalVariable || element instanceof ITypeParameter) { |
| HTMLPrinter.addSmallHeader(buffer, getInfoText(element, editorInputElement, hoverRegion, true)); |
| buffer.append("<br>"); //$NON-NLS-1$ |
| addAnnotations(buffer, element, editorInputElement, hoverRegion); |
| Reader reader= null; |
| try { |
| String content= JavadocContentAccess2.getHTMLContent(element, true); |
| IPackageFragmentRoot root= (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); |
| boolean isBinary= root.exists() && root.getKind() == IPackageFragmentRoot.K_BINARY; |
| if (content != null) { |
| base= JavadocContentAccess2.extractBaseURL(content); |
| if (base == null) { |
| base= JavaDocLocations.getBaseURL(element, isBinary); |
| } |
| reader= new StringReader(content); |
| } else { |
| String explanationForMissingJavadoc= JavaDocLocations.getExplanationForMissingJavadoc(element, root); |
| if (explanationForMissingJavadoc != null) |
| reader= new StringReader(explanationForMissingJavadoc); |
| } |
| } catch (CoreException ex) { |
| reader= new StringReader(JavaDocLocations.handleFailedJavadocFetch(ex)); |
| } |
| |
| if (reader != null) { |
| HTMLPrinter.addParagraph(buffer, reader); |
| } |
| hasContents= true; |
| } |
| leadingImageWidth= 20; |
| } |
| |
| if (!hasContents) |
| return null; |
| |
| if (buffer.length() > 0) { |
| |
| ColorRegistry registry = JFaceResources.getColorRegistry(); |
| RGB fgRGB = registry.getRGB("org.eclipse.jdt.ui.Javadoc.foregroundColor"); //$NON-NLS-1$ |
| RGB bgRGB= registry.getRGB("org.eclipse.jdt.ui.Javadoc.backgroundColor"); //$NON-NLS-1$ |
| |
| HTMLPrinter.insertPageProlog(buffer, 0, fgRGB, bgRGB, JavadocHover.getStyleSheet()); |
| if (base != null) { |
| int endHeadIdx= buffer.indexOf("</head>"); //$NON-NLS-1$ |
| buffer.insert(endHeadIdx, "\n<base href='" + base + "'>\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| HTMLPrinter.addPageEpilog(buffer); |
| return new JavadocBrowserInformationControlInput(previousInput, element, buffer.toString(), leadingImageWidth); |
| } |
| |
| return null; |
| } |
| |
| private static String getInfoText(IJavaElement element, ITypeRoot editorInputElement, IRegion hoverRegion, boolean allowImage) { |
| long flags= getHeaderFlags(element); |
| |
| boolean haveSource= editorInputElement instanceof ICompilationUnit; |
| ASTNode node= haveSource ? getHoveredASTNode(editorInputElement, hoverRegion) : null; |
| IBinding binding= getHoverBinding(element, node); |
| |
| StringBuffer label; |
| if (binding != null) { |
| label= new StringBuffer(JavaElementLinks.getBindingLabel(binding, element, flags, haveSource)); |
| } else { |
| label= new StringBuffer(JavaElementLinks.getElementLabel(element, flags)); |
| } |
| |
| if (element.getElementType() == IJavaElement.FIELD) { |
| String constantValue= getConstantValue((IField) element, editorInputElement, hoverRegion); |
| if (constantValue != null) { |
| constantValue= HTMLPrinter.convertToHTMLContentWithWhitespace(constantValue); |
| label.append(CONSTANT_VALUE_SEPARATOR); |
| label.append(constantValue); |
| } |
| } |
| |
| if (element.getElementType() == IJavaElement.METHOD) { |
| String defaultValue; |
| try { |
| defaultValue= getAnnotationMemberDefaultValue((IMethod) element, editorInputElement, hoverRegion); |
| } catch (JavaModelException e) { |
| defaultValue= null; |
| } |
| if (defaultValue != null) { |
| defaultValue= HTMLPrinter.convertToHTMLContentWithWhitespace(defaultValue); |
| label.append(CONSTANT_VALUE_SEPARATOR); |
| label.append(defaultValue); |
| } |
| } |
| |
| return getImageAndLabel(element, allowImage, label.toString()); |
| } |
| |
| /** |
| * Returns the default value of the given annotation type method. |
| * |
| * @param method the method |
| * @param editorInputElement the editor input element |
| * @param hoverRegion the hover region in the editor |
| * @return the default value of the given annotation type method or <code>null</code> if none |
| * @throws JavaModelException if an exception occurs while accessing its default value |
| */ |
| public static String getAnnotationMemberDefaultValue(IMethod method, ITypeRoot editorInputElement, IRegion hoverRegion) throws JavaModelException { |
| IMemberValuePair memberValuePair= method.getDefaultValue(); |
| if (memberValuePair == null) { |
| return null; |
| } |
| |
| Object defaultValue= memberValuePair.getValue(); |
| boolean isEmptyArray= defaultValue instanceof Object[] && ((Object[]) defaultValue).length == 0; |
| int valueKind= memberValuePair.getValueKind(); |
| |
| if (valueKind == IMemberValuePair.K_UNKNOWN && !isEmptyArray) { |
| IBinding binding= getHoveredNodeBinding(method, editorInputElement, hoverRegion); |
| if (binding instanceof IMethodBinding) { |
| Object value= ((IMethodBinding) binding).getDefaultValue(); |
| StringBuffer buf= new StringBuffer(); |
| try { |
| addValue(buf, value, false); |
| } catch (URISyntaxException e) { |
| // should not happen as links are not added |
| } |
| return buf.toString(); |
| } |
| |
| } else if (defaultValue != null) { |
| IAnnotation parentAnnotation= (IAnnotation) method.getAncestor(IJavaElement.ANNOTATION); |
| StringBuffer buf= new StringBuffer(); |
| new JavaElementLabelComposer(buf).appendAnnotationValue(parentAnnotation, defaultValue, valueKind, LABEL_FLAGS); |
| return buf.toString(); |
| } |
| |
| return null; |
| } |
| |
| private static IBinding getHoveredNodeBinding(IJavaElement element, ITypeRoot editorInputElement, IRegion hoverRegion) { |
| if (editorInputElement == null || hoverRegion == null) { |
| return null; |
| } |
| IBinding binding; |
| ASTNode node= getHoveredASTNode(editorInputElement, hoverRegion); |
| if (node == null) { |
| ASTParser p= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); |
| p.setProject(element.getJavaProject()); |
| p.setBindingsRecovery(true); |
| try { |
| binding= p.createBindings(new IJavaElement[] { element }, null)[0]; |
| } catch (OperationCanceledException e) { |
| return null; |
| } |
| } else { |
| binding= resolveBinding(node); |
| } |
| return binding; |
| } |
| |
| /** |
| * Try to acquire a binding corresponding to the given element |
| * for more precise information about (type) annotations. |
| * |
| * Currently this lookup is only enabled when null-annotations are enabled for the project. |
| * |
| * @param element the element being rendered |
| * @param node the AST node corresponding to the given element, or null, if no AST node is available. |
| * @return either a binding corresponding to the given element or null. |
| */ |
| public static IBinding getHoverBinding(IJavaElement element, ASTNode node) { |
| |
| if (element.getJavaProject().getOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, true).equals(JavaCore.ENABLED)) { |
| if (node == null) { |
| if (element instanceof ISourceReference) { |
| ASTParser p= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); |
| p.setProject(element.getJavaProject()); |
| p.setBindingsRecovery(true); |
| try { |
| return p.createBindings(new IJavaElement[] { element }, null)[0]; |
| } catch (OperationCanceledException e) { |
| return null; |
| } |
| } |
| } else { |
| return resolveBinding(node); |
| } |
| } |
| return null; |
| } |
| |
| private static String getImageURL(IJavaElement element) { |
| String imageName= null; |
| URL imageUrl= JavaPlugin.getDefault().getImagesOnFSRegistry().getImageURL(element); |
| if (imageUrl != null) { |
| imageName= imageUrl.toExternalForm(); |
| } |
| |
| return imageName; |
| } |
| |
| public static long getHeaderFlags(IJavaElement element) { |
| switch (element.getElementType()) { |
| case IJavaElement.LOCAL_VARIABLE: |
| return LOCAL_VARIABLE_FLAGS; |
| case IJavaElement.TYPE_PARAMETER: |
| return TYPE_PARAMETER_FLAGS; |
| case IJavaElement.PACKAGE_FRAGMENT: |
| case IJavaElement.PACKAGE_DECLARATION: |
| return PACKAGE_FLAGS; |
| default: |
| return LABEL_FLAGS; |
| } |
| } |
| |
| /** |
| * Tells whether the given field is static final. |
| * |
| * @param field the member to test |
| * @return <code>true</code> if static final |
| * @since 3.4 |
| */ |
| public static boolean isStaticFinal(IField field) { |
| try { |
| return JdtFlags.isFinal(field) && JdtFlags.isStatic(field); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| return false; |
| } |
| } |
| |
| /** |
| * Returns the constant value for the given field. |
| * |
| * @param field the field |
| * @param editorInputElement the editor input element |
| * @param hoverRegion the hover region in the editor |
| * @return the constant value for the given field or <code>null</code> if none |
| * @since 3.4 |
| */ |
| public static String getConstantValue(IField field, ITypeRoot editorInputElement, IRegion hoverRegion) { |
| if (!isStaticFinal(field)) |
| return null; |
| |
| Object constantValue; |
| ASTNode node= getHoveredASTNode(editorInputElement, hoverRegion); |
| if (node != null) { |
| constantValue= getVariableBindingConstValue(node, field); |
| } else { |
| constantValue= JavadocView.computeFieldConstantFromTypeAST(field, null); |
| } |
| if (constantValue == null) |
| return null; |
| |
| if (constantValue instanceof String) { |
| return ASTNodes.getEscapedStringLiteral((String) constantValue); |
| } else { |
| return getHexConstantValue(constantValue); |
| } |
| } |
| |
| private static ASTNode getHoveredASTNode(ITypeRoot editorInputElement, IRegion hoverRegion) { |
| if (editorInputElement == null || hoverRegion == null) |
| return null; |
| |
| CompilationUnit unit= SharedASTProviderCore.getAST(editorInputElement, SharedASTProviderCore.WAIT_ACTIVE_ONLY, null); |
| if (unit == null) |
| return null; |
| |
| return NodeFinder.perform(unit, hoverRegion.getOffset(), hoverRegion.getLength()); |
| } |
| |
| /** |
| * Creates and returns a formatted message for the given |
| * constant with its hex value. |
| * |
| * @param constantValue the constant value |
| * @param hexValue the hex value |
| * @return a formatted string with constant and hex values |
| * @since 3.4 |
| */ |
| private static String formatWithHexValue(Object constantValue, String hexValue) { |
| return Messages.format(JavaHoverMessages.JavadocHover_constantValue_hexValue, new String[] { constantValue.toString(), hexValue }); |
| } |
| |
| /** |
| * Returns the Javadoc hover style sheet with the current Javadoc font from the preferences. |
| * @return the updated style sheet |
| * @since 3.4 |
| */ |
| private static String getStyleSheet() { |
| if (fgStyleSheet == null) { |
| fgStyleSheet= loadStyleSheet("/JavadocHoverStyleSheet.css"); //$NON-NLS-1$ |
| } |
| String css= fgStyleSheet; |
| if (css != null) { |
| FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0]; |
| css= HTMLPrinter.convertTopLevelFont(css, fontData); |
| } |
| |
| return css; |
| } |
| |
| /** |
| * Loads and returns the style sheet associated with either Javadoc hover or the view. |
| * |
| * @param styleSheetName the style sheet name of either the Javadoc hover or the view |
| * @return the style sheet, or <code>null</code> if unable to load |
| * @since 3.4 |
| */ |
| public static String loadStyleSheet(String styleSheetName) { |
| Bundle bundle= Platform.getBundle(JavaPlugin.getPluginId()); |
| URL styleSheetURL= bundle.getEntry(styleSheetName); |
| if (styleSheetURL == null) |
| return null; |
| |
| BufferedReader reader= null; |
| try { |
| reader= new BufferedReader(new InputStreamReader(styleSheetURL.openStream())); |
| StringBuilder buffer= new StringBuilder(1500); |
| String line= reader.readLine(); |
| while (line != null) { |
| buffer.append(line); |
| buffer.append('\n'); |
| line= reader.readLine(); |
| } |
| |
| FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0]; |
| return HTMLPrinter.convertTopLevelFont(buffer.toString(), fontData); |
| } catch (IOException ex) { |
| JavaPlugin.log(ex); |
| return ""; //$NON-NLS-1$ |
| } finally { |
| try { |
| if (reader != null) |
| reader.close(); |
| } catch (IOException e) { |
| //ignore |
| } |
| } |
| } |
| |
| public static String getImageAndLabel(IJavaElement element, boolean allowImage, String label) { |
| StringBuilder buf= new StringBuilder(); |
| int imageWidth= 16; |
| int imageHeight= 16; |
| int labelLeft= 20; |
| int labelTop= 2; |
| |
| buf.append("<div style='word-wrap: break-word; position: relative; "); //$NON-NLS-1$ |
| |
| String imageSrcPath= allowImage ? getImageURL(element) : null; |
| if (imageSrcPath != null) { |
| buf.append("margin-left: ").append(labelLeft).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("padding-top: ").append(labelTop).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| buf.append("'>"); //$NON-NLS-1$ |
| if (imageSrcPath != null) { |
| if (element != null) { |
| try { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.OPEN_LINK_SCHEME, element); |
| buf.append("<a href='").append(uri).append("'>"); //$NON-NLS-1$//$NON-NLS-2$ |
| } catch (URISyntaxException e) { |
| element= null; // no link |
| } |
| } |
| StringBuffer imageStyle= new StringBuffer("border:none; position: absolute; "); //$NON-NLS-1$ |
| imageStyle.append("width: ").append(imageWidth).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$ |
| imageStyle.append("height: ").append(imageHeight).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$ |
| imageStyle.append("left: ").append(- labelLeft - 1).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| // hack for broken transparent PNG support in IE 6, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223900 : |
| buf.append("<!--[if lte IE 6]><![if gte IE 5.5]>\n"); //$NON-NLS-1$ |
| String tooltip= element == null ? "" : "alt='" + JavaHoverMessages.JavadocHover_openDeclaration + "' "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| buf.append("<span ").append(tooltip).append("style=\"").append(imageStyle). //$NON-NLS-1$ //$NON-NLS-2$ |
| append("filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='").append(imageSrcPath).append("')\"></span>\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("<![endif]><![endif]-->\n"); //$NON-NLS-1$ |
| |
| buf.append("<!--[if !IE]>-->\n"); //$NON-NLS-1$ |
| buf.append("<img ").append(tooltip).append("style='").append(imageStyle).append("' src='").append(imageSrcPath).append("'/>\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| buf.append("<!--<![endif]-->\n"); //$NON-NLS-1$ |
| buf.append("<!--[if gte IE 7]>\n"); //$NON-NLS-1$ |
| buf.append("<img ").append(tooltip).append("style='").append(imageStyle).append("' src='").append(imageSrcPath).append("'/>\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| buf.append("<![endif]-->\n"); //$NON-NLS-1$ |
| if (element != null) { |
| buf.append("</a>"); //$NON-NLS-1$ |
| } |
| } |
| |
| buf.append(label); |
| |
| buf.append("</div>"); //$NON-NLS-1$ |
| return buf.toString(); |
| } |
| |
| public static void addAnnotations(StringBuilder buf, IJavaElement element, ITypeRoot editorInputElement, IRegion hoverRegion) { |
| try { |
| if (element instanceof IAnnotatable) { |
| String annotationString= getAnnotations(element, editorInputElement, hoverRegion); |
| if (annotationString != null) { |
| buf.append("<div style='margin-bottom: 5px;'>"); //$NON-NLS-1$ |
| buf.append(annotationString); |
| buf.append("</div>"); //$NON-NLS-1$ |
| } |
| } else if (element instanceof IPackageFragment) { |
| IPackageFragment pack= (IPackageFragment) element; |
| ICompilationUnit cu= pack.getCompilationUnit(JavaModelUtil.PACKAGE_INFO_JAVA); |
| if (cu.exists()) { |
| IPackageDeclaration[] packDecls= cu.getPackageDeclarations(); |
| if (packDecls.length > 0) { |
| addAnnotations(buf, packDecls[0], null, null); |
| } |
| } else { |
| IOrdinaryClassFile classFile= pack.getOrdinaryClassFile(JavaModelUtil.PACKAGE_INFO_CLASS); |
| if (classFile.exists()) { |
| addAnnotations(buf, classFile.getType(), null, null); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| // no annotations this time... |
| buf.append("<br>"); //$NON-NLS-1$ |
| } catch (URISyntaxException e) { |
| // no annotations this time... |
| buf.append("<br>"); //$NON-NLS-1$ |
| } |
| } |
| |
| private static String getAnnotations(IJavaElement element, ITypeRoot editorInputElement, IRegion hoverRegion) throws URISyntaxException, JavaModelException { |
| if (!(element instanceof IPackageFragment)) { |
| if (!(element instanceof IAnnotatable)) |
| return null; |
| |
| if (((IAnnotatable)element).getAnnotations().length == 0) |
| return null; |
| } |
| |
| IBinding binding= getHoveredNodeBinding(element, editorInputElement, hoverRegion); |
| if (binding == null) |
| return null; |
| |
| IAnnotationBinding[] annotations= binding.getAnnotations(); |
| if (annotations.length == 0) |
| return null; |
| |
| StringBuffer buf= new StringBuffer(); |
| for (IAnnotationBinding annotation : annotations) { |
| //TODO: skip annotations that don't have an @Documented annotation? |
| addAnnotation(buf, annotation, true); |
| buf.append("<br>"); //$NON-NLS-1$ |
| } |
| |
| return buf.toString(); |
| } |
| |
| private static IBinding resolveBinding(ASTNode node) { |
| if (node instanceof SimpleName) { |
| SimpleName simpleName= (SimpleName) node; |
| // workaround for https://bugs.eclipse.org/62605 (constructor name resolves to type, not method) |
| ASTNode normalized= ASTNodes.getNormalizedNode(simpleName); |
| if (normalized.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) { |
| ClassInstanceCreation cic= (ClassInstanceCreation) normalized.getParent(); |
| IMethodBinding constructorBinding= cic.resolveConstructorBinding(); |
| if (constructorBinding == null) |
| return null; |
| ITypeBinding declaringClass= constructorBinding.getDeclaringClass(); |
| if (!declaringClass.isAnonymous()) |
| return constructorBinding; |
| ITypeBinding superTypeDeclaration= declaringClass.getSuperclass().getTypeDeclaration(); |
| return resolveSuperclassConstructor(superTypeDeclaration, constructorBinding); |
| } |
| return simpleName.resolveBinding(); |
| |
| } else if (node instanceof SuperConstructorInvocation) { |
| return ((SuperConstructorInvocation) node).resolveConstructorBinding(); |
| } else if (node instanceof ConstructorInvocation) { |
| return ((ConstructorInvocation) node).resolveConstructorBinding(); |
| } else if (node instanceof LambdaExpression) { |
| return ((LambdaExpression) node).resolveMethodBinding(); |
| } else { |
| return null; |
| } |
| } |
| |
| private static IBinding resolveSuperclassConstructor(ITypeBinding superClassDeclaration, IMethodBinding constructor) { |
| for (IMethodBinding method : superClassDeclaration.getDeclaredMethods()) { |
| if (method.isConstructor() && constructor.isSubsignature(method)) |
| return method; |
| } |
| return null; |
| } |
| |
| private static void addAnnotation(StringBuffer buf, IAnnotationBinding annotation, boolean addLinks) throws URISyntaxException { |
| IJavaElement javaElement= annotation.getAnnotationType().getJavaElement(); |
| buf.append('@'); |
| if (javaElement == null || !addLinks) { |
| buf.append(annotation.getName()); |
| } else { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, javaElement); |
| addLink(buf, uri, annotation.getName()); |
| } |
| |
| IMemberValuePairBinding[] mvPairs= annotation.getDeclaredMemberValuePairs(); |
| if (mvPairs.length > 0) { |
| buf.append('('); |
| for (int j= 0; j < mvPairs.length; j++) { |
| if (j > 0) { |
| buf.append(JavaElementLabels.COMMA_STRING); |
| } |
| IMemberValuePairBinding mvPair= mvPairs[j]; |
| if (addLinks) { |
| String memberURI= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, mvPair.getMethodBinding().getJavaElement()); |
| addLink(buf, memberURI, mvPair.getName()); |
| } else { |
| buf.append(mvPair.getName()); |
| } |
| buf.append('='); |
| addValue(buf, mvPair.getValue(), addLinks); |
| } |
| buf.append(')'); |
| } |
| } |
| |
| private static void addValue(StringBuffer buf, Object value, boolean addLinks) throws URISyntaxException { |
| // Note: To be bug-compatible with Javadoc from Java 5/6/7, we currently don't escape HTML tags in String-valued annotations. |
| if (value instanceof ITypeBinding) { |
| ITypeBinding typeBinding= (ITypeBinding)value; |
| IJavaElement type= typeBinding.getJavaElement(); |
| if (type == null || !addLinks) { |
| buf.append(typeBinding.getName()); |
| } else { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, type); |
| String name= type.getElementName(); |
| addLink(buf, uri, name); |
| } |
| buf.append(".class"); //$NON-NLS-1$ |
| |
| } else if (value instanceof IVariableBinding) { // only enum constants |
| IVariableBinding variableBinding= (IVariableBinding)value; |
| IJavaElement variable= variableBinding.getJavaElement(); |
| if (variable == null || !addLinks) { |
| buf.append(variableBinding.getName()); |
| } else { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, variable); |
| String name= variable.getElementName(); |
| addLink(buf, uri, name); |
| } |
| |
| } else if (value instanceof IAnnotationBinding) { |
| IAnnotationBinding annotationBinding= (IAnnotationBinding)value; |
| addAnnotation(buf, annotationBinding, addLinks); |
| |
| } else if (value instanceof String) { |
| buf.append(ASTNodes.getEscapedStringLiteral((String)value)); |
| |
| } else if (value instanceof Character) { |
| buf.append(ASTNodes.getEscapedCharacterLiteral(((Character)value).charValue())); |
| |
| } else if (value instanceof Object[]) { |
| Object[] values= (Object[])value; |
| buf.append('{'); |
| for (int i= 0; i < values.length; i++) { |
| if (i > 0) { |
| buf.append(JavaElementLabels.COMMA_STRING); |
| } |
| addValue(buf, values[i], addLinks); |
| } |
| buf.append('}'); |
| |
| } else { // primitive types (except char) or null |
| buf.append(String.valueOf(value)); |
| } |
| } |
| |
| private static StringBuffer addLink(StringBuffer buf, String uri, String label) { |
| return buf.append(JavaElementLinks.createLink(uri, label)); |
| } |
| |
| public static String getHexConstantValue(Object constantValue) { |
| if (constantValue instanceof Character) { |
| String constantResult= '\'' + constantValue.toString() + '\''; |
| |
| char charValue= ((Character) constantValue).charValue(); |
| String hexString= Integer.toHexString(charValue); |
| StringBuilder hexResult= new StringBuilder("\\u"); //$NON-NLS-1$ |
| for (int i= hexString.length(); i < 4; i++) { |
| hexResult.append('0'); |
| } |
| hexResult.append(hexString); |
| return formatWithHexValue(constantResult, hexResult.toString()); |
| |
| } else if (constantValue instanceof Byte) { |
| int byteValue= ((Byte) constantValue).intValue() & 0xFF; |
| return formatWithHexValue(constantValue, "0x" + Integer.toHexString(byteValue)); //$NON-NLS-1$ |
| |
| } else if (constantValue instanceof Short) { |
| int shortValue= ((Short) constantValue).shortValue() & 0xFFFF; |
| return formatWithHexValue(constantValue, "0x" + Integer.toHexString(shortValue)); //$NON-NLS-1$ |
| |
| } else if (constantValue instanceof Integer) { |
| int intValue= ((Integer) constantValue).intValue(); |
| return formatWithHexValue(constantValue, "0x" + Integer.toHexString(intValue)); //$NON-NLS-1$ |
| |
| } else if (constantValue instanceof Long) { |
| long longValue= ((Long) constantValue).longValue(); |
| return formatWithHexValue(constantValue, "0x" + Long.toHexString(longValue)); //$NON-NLS-1$ |
| |
| } else { |
| return constantValue.toString(); |
| } |
| } |
| |
| public static Object getVariableBindingConstValue(ASTNode node, IField field) { |
| if (node != null && node.getNodeType() == ASTNode.SIMPLE_NAME) { |
| IBinding binding= ((SimpleName) node).resolveBinding(); |
| if (binding != null && binding.getKind() == IBinding.VARIABLE) { |
| IVariableBinding variableBinding= (IVariableBinding) binding; |
| if (field.equals(variableBinding.getJavaElement())) { |
| return variableBinding.getConstantValue(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| |
| } |