ext.org.eclipse.jdt.internal.ui.infoviews.JavadocView.java Source code

Java tutorial

Introduction

Here is the source code for ext.org.eclipse.jdt.internal.ui.infoviews.JavadocView.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2011 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> - [misc] Display values for constant fields in the Javadoc view - https://bugs.eclipse.org/bugs/show_bug.cgi?id=204914
 *     Brock Janiczak <brockj@tpg.com.au> - [implementation] Streams not being closed in Javadoc views - https://bugs.eclipse.org/bugs/show_bug.cgi?id=214854
 *     Benjamin Muskalla <bmuskalla@innoopract.com> - [javadoc view] NPE on enumerations - https://bugs.eclipse.org/bugs/show_bug.cgi?id=223586
 *******************************************************************************/
package ext.org.eclipse.jdt.internal.ui.infoviews;

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.osgi.framework.Bundle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.OpenWindowListener;
import org.eclipse.swt.browser.WindowEvent;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
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.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;

import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.handlers.IHandlerService;

import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;

import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.ui.IContextMenuConstants;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.jdt.ui.actions.JdtActionConstants;
import org.eclipse.jdt.ui.actions.OpenAttachedJavadocAction;
import org.eclipse.jdt.ui.text.IJavaPartitions;

import patch.org.eclipse.jdt.internal.ui.JavaPlugin;

import ext.org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations;
import ext.org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import ext.org.eclipse.jdt.internal.corext.util.JdtFlags;
import ext.org.eclipse.jdt.internal.corext.util.Messages;
import ext.org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import ext.org.eclipse.jdt.internal.ui.JavaPluginImages;
import ext.org.eclipse.jdt.internal.ui.actions.ActionMessages;
import ext.org.eclipse.jdt.internal.ui.actions.SimpleSelectionProvider;
import ext.org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import ext.org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import ext.org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover;
import ext.org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2;
import ext.org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import ext.org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks;

/**
 * View which shows Javadoc for a given Java element.
 *
 * FIXME: As of 3.0 selectAll() and getSelection() is not working
 *         see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
 *
 * @since 3.0
 */
public class JavadocView extends AbstractInfoView {

    /**
     * Implementation of a {@link BrowserInput} using
     * a {@link IJavaElement} as input.
     *
     * @since 3.4
     */
    private static final class JavaElementBrowserInput extends BrowserInput {

        private final IJavaElement fInput;

        public JavaElementBrowserInput(BrowserInput previous, IJavaElement inputElement) {
            super(previous);
            Assert.isNotNull(inputElement);
            fInput = inputElement;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jdt.internal.ui.infoviews.JavadocView.IBrowserInput#getInputElement()
         */
        @Override
        public Object getInputElement() {
            return fInput;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jdt.internal.ui.infoviews.JavadocView.IBrowserInput#getInputName()
         */
        @Override
        public String getInputName() {
            return fInput.getElementName();
        }
    }

    /**
     * Implementation of a {@link BrowserInput} using an
     * {@link URL} as input.
     *
     * @since 3.4
     */
    private static class URLBrowserInput extends BrowserInput {

        private final URL fURL;

        public URLBrowserInput(BrowserInput previous, URL url) {
            super(previous);
            Assert.isNotNull(url);
            fURL = url;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jdt.internal.ui.infoviews.JavadocView.IBrowserInput#getInputElement()
         */
        @Override
        public Object getInputElement() {
            return fURL;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jdt.internal.ui.infoviews.JavadocView.IBrowserInput#getInputName()
         */
        @Override
        public String getInputName() {
            return fURL.toExternalForm();
        }
    }

    /**
     * Action to go forward in the history.
     *
     * @since 3.4
     */
    private final class ForthAction extends Action {

        public ForthAction() {
            setText(InfoViewMessages.JavadocView_action_forward_name);
            ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
            setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
            setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));

            update();
        }

        public void update() {
            if (fCurrent != null && fCurrent.getNext() != null) {
                BrowserInput element = fCurrent.getNext();
                setToolTipText(Messages.format(InfoViewMessages.JavadocView_action_forward_enabledTooltip,
                        BasicElementLabels.getJavaElementName(element.getInputName())));
                setEnabled(true);
            } else {
                setToolTipText(InfoViewMessages.JavadocView_action_forward_disabledTooltip);
                setEnabled(false);
            }
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.action.Action#run()
         */
        @Override
        public void run() {
            setInput(fCurrent.getNext());
        }

    }

    /**
     * Action to go backwards in the history.
     *
     * @since 3.4
     */
    private final class BackAction extends Action {

        public BackAction() {
            setText(InfoViewMessages.JavadocView_action_back_name);
            ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
            setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
            setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));

            update();
        }

        private void update() {
            if (fCurrent != null && fCurrent.getPrevious() != null) {
                BrowserInput element = fCurrent.getPrevious();
                setToolTipText(Messages.format(InfoViewMessages.JavadocView_action_back_enabledTooltip,
                        BasicElementLabels.getJavaElementName(element.getInputName())));
                setEnabled(true);
            } else {
                setToolTipText(InfoViewMessages.JavadocView_action_back_disabledTooltip);
                setEnabled(false);
            }
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.action.Action#run()
         */
        @Override
        public void run() {
            setInput(fCurrent.getPrevious());
        }
    }

    /**
     * Action to toggle linking with selection.
     *
     * @since 3.4
     */
    private class LinkAction extends Action {

        public LinkAction() {
            super(InfoViewMessages.JavadocView_action_toogleLinking_text, SWT.TOGGLE);
            setToolTipText(InfoViewMessages.JavadocView_action_toggleLinking_toolTipText);
            JavaPluginImages.setLocalImageDescriptors(this, "synced.gif"); //$NON-NLS-1$
            setChecked(isLinkingEnabled());
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.action.Action#run()
         */
        @Override
        public void run() {
            setLinkingEnabled(!isLinkingEnabled());
        }
    }

    /**
     * Action to open the selection in an external browser. If the selection is a java element its
     * corresponding javadoc is shown if possible. If it is an URL the URL's content is shown.
     * 
     * The action is disabled if the selection can not be opened.
     * 
     * @since 3.6
     */
    private static class OpenInBrowserAction extends OpenAttachedJavadocAction {

        /**
         * Create a new ShowExternalJavadocAction
         * 
         * @param site the site
         */
        public OpenInBrowserAction(IWorkbenchSite site) {
            super(site);
        }

        /* (non-Javadoc)
         * Method declared on SelectionDispatchAction.
         */
        @Override
        public void selectionChanged(IStructuredSelection structuredSelection) {
            super.selectionChanged(structuredSelection);
            Object element = structuredSelection.getFirstElement();
            if (element instanceof URL) {
                setText(InfoViewMessages.OpenInBrowserAction_url_label);
                setToolTipText(InfoViewMessages.OpenInBrowserAction_url_toolTip);
            } else {
                setText(ActionMessages.OpenAttachedJavadocAction_label);
                setToolTipText(ActionMessages.OpenAttachedJavadocAction_tooltip);
            }
        }

        /* (non-Javadoc)
         * Method declared on SelectionDispatchAction.
         */
        @Override
        public void run(IStructuredSelection selection) {
            if (!canEnableFor(selection))
                return;

            Object element = selection.getFirstElement();
            if (element instanceof IJavaElement)
                super.run(selection);
            else
                open((URL) element);

        }

        /*
         * @see org.eclipse.jdt.ui.actions.OpenAttachedJavadocAction#canEnableFor(org.eclipse.jface.viewers.IStructuredSelection)
         */
        @Override
        protected boolean canEnableFor(IStructuredSelection selection) {
            if (selection.size() != 1)
                return false;

            Object element = selection.getFirstElement();
            return element instanceof URL || super.canEnableFor(selection);
        }

    }

    /**
     * Preference key for the preference whether to show a dialog
     * when the SWT Browser widget is not available.
     * @since 3.0
     */
    private static final String DO_NOT_WARN_PREFERENCE_KEY = "JavadocView.error.doNotWarn"; //$NON-NLS-1$

    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73558
    private static final boolean WARNING_DIALOG_ENABLED = false;

    /** 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;

    /** The HTML widget. */
    private Browser fBrowser;
    /** The text widget. */
    private StyledText fText;
    /** The information presenter. */
    private HTMLTextPresenter fPresenter;
    /** The text presentation. */
    private final TextPresentation fPresentation = new TextPresentation();
    /** The select all action */
    private SelectAllAction fSelectAllAction;
    /** The style sheet (css) */
    private static String fgStyleSheet;
    /**
     * <code>true</code> once the style sheet has been loaded.
     * @since 3.3
     */
    private static boolean fgStyleSheetLoaded = false;

    /** The Browser widget */
    private boolean fIsUsingBrowserWidget;

    private RGB fBackgroundColorRGB;
    /**
     * The font listener.
     * @since 3.3
     */
    private IPropertyChangeListener fFontListener;

    /**
     * Holds original Javadoc input string.
     * @since 3.4
     */
    private String fOriginalInput;

    /**
     * The current input element if any
     * @since 3.4
     */
    private BrowserInput fCurrent;

    /**
     * Action to go back in the link history.
     * @since 3.4
     */
    private BackAction fBackAction;

    /**
     * Action to go forth in the link history.
     * @since 3.4
     */
    private ForthAction fForthAction;

    /**
     * Action to enable and disable link with selection.
     * @since 3.4
     */
    private LinkAction fToggleLinkAction;

    /**
     * Action to open the attached Javadoc.
     * 
     * @since 3.4
     */
    private OpenInBrowserAction fOpenBrowserAction;

    /**
     * A selection provider providing the current
     * Java element input of this view as selection.
     * @since 3.4
     */
    private ISelectionProvider fInputSelectionProvider;

    /**
     * The Javadoc view's select all action.
     */
    private class SelectAllAction extends Action {

        /** The control. */
        private final Control fControl;
        /** The selection provider. */
        private final SelectionProvider fSelectionProvider;

        /**
         * Creates the action.
         *
         * @param control the widget
         * @param selectionProvider the selection provider
         */
        public SelectAllAction(Control control, SelectionProvider selectionProvider) {
            super("selectAll"); //$NON-NLS-1$

            Assert.isNotNull(control);
            Assert.isNotNull(selectionProvider);
            fControl = control;
            fSelectionProvider = selectionProvider;

            // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
            setEnabled(!fIsUsingBrowserWidget);

            setText(InfoViewMessages.SelectAllAction_label);
            setToolTipText(InfoViewMessages.SelectAllAction_tooltip);
            setDescription(InfoViewMessages.SelectAllAction_description);

            PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                    IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
        }

        /**
         * Selects all in the view.
         */
        @Override
        public void run() {
            if (fControl instanceof StyledText)
                ((StyledText) fControl).selectAll();
            else {
                // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
                //            ((Browser)fControl).selectAll();
                if (fSelectionProvider != null)
                    fSelectionProvider.fireSelectionChanged();
            }
        }
    }

    /**
     * The Javadoc view's selection provider.
     */
    private static class SelectionProvider implements ISelectionProvider {

        /** The selection changed listeners. */
        private final ListenerList fListeners = new ListenerList(ListenerList.IDENTITY);
        /** The widget. */
        private final Control fControl;

        /**
         * Creates a new selection provider.
         *
         * @param control   the widget
         */
        public SelectionProvider(Control control) {
            Assert.isNotNull(control);
            fControl = control;
            if (fControl instanceof StyledText) {
                ((StyledText) fControl).addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(SelectionEvent e) {
                        fireSelectionChanged();
                    }
                });
            } else {
                // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
                //            ((Browser)fControl).addSelectionListener(new SelectionAdapter() {
                //               public void widgetSelected(SelectionEvent e) {
                //                  fireSelectionChanged();
                //               }
                //            });
            }
        }

        /**
         * Sends a selection changed event to all listeners.
         */
        public void fireSelectionChanged() {
            ISelection selection = getSelection();
            SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
            Object[] selectionChangedListeners = fListeners.getListeners();
            for (int i = 0; i < selectionChangedListeners.length; i++)
                ((ISelectionChangedListener) selectionChangedListeners[i]).selectionChanged(event);
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
         */
        public void addSelectionChangedListener(ISelectionChangedListener listener) {
            fListeners.add(listener);
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
         */
        public ISelection getSelection() {
            if (fControl instanceof StyledText) {
                IDocument document = new Document(((StyledText) fControl).getSelectionText());
                return new TextSelection(document, 0, document.getLength());
            } else {
                // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
                return StructuredSelection.EMPTY;
            }
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
         */
        public void removeSelectionChangedListener(ISelectionChangedListener listener) {
            fListeners.remove(listener);
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
         */
        public void setSelection(ISelection selection) {
            // not supported
        }
    }

    /*
     * @see AbstractInfoView#internalCreatePartControl(Composite)
     */
    @Override
    protected void internalCreatePartControl(Composite parent) {
        try {
            fBrowser = new Browser(parent, SWT.NONE);
            fBrowser.setJavascriptEnabled(false);
            fIsUsingBrowserWidget = true;
            addLinkListener(fBrowser);
            fBrowser.addOpenWindowListener(new OpenWindowListener() {
                public void open(WindowEvent event) {
                    event.required = true; // Cancel opening of new windows
                }
            });

        } catch (SWTError er) {

            /* The Browser widget throws an SWTError if it fails to
             * instantiate properly. Application code should catch
             * this SWTError and disable any feature requiring the
             * Browser widget.
             * Platform requirements for the SWT Browser widget are available
             * from the SWT FAQ web site.
             */

            IPreferenceStore store = JavaPlugin.getDefault().getPreferenceStore();
            boolean doNotWarn = store.getBoolean(DO_NOT_WARN_PREFERENCE_KEY);
            if (WARNING_DIALOG_ENABLED) {
                if (!doNotWarn) {
                    String title = InfoViewMessages.JavadocView_error_noBrowser_title;
                    String message = InfoViewMessages.JavadocView_error_noBrowser_message;
                    String toggleMessage = InfoViewMessages.JavadocView_error_noBrowser_doNotWarn;
                    MessageDialogWithToggle dialog = MessageDialogWithToggle.openError(parent.getShell(), title,
                            message, toggleMessage, false, null, null);
                    if (dialog.getReturnCode() == Window.OK)
                        store.setValue(DO_NOT_WARN_PREFERENCE_KEY, dialog.getToggleState());
                }
            }

            fIsUsingBrowserWidget = false;
        }

        if (!fIsUsingBrowserWidget) {
            fText = new StyledText(parent, SWT.V_SCROLL | SWT.H_SCROLL);
            fText.setEditable(false);
            fPresenter = new HTMLTextPresenter(false);

            fText.addControlListener(new ControlAdapter() {
                /*
                 * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
                 */
                @Override
                public void controlResized(ControlEvent e) {
                    doSetInput(fOriginalInput);
                }
            });
        }

        initStyleSheet();
        listenForFontChanges();
        getViewSite().setSelectionProvider(new SelectionProvider(getControl()));
    }

    /**
     * Registers a listener for the Java editor font.
     *
     * @since 3.3
     */
    private void listenForFontChanges() {
        fFontListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                if (PreferenceConstants.APPEARANCE_JAVADOC_FONT.equals(event.getProperty())) {
                    fgStyleSheetLoaded = false;
                    // trigger reloading, but make sure other listeners have already run, so that
                    // the style sheet gets reloaded only once.
                    final Display display = getSite().getPage().getWorkbenchWindow().getWorkbench().getDisplay();
                    if (!display.isDisposed()) {
                        display.asyncExec(new Runnable() {
                            public void run() {
                                if (!display.isDisposed()) {
                                    initStyleSheet();
                                    refresh();
                                }
                            }
                        });
                    }
                }
            }
        };
        JFaceResources.getFontRegistry().addListener(fFontListener);
    }

    private static void initStyleSheet() {
        if (fgStyleSheetLoaded)
            return;
        fgStyleSheetLoaded = true;
        fgStyleSheet = loadStyleSheet();
    }

    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) {
            }
        }
    }

    /*
     * @see AbstractInfoView#createActions()
     */
    @Override
    protected void createActions() {
        super.createActions();
        fSelectAllAction = new SelectAllAction(getControl(), (SelectionProvider) getSelectionProvider());

        fBackAction = new BackAction();
        fBackAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_BACK);
        fForthAction = new ForthAction();
        fForthAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_FORWARD);

        fToggleLinkAction = new LinkAction();
        fToggleLinkAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_TOGGLE_LINK_WITH_EDITOR);

        fInputSelectionProvider = new SimpleSelectionProvider();
        fOpenBrowserAction = new OpenInBrowserAction(getSite());
        fOpenBrowserAction.setSpecialSelectionProvider(fInputSelectionProvider);
        fOpenBrowserAction.setImageDescriptor(JavaPluginImages.DESC_ELCL_OPEN_BROWSER);
        fOpenBrowserAction.setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_OPEN_BROWSER);
        fOpenBrowserAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_ATTACHED_JAVADOC);
        fInputSelectionProvider.addSelectionChangedListener(fOpenBrowserAction);

        IJavaElement input = getInput();
        StructuredSelection selection;
        if (input != null) {
            selection = new StructuredSelection(input);
        } else {
            selection = new StructuredSelection();
        }
        fInputSelectionProvider.setSelection(selection);
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#fillActionBars(org.eclipse.ui.IActionBars)
     * @since 3.4
     */
    @Override
    protected void fillActionBars(final IActionBars actionBars) {
        super.fillActionBars(actionBars);

        actionBars.setGlobalActionHandler(ActionFactory.BACK.getId(), fBackAction);
        actionBars.setGlobalActionHandler(ActionFactory.FORWARD.getId(), fForthAction);

        fInputSelectionProvider.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                actionBars.setGlobalActionHandler(JdtActionConstants.OPEN_ATTACHED_JAVA_DOC, fOpenBrowserAction);
            }
        });

        IHandlerService handlerService = (IHandlerService) getSite().getService(IHandlerService.class);
        handlerService.activateHandler(IWorkbenchCommandConstants.NAVIGATE_TOGGLE_LINK_WITH_EDITOR,
                new ActionHandler(fToggleLinkAction));
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#fillToolBar(org.eclipse.jface.action.IToolBarManager)
     * @since 3.4
     */
    @Override
    protected void fillToolBar(IToolBarManager tbm) {
        tbm.add(fBackAction);
        tbm.add(fForthAction);
        tbm.add(new Separator());

        tbm.add(fToggleLinkAction);
        super.fillToolBar(tbm);
        tbm.add(fOpenBrowserAction);
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
     * @since 3.4
     */
    @Override
    public void menuAboutToShow(IMenuManager menu) {
        super.menuAboutToShow(menu);

        menu.appendToGroup(IContextMenuConstants.GROUP_GOTO, fBackAction);
        menu.appendToGroup(IContextMenuConstants.GROUP_GOTO, fForthAction);

        menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, fOpenBrowserAction);
    }

    /*
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getSelectAllAction()
     * @since 3.0
     */
    @Override
    protected IAction getSelectAllAction() {
        // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
        if (fIsUsingBrowserWidget)
            return null;

        return fSelectAllAction;
    }

    /*
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getCopyToClipboardAction()
     * @since 3.0
     */
    @Override
    protected IAction getCopyToClipboardAction() {
        // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
        if (fIsUsingBrowserWidget)
            return null;

        return super.getCopyToClipboardAction();
    }

    /*
      * @see AbstractInfoView#setForeground(Color)
      */
    @Override
    protected void setForeground(Color color) {
        getControl().setForeground(color);
    }

    /*
     * @see AbstractInfoView#setBackground(Color)
     */
    @Override
    protected void setBackground(Color color) {
        getControl().setBackground(color);
        fBackgroundColorRGB = color.getRGB();
        refresh();
    }

    /**
     * Refreshes the view.
     *
     * @since 3.3
     */
    private void refresh() {
        IJavaElement input = getInput();
        if (input == null) {
            StringBuffer buffer = new StringBuffer(""); //$NON-NLS-1$
            HTMLPrinter.insertPageProlog(buffer, 0, null, fBackgroundColorRGB, fgStyleSheet);
            doSetInput(buffer.toString());
        } else {
            doSetInput(computeInput(input));
        }
    }

    /*
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getBackgroundColorKey()
     * @since 3.2
     */
    @Override
    protected String getBackgroundColorKey() {
        return "org.eclipse.jdt.ui.JavadocView.backgroundColor"; //$NON-NLS-1$
    }

    /*
     * @see AbstractInfoView#internalDispose()
     */
    @Override
    protected void internalDispose() {
        fText = null;
        fBrowser = null;
        if (fFontListener != null) {
            JFaceResources.getFontRegistry().removeListener(fFontListener);
            fFontListener = null;
        }

        if (fOpenBrowserAction != null) {
            fInputSelectionProvider.removeSelectionChangedListener(fOpenBrowserAction);
            fOpenBrowserAction = null;
        }
    }

    /*
     * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
     */
    @Override
    public void setFocus() {
        getControl().setFocus();
    }

    /*
     * @see AbstractInfoView#computeInput(Object)
     */
    @Override
    protected Object computeInput(Object input) {
        //TODO: never used?
        if (getControl() == null || !(input instanceof IJavaElement))
            return null;

        IWorkbenchPart part = null;
        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (window != null) {
            IWorkbenchPage page = window.getActivePage();
            if (page != null) {
                part = page.getActivePart();
            }
        }

        ISelection selection = null;
        if (part != null) {
            IWorkbenchPartSite site = part.getSite();
            if (site != null) {
                ISelectionProvider provider = site.getSelectionProvider();
                if (provider != null) {
                    selection = provider.getSelection();
                }
            }
        }

        return computeInput(part, selection, (IJavaElement) input, new NullProgressMonitor());
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#computeInput(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection, org.eclipse.jdt.core.IJavaElement, org.eclipse.core.runtime.IProgressMonitor)
     * @since 3.4
     */
    @Override
    protected Object computeInput(IWorkbenchPart part, ISelection selection, IJavaElement input,
            IProgressMonitor monitor) {
        if (getControl() == null || input == null)
            return null;

        String javadocHtml;

        switch (input.getElementType()) {
        case IJavaElement.COMPILATION_UNIT:
            try {
                javadocHtml = getJavadocHtml(((ICompilationUnit) input).getTypes(), part, selection, monitor);
            } catch (JavaModelException ex) {
                javadocHtml = null;
            }
            break;
        case IJavaElement.CLASS_FILE:
            javadocHtml = getJavadocHtml(new IJavaElement[] { ((IClassFile) input).getType() }, part, selection,
                    monitor);
            break;
        default:
            javadocHtml = getJavadocHtml(new IJavaElement[] { input }, part, selection, monitor);
        }

        if (javadocHtml == null)
            return ""; //$NON-NLS-1$

        return javadocHtml;
    }

    /*
     * @see AbstractInfoView#computeDescription(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection, org.eclipse.jdt.core.IJavaElement, org.eclipse.core.runtime.IProgressMonitor)
     * @since 3.4
     */
    @Override
    protected String computeDescription(IWorkbenchPart part, ISelection selection, IJavaElement inputElement,
            IProgressMonitor monitor) {
        return ""; //$NON-NLS-1$
    }

    /**
     * Set input to the given input.
     *
     * @param input the input for the view
     * @since 3.4
     */
    public void setInput(BrowserInput input) {
        fCurrent = input;

        Object inputElement = input.getInputElement();
        if (inputElement instanceof IJavaElement) {
            setInput((IJavaElement) inputElement);
        } else if (inputElement instanceof URL) {
            fBrowser.setUrl(((URL) inputElement).toExternalForm());

            if (fInputSelectionProvider != null)
                fInputSelectionProvider.setSelection(new StructuredSelection(inputElement));
        }

        fForthAction.update();
        fBackAction.update();
    }

    /**
     * {@inheritDoc}
     * 
     * @param input a String containing the HTML to be showin in the view
     */
    @Override
    protected void doSetInput(Object input) {
        String javadocHtml = (String) input;
        fOriginalInput = javadocHtml;

        if (fInputSelectionProvider != null) {
            IJavaElement inputElement = getInput();
            StructuredSelection selection = inputElement == null ? StructuredSelection.EMPTY
                    : new StructuredSelection(inputElement);
            fInputSelectionProvider.setSelection(selection);
        }

        if (fIsUsingBrowserWidget) {
            if (javadocHtml != null && javadocHtml.length() > 0) {
                boolean RTL = (getSite().getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
                if (RTL) {
                    StringBuffer buffer = new StringBuffer(javadocHtml);
                    HTMLPrinter.insertStyles(buffer, new String[] { "direction:rtl" }); //$NON-NLS-1$
                    javadocHtml = buffer.toString();
                }
            }
            fBrowser.setText(javadocHtml);
        } else {
            fPresentation.clear();
            Rectangle size = fText.getClientArea();

            try {
                javadocHtml = fPresenter.updatePresentation(fText, javadocHtml, fPresentation, size.width,
                        size.height);
            } catch (IllegalArgumentException ex) {
                // the javadoc might no longer be valid
                return;
            }
            fText.setText(javadocHtml);
            TextPresentation.applyTextPresentation(fPresentation, fText);
        }
    }

    /**
     * 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, IWorkbenchPart activePart, ISelection selection,
            IProgressMonitor monitor) {
        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;
                if (member instanceof IField) {
                    constantValue = computeFieldConstant(activePart, selection, (IField) member, monitor);
                    if (constantValue != null)
                        constantValue = HTMLPrinter.convertToHTMLContentWithWhitespace(constantValue);
                }

                HTMLPrinter.addSmallHeader(buffer, getInfoText(member, constantValue, true));

                try {
                    ISourceRange nameRange = ((IMember) curr).getNameRange();
                    if (SourceRange.isAvailable(nameRange)) {
                        ITypeRoot typeRoot = ((IMember) curr).getTypeRoot();
                        Region hoverRegion = new Region(nameRange.getOffset(), nameRange.getLength());
                        buffer.append("<br>"); //$NON-NLS-1$
                        JavadocHover.addAnnotations(buffer, curr, typeRoot, hoverRegion);
                    }
                } catch (JavaModelException e) {
                    // no annotations this time...
                }

                Reader reader;
                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(InfoViewMessages.JavadocView_noAttachments);
                        else if (!hasAttachedJavadoc && !hasSource)
                            reader = new StringReader(InfoViewMessages.JavadocView_noAttachedJavadoc);
                        else if (!hasAttachedSource)
                            reader = new StringReader(InfoViewMessages.JavadocView_noAttachedSource);
                        else if (!hasSource)
                            reader = new StringReader(InfoViewMessages.JavadocView_noInformation);

                    } else {
                        base = JavaDocLocations.getBaseURL(member);
                    }

                } catch (JavaModelException ex) {
                    reader = new StringReader(InfoViewMessages.JavadocView_error_gettingJavadoc);
                    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));
                if (curr instanceof ILocalVariable) {
                    ISourceRange nameRange = ((ILocalVariable) curr).getNameRange();
                    ITypeRoot typeRoot = ((ILocalVariable) curr).getTypeRoot();
                    Region hoverRegion = new Region(nameRange.getOffset(), nameRange.getLength());
                    buffer.append("<br>"); //$NON-NLS-1$
                    JavadocHover.addAnnotations(buffer, curr, typeRoot, hoverRegion);
                }
            }
        }

        boolean flushContent = true;
        if (buffer.length() > 0 || flushContent) {
            HTMLPrinter.insertPageProlog(buffer, 0, null, fBackgroundColorRGB, fgStyleSheet);
            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;
        if (allowImage) {
            URL imageUrl = JavaPlugin.getDefault().getImagesOnFSRegistry().getImageURL(member);
            if (imageUrl != null) {
                imageName = imageUrl.toExternalForm();
            }
        }

        StringBuffer buf = new StringBuffer();
        JavadocHover.addImageAndLabel(buf, member, imageName, 16, 16, label.toString(), 20, 2);
        return buf.toString();
    }

    /*
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#isIgnoringNewInput(org.eclipse.jdt.core.IJavaElement, org.eclipse.jface.viewers.ISelection)
     * @since 3.2
     */
    @Override
    protected boolean isIgnoringNewInput(IJavaElement je, IWorkbenchPart part, ISelection selection) {
        if (fCurrent != null && fCurrent.getInputElement() instanceof URL)
            return false;

        if (super.isIgnoringNewInput(je, part, selection) && part instanceof ITextEditor
                && selection instanceof ITextSelection) {

            ITextEditor editor = (ITextEditor) part;
            IDocumentProvider docProvider = editor.getDocumentProvider();
            if (docProvider == null)
                return false;

            IDocument document = docProvider.getDocument(editor.getEditorInput());
            if (!(document instanceof IDocumentExtension3))
                return false;

            try {
                int offset = ((ITextSelection) selection).getOffset();
                String partition = ((IDocumentExtension3) document)
                        .getContentType(IJavaPartitions.JAVA_PARTITIONING, offset, false);
                return partition != IJavaPartitions.JAVA_DOC;
            } catch (BadPartitioningException ex) {
                return false;
            } catch (BadLocationException ex) {
                return false;
            }

        }
        return false;
    }

    /*
     * @see AbstractInfoView#findSelectedJavaElement(IWorkbenchPart)
     */
    @Override
    protected IJavaElement findSelectedJavaElement(IWorkbenchPart part, ISelection selection) {
        IJavaElement element;
        try {
            element = super.findSelectedJavaElement(part, selection);

            if (element == null && part instanceof JavaEditor && selection instanceof ITextSelection) {

                JavaEditor editor = (JavaEditor) part;
                ITextSelection textSelection = (ITextSelection) selection;

                IDocumentProvider documentProvider = editor.getDocumentProvider();
                if (documentProvider == null)
                    return null;

                IDocument document = documentProvider.getDocument(editor.getEditorInput());
                if (document == null)
                    return null;

                ITypedRegion typedRegion = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING,
                        textSelection.getOffset(), false);
                if (IJavaPartitions.JAVA_DOC.equals(typedRegion.getType()))
                    return TextSelectionConverter.getElementAtOffset((JavaEditor) part, textSelection);
                else
                    return null;
            } else
                return element;
        } catch (JavaModelException e) {
            return null;
        } catch (BadLocationException e) {
            return null;
        }
    }

    /*
     * @see AbstractInfoView#getControl()
     */
    @Override
    protected Control getControl() {
        if (fIsUsingBrowserWidget)
            return fBrowser;
        else
            return fText;
    }

    /*
     * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getHelpContextId()
     * @since 3.1
     */
    @Override
    protected String getHelpContextId() {
        return IJavaHelpContextIds.JAVADOC_VIEW;
    }

    /**
     * Compute the textual representation of a 'static' 'final' field's constant initializer value.
     *
     * @param activePart the part that triggered the computation, or <code>null</code>
     * @param selection the selection that references the field, or <code>null</code>
     * @param resolvedField the filed whose constant value will be computed
     * @param monitor the progress monitor
     *
     * @return the textual representation of the constant, or <code>null</code> if the
     *   field is not a constant field, the initializer value could not be computed, or
     *   the progress monitor was cancelled
     * @since 3.4
     */
    private String computeFieldConstant(IWorkbenchPart activePart, ISelection selection, IField resolvedField,
            IProgressMonitor monitor) {

        if (!isStaticFinal(resolvedField))
            return null;

        Object constantValue;
        IJavaProject preferenceProject;

        if (selection instanceof ITextSelection && activePart instanceof JavaEditor) {
            IEditorPart editor = (IEditorPart) activePart;
            ITypeRoot activeType = JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
            preferenceProject = activeType.getJavaProject();
            constantValue = getConstantValueFromActiveEditor(activeType, resolvedField, (ITextSelection) selection,
                    monitor);
            if (constantValue == null) // fall back - e.g. when selection is inside Javadoc of the element
                constantValue = computeFieldConstantFromTypeAST(resolvedField, monitor);
        } else {
            constantValue = computeFieldConstantFromTypeAST(resolvedField, monitor);
            preferenceProject = resolvedField.getJavaProject();
        }

        if (constantValue != null)
            return getFormattedAssignmentOperator(preferenceProject) + formatCompilerConstantValue(constantValue);

        return null;
    }

    /**
     * Retrieve a constant initializer value of a field by (AST) parsing field's type.
     *
     * @param constantField the constant field
     * @param monitor the progress monitor
     * @return the constant value of the field, or <code>null</code> if it could not be computed
     *   (or if the progress was cancelled).
     * @since 3.4
     */
    private Object computeFieldConstantFromTypeAST(IField constantField, IProgressMonitor monitor) {
        if (monitor.isCanceled())
            return null;

        CompilationUnit ast = SharedASTProvider.getAST(constantField.getTypeRoot(), SharedASTProvider.WAIT_NO,
                monitor);
        if (ast != null) {
            try {
                if (constantField.isEnumConstant())
                    return null;

                VariableDeclarationFragment fieldDecl = ASTNodeSearchUtil
                        .getFieldDeclarationFragmentNode(constantField, ast);
                if (fieldDecl == null)
                    return null;
                Expression initializer = fieldDecl.getInitializer();
                if (initializer == null)
                    return null;
                return initializer.resolveConstantExpressionValue();
            } catch (JavaModelException e) {
                // ignore the exception and try the next method
            }
        }

        if (monitor.isCanceled())
            return null;

        ASTParser p = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
        p.setProject(constantField.getJavaProject());
        IBinding[] createBindings;
        try {
            createBindings = p.createBindings(new IJavaElement[] { constantField }, monitor);
        } catch (OperationCanceledException e) {
            return null;
        }

        IVariableBinding variableBinding = (IVariableBinding) createBindings[0];
        if (variableBinding != null)
            return variableBinding.getConstantValue();

        return null;
    }

    /**
     * Tells whether the given member is static final.
     * <p>
     * XXX: Copied from {@link JavadocHover}.
     * </p>
     * @param member the member to test
     * @return <code>true</code> if static final
     * @since 3.4
     */
    private static boolean isStaticFinal(IJavaElement member) {
        if (member.getElementType() != IJavaElement.FIELD)
            return false;

        IField field = (IField) member;
        try {
            return JdtFlags.isFinal(field) && JdtFlags.isStatic(field);
        } catch (JavaModelException e) {
            JavaPlugin.log(e);
            return false;
        }
    }

    /**
     * Returns the constant value for a field that is referenced by the currently active type.
     * This method does may not run in the main UI thread.
     * <p>
     * XXX: This method was part of the JavadocHover#getConstantValue(IField field, IRegion hoverRegion)
     *       method (lines 299-314).
     * </p>
     * @param activeType the type that is currently active
     * @param field the field that is being referenced (usually not declared in <code>activeType</code>)
     * @param selection the region in <code>activeType</code> that contains the field reference
     * @param monitor a progress monitor
     *
     * @return the constant value for the given field or <code>null</code> if none
     * @since 3.4
     */
    private static Object getConstantValueFromActiveEditor(ITypeRoot activeType, IField field,
            ITextSelection selection, IProgressMonitor monitor) {
        Object constantValue = null;

        CompilationUnit unit = SharedASTProvider.getAST(activeType, SharedASTProvider.WAIT_ACTIVE_ONLY, monitor);
        if (unit == null)
            return null;

        ASTNode node = NodeFinder.perform(unit, selection.getOffset(), selection.getLength());
        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())) {
                    constantValue = variableBinding.getConstantValue();
                }
            }
        }
        return constantValue;
    }

    /**
     * Returns the string representation of the given constant value.
     * <p>
     * XXX: In {@link JavadocHover} this method was part of JavadocHover#getConstantValue lines 318-361.
     * </p>
     * @param constantValue the constant value
     * @return the string representation of the given constant value.
     * @since 3.4
     */
    private static String formatCompilerConstantValue(Object constantValue) {
        if (constantValue instanceof String) {
            StringBuffer result = new StringBuffer();
            result.append('"');
            String stringConstant = (String) constantValue;
            if (stringConstant.length() > 80) {
                result.append(stringConstant.substring(0, 80));
                result.append(JavaElementLabels.ELLIPSIS_STRING);
            } else {
                result.append(stringConstant);
            }
            result.append('"');
            return result.toString();

        } else if (constantValue instanceof Character) {
            String constantResult = '\'' + constantValue.toString() + '\'';

            char charValue = ((Character) constantValue).charValue();
            String hexString = Integer.toHexString(charValue);
            StringBuffer hexResult = new StringBuffer("\\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();
        }
    }

    /**
     * Creates and returns a formatted message for the given
     * constant with its hex value.
     * <p>
     * XXX: Copied from {@link JavadocHover}.
     * </p>
     *
     * @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(InfoViewMessages.JavadocView_constantValue_hexValue,
                new String[] { constantValue.toString(), hexValue });
    }

    /**
     * Returns the assignment operator string with the project's formatting applied to it.
     * <p>
     * XXX: This method was extracted from JavadocHover#getInfoText method.
     * </p>
     * @param javaProject the Java project whose formatting options will be used.
     * @return the formatted assignment operator string.
     * @since 3.4
     */
    private static String getFormattedAssignmentOperator(IJavaProject javaProject) {
        StringBuffer buffer = new StringBuffer();
        if (JavaCore.INSERT.equals(javaProject
                .getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR, true)))
            buffer.append(' ');
        buffer.append('=');
        if (JavaCore.INSERT.equals(javaProject
                .getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR, true)))
            buffer.append(' ');
        return buffer.toString();
    }

    /**
     * see also org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.addLinkListener(BrowserInformationControl)
     *
     * Add link listener to the given browser
     * @param browser the browser to add a listener to
     * @since 3.4
     */
    private void addLinkListener(Browser browser) {
        browser.addLocationListener(JavaElementLinks.createLocationListener(new JavaElementLinks.ILinkHandler() {

            /* (non-Javadoc)
             * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleDeclarationLink(org.eclipse.jdt.core.IJavaElement)
             */
            public void handleDeclarationLink(IJavaElement target) {
                try {
                    JavaUI.openInEditor(target);
                } catch (PartInitException e) {
                    JavaPlugin.log(e);
                } catch (JavaModelException e) {
                    JavaPlugin.log(e);
                }
            }

            /* (non-Javadoc)
             * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleExternalLink(java.net.URL, org.eclipse.swt.widgets.Display)
             */
            public boolean handleExternalLink(final URL url, Display display) {
                if (fCurrent == null || (fCurrent.getInputElement() instanceof URL
                        && !url.toExternalForm().equals(((URL) fCurrent.getInputElement()).toExternalForm()))) {
                    fCurrent = new URLBrowserInput(fCurrent, url);

                    if (fBackAction != null) {
                        fBackAction.update();
                        fForthAction.update();
                    }

                    if (fInputSelectionProvider != null)
                        fInputSelectionProvider.setSelection(new StructuredSelection(url));
                }

                return false;
            }

            /* (non-Javadoc)
             * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleInlineJavadocLink(org.eclipse.jdt.core.IJavaElement)
             */
            public void handleInlineJavadocLink(IJavaElement target) {
                JavaElementBrowserInput newInput = new JavaElementBrowserInput(fCurrent, target);
                JavadocView.this.setInput(newInput);
            }

            /* (non-Javadoc)
             * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleJavadocViewLink(org.eclipse.jdt.core.IJavaElement)
             */
            public void handleJavadocViewLink(IJavaElement target) {
                handleInlineJavadocLink(target);
            }

            /* (non-Javadoc)
             * @see org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.ILinkHandler#handleTextSet()
             */
            public void handleTextSet() {
                IJavaElement input = getInput();
                if (input == null)
                    return;

                if (fCurrent == null || !fCurrent.getInputElement().equals(input)) {
                    fCurrent = new JavaElementBrowserInput(null, input);

                    if (fBackAction != null) {
                        fBackAction.update();
                        fForthAction.update();
                    }
                }
            }
        }));
    }

}