/******************************************************************************* | |
* Copyright (c) 2000, 2009 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 | |
* 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 | |
* BSI Business Systems Integration AG - adapted to Scout SDK | |
******************************************************************************/ | |
package org.eclipse.scout.sdk.ui.fields.tooltip; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.Reader; | |
import java.io.StringReader; | |
import java.net.URL; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Platform; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.jobs.Job; | |
import org.eclipse.jdt.core.IJavaElement; | |
import org.eclipse.jdt.core.IMember; | |
import org.eclipse.jdt.core.IOpenable; | |
import org.eclipse.jdt.core.IPackageFragmentRoot; | |
import org.eclipse.jdt.core.JavaModelException; | |
import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations; | |
import org.eclipse.jdt.internal.ui.JavaPlugin; | |
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover; | |
import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; | |
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; | |
import org.eclipse.jdt.ui.JavaElementLabels; | |
import org.eclipse.jdt.ui.JavadocContentAccess; | |
import org.eclipse.jdt.ui.PreferenceConstants; | |
import org.eclipse.jface.internal.text.html.HTMLPrinter; | |
import org.eclipse.jface.resource.JFaceResources; | |
import org.eclipse.scout.commons.StringUtility; | |
import org.eclipse.scout.sdk.compatibility.JavadocHoverUtility; | |
import org.eclipse.scout.sdk.ui.internal.ScoutSdkUi; | |
import org.eclipse.scout.sdk.util.type.TypeUtility; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.SWTError; | |
import org.eclipse.swt.browser.Browser; | |
import org.eclipse.swt.graphics.FontData; | |
import org.eclipse.swt.layout.GridData; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Display; | |
import org.osgi.framework.Bundle; | |
/** | |
* Renders JavaDoc of a given {@link IMember}. | |
* <p> | |
* This class is based on {@link JavadocHover}. | |
* | |
* @author Andreas Hoegger | |
* @since 1.0.8 17.09.2010 | |
*/ | |
@SuppressWarnings("restriction") | |
public class JavadocTooltip extends AbstractTooltip { | |
/** Flags used to render a label in the text widget. */ | |
private static final long LABEL_FLAGS = JavaElementLabels.ALL_FULLY_QUALIFIED | |
| JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS | |
| JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.T_TYPE_PARAMETERS; | |
private IMember m_originalMember; | |
private IMember m_currentMember; | |
private Browser m_browser; | |
private String m_javaDoc; | |
public JavadocTooltip(Control sourceControl) { | |
super(sourceControl); | |
} | |
public void setMember(IMember member) { | |
m_originalMember = member; | |
resetCurrentMember(); | |
} | |
private void resetCurrentMember() { | |
setCurrentMember(m_originalMember); | |
} | |
private void setCurrentMember(IMember member) { | |
if (m_currentMember != member) { | |
m_currentMember = member; | |
computeJavadoc(); | |
} | |
} | |
@Override | |
protected void createContent(Composite parent) { | |
try { | |
m_browser = new Browser(parent, SWT.NONE); | |
m_browser.setJavascriptEnabled(true); | |
m_browser.setText(m_javaDoc); | |
GridData layoutData = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH); | |
layoutData.heightHint = 200; | |
layoutData.widthHint = 650; | |
m_browser.setLayoutData(layoutData); | |
m_browser.addLocationListener(JavaElementLinks.createLocationListener(new JavaElementLinks.ILinkHandler() { | |
@Override | |
public void handleTextSet() { | |
} | |
@Override | |
public void handleJavadocViewLink(IJavaElement target) { | |
} | |
@Override | |
public void handleInlineJavadocLink(IJavaElement target) { | |
if (target instanceof IMember) { | |
setCurrentMember((IMember) target); | |
} | |
} | |
@Override | |
public boolean handleExternalLink(URL url, Display display) { | |
return false; | |
} | |
@Override | |
public void handleDeclarationLink(IJavaElement target) { | |
} | |
})); | |
} | |
catch (SWTError swterr) { | |
// can happen if no browser is installed in the OS or if the browser cannot be found (not correctly registered). | |
// see http://www.eclipse.org/swt/faq.php#browserlinuxrcp | |
// and: https://bbs.archlinux.org/viewtopic.php?pid=266262#p266262 | |
ScoutSdkUi.logError("Error creating Javadoc Tooltip: " + swterr.getMessage()); | |
if (m_browser != null) { | |
m_browser.dispose(); | |
m_browser = null; | |
} | |
} | |
} | |
@Override | |
protected void show(int x, int y) { | |
resetCurrentMember(); | |
if (!StringUtility.isNullOrEmpty(m_javaDoc)) { | |
super.show(x, y); | |
} | |
} | |
private void computeJavadoc() { | |
if (!TypeUtility.exists(m_currentMember)) { | |
return; | |
} | |
Job j = new Job("calculating java doc of '" + m_currentMember.getElementName() + "'") { | |
@Override | |
protected IStatus run(IProgressMonitor monitor) { | |
Reader contentReader = null; | |
try { | |
if (TypeUtility.exists(m_currentMember)) { | |
contentReader = JavadocContentAccess.getHTMLContentReader(m_currentMember, true, true); | |
if (contentReader != null) { | |
m_javaDoc = getJavadocHtml(new IJavaElement[]{m_currentMember}); | |
if (getSourceControl() != null && !getSourceControl().isDisposed()) { | |
getSourceControl().getDisplay().asyncExec(new Runnable() { | |
@Override | |
public void run() { | |
if (m_browser != null && !m_browser.isDisposed()) { | |
m_browser.setText(m_javaDoc); | |
} | |
} | |
}); | |
} | |
} | |
} | |
} | |
catch (Exception e) { | |
ScoutSdkUi.logWarning(e); | |
} | |
finally { | |
if (contentReader != null) { | |
try { | |
contentReader.close(); | |
} | |
catch (Exception e) { | |
} | |
} | |
} | |
return Status.OK_STATUS; | |
} | |
}; | |
j.setPriority(Job.DECORATE); | |
j.setSystem(true); | |
j.setUser(false); | |
j.schedule(); | |
} | |
/** | |
* Returns the Javadoc in HTML format. | |
* | |
* @param result | |
* the Java elements for which to get the Javadoc | |
* @param activePart | |
* the active part if any | |
* @param selection | |
* the selection of the active site if any | |
* @param monitor | |
* a monitor to report progress to | |
* @return a string with the Javadoc in HTML format. | |
*/ | |
private String getJavadocHtml(IJavaElement[] result) { | |
StringBuffer buffer = new StringBuffer(); | |
int nResults = result.length; | |
if (nResults == 0) return null; | |
String base = null; | |
if (nResults > 1) { | |
for (int i = 0; i < result.length; i++) { | |
HTMLPrinter.startBulletList(buffer); | |
IJavaElement curr = result[i]; | |
if (curr instanceof IMember || curr.getElementType() == IJavaElement.LOCAL_VARIABLE) HTMLPrinter.addBullet(buffer, getInfoText(curr, null, false)); | |
HTMLPrinter.endBulletList(buffer); | |
} | |
} | |
else { | |
IJavaElement curr = result[0]; | |
if (curr instanceof IMember) { | |
final IMember member = (IMember) curr; | |
String constantValue = null; | |
HTMLPrinter.addSmallHeader(buffer, getInfoText(member, constantValue, true)); | |
Reader reader = null; | |
try { | |
String content = JavadocContentAccess2.getHTMLContent(member, true); | |
reader = content == null ? null : new StringReader(content); | |
// Provide hint why there's no Javadoc | |
if (reader == null && member.isBinary()) { | |
boolean hasAttachedJavadoc = JavaDocLocations.getJavadocBaseLocation(member) != null; | |
IPackageFragmentRoot root = (IPackageFragmentRoot) member.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); | |
boolean hasAttachedSource = root != null && root.getSourceAttachmentPath() != null; | |
IOpenable openable = member.getOpenable(); | |
boolean hasSource = openable.getBuffer() != null; | |
if (!hasAttachedSource && !hasAttachedJavadoc) reader = new StringReader(""); | |
else if (!hasAttachedJavadoc && !hasSource) reader = new StringReader(""); | |
else if (!hasAttachedSource) reader = new StringReader(""); | |
else if (!hasSource) reader = new StringReader(""); | |
} | |
else { | |
base = JavaDocLocations.getBaseURL(member); | |
} | |
} | |
catch (JavaModelException ex) { | |
JavaPlugin.log(ex.getStatus()); | |
} | |
if (reader != null) { | |
HTMLPrinter.addParagraph(buffer, reader); | |
} | |
} | |
else if (curr.getElementType() == IJavaElement.LOCAL_VARIABLE || curr.getElementType() == IJavaElement.TYPE_PARAMETER) { | |
HTMLPrinter.addSmallHeader(buffer, getInfoText(curr, null, true)); | |
} | |
} | |
boolean flushContent = true; | |
if (buffer.length() > 0 || flushContent) { | |
HTMLPrinter.insertPageProlog(buffer, 0, null, null, loadStyleSheet()); | |
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 buffer.toString(); | |
} | |
return null; | |
} | |
/** | |
* Gets the label for the given member. | |
* | |
* @param member | |
* the Java member | |
* @param constantValue | |
* the constant value if any | |
* @param allowImage | |
* true if the java element image should be shown | |
* @return a string containing the member's label | |
*/ | |
private String getInfoText(IJavaElement member, String constantValue, boolean allowImage) { | |
StringBuffer label = new StringBuffer(JavaElementLinks.getElementLabel(member, LABEL_FLAGS)); | |
if (member.getElementType() == IJavaElement.FIELD && constantValue != null) { | |
label.append(constantValue); | |
} | |
String imageName = null; | |
try { | |
if (allowImage) { | |
URL imageUrl = JavaPlugin.getDefault().getImagesOnFSRegistry().getImageURL(member); | |
if (imageUrl != null) { | |
imageName = imageUrl.toExternalForm(); | |
} | |
} | |
} | |
catch (Exception e) { | |
ScoutSdkUi.logWarning("could not load image for '" + member.getElementName() + "'."); | |
} | |
return JavadocHoverUtility.addImageAndLabel(member, imageName, label.toString()); | |
} | |
private static String loadStyleSheet() { | |
Bundle bundle = Platform.getBundle(JavaPlugin.getPluginId()); | |
URL styleSheetURL = bundle.getEntry("/JavadocViewStyleSheet.css"); //$NON-NLS-1$ | |
if (styleSheetURL == null) return null; | |
BufferedReader reader = null; | |
try { | |
reader = new BufferedReader(new InputStreamReader(styleSheetURL.openStream())); | |
StringBuffer buffer = new StringBuffer(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 null; | |
} | |
finally { | |
try { | |
if (reader != null) reader.close(); | |
} | |
catch (IOException e) { | |
} | |
} | |
} | |
} |