test0501.JavaEditor.java Source code

Java tutorial

Introduction

Here is the source code for test0501.JavaEditor.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package test0501;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.CollationElementIterator;
import java.text.Collator;
import java.text.RuleBasedCollator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BidiSegmentEvent;
import org.eclipse.swt.custom.BidiSegmentListener;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.IPostSelectionProvider;
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.text.BadLocationException;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension3;
import org.eclipse.jface.text.ITextViewerExtension4;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.IInformationProviderExtension2;
import org.eclipse.jface.text.information.InformationPresenter;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.LineChangeHover;
import org.eclipse.jface.text.source.SourceViewerConfiguration;

import org.eclipse.ui.editors.text.DefaultEncodingSupport;
import org.eclipse.ui.editors.text.IEncodingSupport;

import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.help.WorkbenchHelp;
import org.eclipse.ui.part.EditorActionBarContributor;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.texteditor.AddTaskAction;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultAnnotation;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.ExtendedTextEditor;
import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IEditorStatusLine;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.ResourceAction;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.TextEditorAction;
import org.eclipse.ui.texteditor.TextNavigationAction;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import org.eclipse.search.ui.SearchUI;

import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICodeAssist;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportContainer;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;

import org.eclipse.jdt.ui.IContextMenuConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.jdt.ui.actions.JavaSearchActionGroup;
import org.eclipse.jdt.ui.actions.OpenEditorActionGroup;
import org.eclipse.jdt.ui.actions.OpenViewActionGroup;
import org.eclipse.jdt.ui.actions.ShowActionGroup;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jdt.ui.text.JavaTextTools;

import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.GoToNextPreviousMemberAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.SelectionHistory;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectEnclosingAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectHistoryAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectNextAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectPreviousAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectionAction;
import org.eclipse.jdt.internal.ui.search.FindOccurrencesEngine;
import org.eclipse.jdt.internal.ui.text.HTMLTextPresenter;
import org.eclipse.jdt.internal.ui.text.IJavaPartitions;
import org.eclipse.jdt.internal.ui.text.JavaChangeHover;
import org.eclipse.jdt.internal.ui.text.JavaPairMatcher;
import org.eclipse.jdt.internal.ui.util.JavaUIHelp;
import org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider;

/**
 * Java specific text editor.
 */
public abstract class JavaEditor extends ExtendedTextEditor implements IViewPartInputProvider {

    /**
     * Internal implementation class for a change listener.
     * @since 3.0
     */
    protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener {

        /**
         * Installs this selection changed listener with the given selection provider. If
         * the selection provider is a post selection provider, post selection changed
         * events are the preferred choice, otherwise normal selection changed events
         * are requested.
         * 
         * @param selectionProvider
         */
        public void install(ISelectionProvider selectionProvider) {
            if (selectionProvider == null)
                return;

            if (selectionProvider instanceof IPostSelectionProvider) {
                IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
                provider.addPostSelectionChangedListener(this);
            } else {
                selectionProvider.addSelectionChangedListener(this);
            }
        }

        /**
         * Removes this selection changed listener from the given selection provider.
         * 
         * @param selectionProvider
         */
        public void uninstall(ISelectionProvider selectionProvider) {
            if (selectionProvider == null)
                return;

            if (selectionProvider instanceof IPostSelectionProvider) {
                IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
                provider.removePostSelectionChangedListener(this);
            } else {
                selectionProvider.removeSelectionChangedListener(this);
            }
        }
    }

    /**
     * Updates the Java outline page selection and this editor's range indicator.
     * 
     * @since 3.0
     */
    private class EditorSelectionChangedListener extends AbstractSelectionChangedListener {

        /*
         * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
         */
        public void selectionChanged(SelectionChangedEvent event) {
            selectionChanged();
        }

        public void selectionChanged() {
            ISourceReference element = computeHighlightRangeSourceReference();
            synchronizeOutlinePage(element);
            setSelection(element, false);
            updateStatusLine();
            updateOccurrences();
        }
    }

    /**
     * Updates the selection in the editor's widget with the selection of the outline page. 
     */
    class OutlineSelectionChangedListener extends AbstractSelectionChangedListener {
        public void selectionChanged(SelectionChangedEvent event) {
            doSelectionChanged(event);
        }
    }

    /*
     * Link mode.  
     */
    class MouseClickListener implements KeyListener, MouseListener, MouseMoveListener, FocusListener, PaintListener,
            IPropertyChangeListener, IDocumentListener, ITextInputListener {

        /** The session is active. */
        private boolean fActive;

        /** The currently active style range. */
        private IRegion fActiveRegion;
        /** The currently active style range as position. */
        private Position fRememberedPosition;
        /** The hand cursor. */
        private Cursor fCursor;

        /** The link color. */
        private Color fColor;
        /** The key modifier mask. */
        private int fKeyModifierMask;

        /**
         * Style ranges before link mode.
         * @since 3.0
         */
        private StyleRange[] fOldStyleRanges;
        /**
         * Link mode style ranges region.
         * @since 3.0
         */
        IRegion fOldStyleRangeRegion;

        public void deactivate() {
            deactivate(false);
        }

        public void deactivate(boolean redrawAll) {
            if (!fActive)
                return;

            repairRepresentation(redrawAll);
            fActive = false;
        }

        public void install() {

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer == null)
                return;

            StyledText text = sourceViewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            updateColor(sourceViewer);

            sourceViewer.addTextInputListener(this);

            IDocument document = sourceViewer.getDocument();
            if (document != null)
                document.addDocumentListener(this);

            text.addKeyListener(this);
            text.addMouseListener(this);
            text.addMouseMoveListener(this);
            text.addFocusListener(this);
            text.addPaintListener(this);

            updateKeyModifierMask();

            IPreferenceStore preferenceStore = getPreferenceStore();
            preferenceStore.addPropertyChangeListener(this);
        }

        private void updateKeyModifierMask() {
            String modifiers = getPreferenceStore().getString(BROWSER_LIKE_LINKS_KEY_MODIFIER);
            fKeyModifierMask = computeStateMask(modifiers);
            if (fKeyModifierMask == -1) {
                // Fallback to stored state mask
                fKeyModifierMask = getPreferenceStore().getInt(BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK);
            }
        }

        private int computeStateMask(String modifiers) {
            if (modifiers == null)
                return -1;

            if (modifiers.length() == 0)
                return SWT.NONE;

            int stateMask = 0;
            StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$
            while (modifierTokenizer.hasMoreTokens()) {
                int modifier = EditorUtility.findLocalizedModifier(modifierTokenizer.nextToken());
                if (modifier == 0 || (stateMask & modifier) == modifier)
                    return -1;
                stateMask = stateMask | modifier;
            }
            return stateMask;
        }

        public void uninstall() {

            if (fColor != null) {
                fColor.dispose();
                fColor = null;
            }

            if (fCursor != null) {
                fCursor.dispose();
                fCursor = null;
            }

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer == null)
                return;

            sourceViewer.removeTextInputListener(this);

            IDocument document = sourceViewer.getDocument();
            if (document != null)
                document.removeDocumentListener(this);

            IPreferenceStore preferenceStore = getPreferenceStore();
            if (preferenceStore != null)
                preferenceStore.removePropertyChangeListener(this);

            StyledText text = sourceViewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            text.removeKeyListener(this);
            text.removeMouseListener(this);
            text.removeMouseMoveListener(this);
            text.removeFocusListener(this);
            text.removePaintListener(this);
        }

        /*
         * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
         */
        public void propertyChange(PropertyChangeEvent event) {
            if (event.getProperty().equals(JavaEditor.LINK_COLOR)) {
                ISourceViewer viewer = getSourceViewer();
                if (viewer != null)
                    updateColor(viewer);
            } else if (event.getProperty().equals(BROWSER_LIKE_LINKS_KEY_MODIFIER)) {
                updateKeyModifierMask();
            }
        }

        private void updateColor(ISourceViewer viewer) {
            if (fColor != null)
                fColor.dispose();

            StyledText text = viewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            Display display = text.getDisplay();
            fColor = createColor(getPreferenceStore(), JavaEditor.LINK_COLOR, display);
        }

        /**
         * Creates a color from the information stored in the given preference store.
         * Returns <code>null</code> if there is no such information available.
         */
        private Color createColor(IPreferenceStore store, String key, Display display) {

            RGB rgb = null;

            if (store.contains(key)) {

                if (store.isDefault(key))
                    rgb = PreferenceConverter.getDefaultColor(store, key);
                else
                    rgb = PreferenceConverter.getColor(store, key);

                if (rgb != null)
                    return new Color(display, rgb);
            }

            return null;
        }

        private void repairRepresentation() {
            repairRepresentation(false);
        }

        private void repairRepresentation(boolean redrawAll) {

            if (fActiveRegion == null)
                return;

            int offset = fActiveRegion.getOffset();
            int length = fActiveRegion.getLength();
            fActiveRegion = null;

            ISourceViewer viewer = getSourceViewer();
            if (viewer != null) {
                resetCursor(viewer);

                // remove style
                if (!redrawAll && viewer instanceof ITextViewerExtension2)
                    ((ITextViewerExtension2) viewer).invalidateTextPresentation(offset, length);
                else
                    viewer.invalidateTextPresentation();

                if (viewer instanceof ITextViewerExtension3) {
                    ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
                    offset = extension.modelOffset2WidgetOffset(offset);
                } else {
                    offset -= viewer.getVisibleRegion().getOffset();
                }

                try {
                    StyledText text = viewer.getTextWidget();
                    // Removes style
                    text.replaceStyleRanges(fOldStyleRangeRegion.getOffset(), fOldStyleRangeRegion.getLength(),
                            fOldStyleRanges);
                    //               text.replaceStyleRanges(offset, length, fOldStyleRanges);
                    // Causes underline to disappear
                    text.redrawRange(offset, length, false);
                } catch (IllegalArgumentException x) {
                    JavaPlugin.log(x);
                }
            }
        }

        // will eventually be replaced by a method provided by jdt.core      
        private IRegion selectWord(IDocument document, int anchor) {

            try {
                int offset = anchor;
                char c;

                while (offset >= 0) {
                    c = document.getChar(offset);
                    if (!Character.isJavaIdentifierPart(c))
                        break;
                    --offset;
                }

                int start = offset;

                offset = anchor;
                int length = document.getLength();

                while (offset < length) {
                    c = document.getChar(offset);
                    if (!Character.isJavaIdentifierPart(c))
                        break;
                    ++offset;
                }

                int end = offset;

                if (start == end)
                    return new Region(start, 0);
                else
                    return new Region(start + 1, end - start - 1);

            } catch (BadLocationException x) {
                return null;
            }
        }

        IRegion getCurrentTextRegion(ISourceViewer viewer) {

            int offset = getCurrentTextOffset(viewer);
            if (offset == -1)
                return null;

            IJavaElement input = SelectionConverter.getInput(JavaEditor.this);
            if (input == null)
                return null;

            try {

                IJavaElement[] elements = null;
                synchronized (input) {
                    elements = ((ICodeAssist) input).codeSelect(offset, 0);
                }

                if (elements == null || elements.length == 0)
                    return null;

                return selectWord(viewer.getDocument(), offset);

            } catch (JavaModelException e) {
                return null;
            }
        }

        private int getCurrentTextOffset(ISourceViewer viewer) {

            try {
                StyledText text = viewer.getTextWidget();
                if (text == null || text.isDisposed())
                    return -1;

                Display display = text.getDisplay();
                Point absolutePosition = display.getCursorLocation();
                Point relativePosition = text.toControl(absolutePosition);

                int widgetOffset = text.getOffsetAtLocation(relativePosition);
                if (viewer instanceof ITextViewerExtension3) {
                    ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
                    return extension.widgetOffset2ModelOffset(widgetOffset);
                } else {
                    return widgetOffset + viewer.getVisibleRegion().getOffset();
                }

            } catch (IllegalArgumentException e) {
                return -1;
            }
        }

        private void highlightRegion(ISourceViewer viewer, IRegion region) {

            if (region.equals(fActiveRegion))
                return;

            repairRepresentation();

            StyledText text = viewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            // highlight region
            int offset = 0;
            int length = 0;

            if (viewer instanceof ITextViewerExtension3) {
                ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
                IRegion widgetRange = extension.modelRange2WidgetRange(region);
                if (widgetRange == null)
                    return;

                offset = widgetRange.getOffset();
                length = widgetRange.getLength();

            } else {
                offset = region.getOffset() - viewer.getVisibleRegion().getOffset();
                length = region.getLength();
            }

            fOldStyleRanges = text.getStyleRanges(offset, length);
            fOldStyleRangeRegion = new Region(offset, length);

            applyForgroundStyle(text, offset, length);
            text.redrawRange(offset, length, false);

            fActiveRegion = region;
        }

        private void applyForgroundStyle(StyledText fTextWidget, int offset, int length) {
            StyleRange[] styleRanges = fTextWidget.getStyleRanges(offset, length);
            ArrayList newStyleRanges = new ArrayList(styleRanges.length + 10);
            int rangeOffset = offset;
            for (int i = 0, max = styleRanges.length; i < max; i++) {
                StyleRange sr = styleRanges[i];
                if (rangeOffset < sr.start) {
                    // Unstyled range
                    StyleRange usr = new StyleRange(rangeOffset, sr.start - rangeOffset, fColor, null);
                    newStyleRanges.add(usr);
                }
                rangeOffset = sr.start + sr.length;
                // Important: Must create a new one
                sr = new StyleRange(sr.start, sr.length, fColor, sr.background, sr.fontStyle);
                newStyleRanges.add(sr);
            }
            int endOffset = offset + length;
            if (rangeOffset < endOffset) {
                // Last unstyled range
                StyleRange usr = new StyleRange(rangeOffset, endOffset - rangeOffset, fColor, null);
                newStyleRanges.add(usr);
            }
            styleRanges = (StyleRange[]) newStyleRanges.toArray(new StyleRange[newStyleRanges.size()]);
            fTextWidget.replaceStyleRanges(offset, length, styleRanges);
        }

        private void activateCursor(ISourceViewer viewer) {
            StyledText text = viewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;
            Display display = text.getDisplay();
            if (fCursor == null)
                fCursor = new Cursor(display, SWT.CURSOR_HAND);
            text.setCursor(fCursor);
        }

        private void resetCursor(ISourceViewer viewer) {
            StyledText text = viewer.getTextWidget();
            if (text != null && !text.isDisposed())
                text.setCursor(null);

            if (fCursor != null) {
                fCursor.dispose();
                fCursor = null;
            }
        }

        /*
         * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
         */
        public void keyPressed(KeyEvent event) {

            if (fActive) {
                deactivate();
                return;
            }

            if (event.keyCode != fKeyModifierMask) {
                deactivate();
                return;
            }

            fActive = true;

            //         removed for #25871         
            //
            //         ISourceViewer viewer= getSourceViewer();
            //         if (viewer == null)
            //            return;
            //         
            //         IRegion region= getCurrentTextRegion(viewer);
            //         if (region == null)
            //            return;
            //         
            //         highlightRegion(viewer, region);
            //         activateCursor(viewer);                                    
        }

        /*
         * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
         */
        public void keyReleased(KeyEvent event) {

            if (!fActive)
                return;

            deactivate();
        }

        /*
         * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseDoubleClick(MouseEvent e) {
        }

        /*
         * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseDown(MouseEvent event) {

            if (!fActive)
                return;

            if (event.stateMask != fKeyModifierMask) {
                deactivate();
                return;
            }

            if (event.button != 1) {
                deactivate();
                return;
            }
        }

        /*
         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseUp(MouseEvent e) {

            if (!fActive)
                return;

            if (e.button != 1) {
                deactivate();
                return;
            }

            boolean wasActive = fCursor != null;

            deactivate();

            if (wasActive) {
                IAction action = getAction("OpenEditor"); //$NON-NLS-1$
                if (action != null)
                    action.run();
            }
        }

        /*
         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseMove(MouseEvent event) {

            if (event.widget instanceof Control && !((Control) event.widget).isFocusControl()) {
                deactivate();
                return;
            }

            if (!fActive) {
                if (event.stateMask != fKeyModifierMask)
                    return;
                // modifier was already pressed
                fActive = true;
            }

            ISourceViewer viewer = getSourceViewer();
            if (viewer == null) {
                deactivate();
                return;
            }

            StyledText text = viewer.getTextWidget();
            if (text == null || text.isDisposed()) {
                deactivate();
                return;
            }

            if ((event.stateMask & SWT.BUTTON1) != 0 && text.getSelectionCount() != 0) {
                deactivate();
                return;
            }

            IRegion region = getCurrentTextRegion(viewer);
            if (region == null || region.getLength() == 0) {
                repairRepresentation();
                return;
            }

            highlightRegion(viewer, region);
            activateCursor(viewer);
        }

        /*
         * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
         */
        public void focusGained(FocusEvent e) {
        }

        /*
         * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
         */
        public void focusLost(FocusEvent event) {
            deactivate();
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
         */
        public void documentAboutToBeChanged(DocumentEvent event) {
            if (fActive && fActiveRegion != null) {
                fRememberedPosition = new Position(fActiveRegion.getOffset(), fActiveRegion.getLength());
                try {
                    event.getDocument().addPosition(fRememberedPosition);
                } catch (BadLocationException x) {
                    fRememberedPosition = null;
                }
            }
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
         */
        public void documentChanged(DocumentEvent event) {
            if (fRememberedPosition != null) {
                if (!fRememberedPosition.isDeleted()) {

                    event.getDocument().removePosition(fRememberedPosition);
                    fActiveRegion = new Region(fRememberedPosition.getOffset(), fRememberedPosition.getLength());
                    fRememberedPosition = null;

                    ISourceViewer viewer = getSourceViewer();
                    if (viewer != null) {
                        StyledText widget = viewer.getTextWidget();
                        if (widget != null && !widget.isDisposed()) {
                            widget.getDisplay().asyncExec(new Runnable() {
                                public void run() {
                                    deactivate();
                                }
                            });
                        }
                    }

                } else {
                    fActiveRegion = null;
                    fRememberedPosition = null;
                    deactivate();
                }
            }
        }

        /*
         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
         */
        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
            if (oldInput == null)
                return;
            deactivate();
            oldInput.removeDocumentListener(this);
        }

        /*
         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
         */
        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
            if (newInput == null)
                return;
            newInput.addDocumentListener(this);
        }

        /*
         * @see PaintListener#paintControl(PaintEvent)
         */
        public void paintControl(PaintEvent event) {
            if (fActiveRegion == null)
                return;

            ISourceViewer viewer = getSourceViewer();
            if (viewer == null)
                return;

            StyledText text = viewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            int offset = 0;
            int length = 0;

            if (viewer instanceof ITextViewerExtension3) {

                ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
                IRegion widgetRange = extension.modelRange2WidgetRange(new Region(offset, length));
                if (widgetRange == null)
                    return;

                offset = widgetRange.getOffset();
                length = widgetRange.getLength();

            } else {

                IRegion region = viewer.getVisibleRegion();
                if (!includes(region, fActiveRegion))
                    return;

                offset = fActiveRegion.getOffset() - region.getOffset();
                length = fActiveRegion.getLength();
            }

            // support for bidi
            Point minLocation = getMinimumLocation(text, offset, length);
            Point maxLocation = getMaximumLocation(text, offset, length);

            int x1 = minLocation.x;
            int x2 = minLocation.x + maxLocation.x - minLocation.x - 1;
            int y = minLocation.y + text.getLineHeight() - 1;

            GC gc = event.gc;
            if (fColor != null && !fColor.isDisposed())
                gc.setForeground(fColor);
            gc.drawLine(x1, y, x2, y);
        }

        private boolean includes(IRegion region, IRegion position) {
            return position.getOffset() >= region.getOffset()
                    && position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
        }

        private Point getMinimumLocation(StyledText text, int offset, int length) {
            Point minLocation = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);

            for (int i = 0; i <= length; i++) {
                Point location = text.getLocationAtOffset(offset + i);

                if (location.x < minLocation.x)
                    minLocation.x = location.x;
                if (location.y < minLocation.y)
                    minLocation.y = location.y;
            }

            return minLocation;
        }

        private Point getMaximumLocation(StyledText text, int offset, int length) {
            Point maxLocation = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);

            for (int i = 0; i <= length; i++) {
                Point location = text.getLocationAtOffset(offset + i);

                if (location.x > maxLocation.x)
                    maxLocation.x = location.x;
                if (location.y > maxLocation.y)
                    maxLocation.y = location.y;
            }

            return maxLocation;
        }
    }

    /**
     * This action dispatches into two behaviours: If there is no current text
     * hover, the javadoc is displayed using information presenter. If there is
     * a current text hover, it is converted into a information presenter in
     * order to make it sticky.
     */
    class InformationDispatchAction extends TextEditorAction {

        /** The wrapped text operation action. */
        private final TextOperationAction fTextOperationAction;

        /**
         * Creates a dispatch action.
         */
        public InformationDispatchAction(ResourceBundle resourceBundle, String prefix,
                final TextOperationAction textOperationAction) {
            super(resourceBundle, prefix, JavaEditor.this);
            if (textOperationAction == null)
                throw new IllegalArgumentException();
            fTextOperationAction = textOperationAction;
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {

            /**
             * Information provider used to present the information.
             * 
             * @since 3.0
             */
            class InformationProvider implements IInformationProvider, IInformationProviderExtension2 {

                private IRegion fHoverRegion;
                private String fHoverInfo;
                private IInformationControlCreator fControlCreator;

                InformationProvider(IRegion hoverRegion, String hoverInfo,
                        IInformationControlCreator controlCreator) {
                    fHoverRegion = hoverRegion;
                    fHoverInfo = hoverInfo;
                    fControlCreator = controlCreator;
                }

                /*
                 * @see org.eclipse.jface.text.information.IInformationProvider#getSubject(org.eclipse.jface.text.ITextViewer, int)
                 */
                public IRegion getSubject(ITextViewer textViewer, int invocationOffset) {
                    return fHoverRegion;
                }

                /*
                 * @see org.eclipse.jface.text.information.IInformationProvider#getInformation(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
                 */
                public String getInformation(ITextViewer textViewer, IRegion subject) {
                    return fHoverInfo;
                }

                /*
                 * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
                 * @since 3.0
                 */
                public IInformationControlCreator getInformationPresenterControlCreator() {
                    return fControlCreator;
                }
            }

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer == null) {
                fTextOperationAction.run();
                return;
            }

            if (sourceViewer instanceof ITextViewerExtension4) {
                ITextViewerExtension4 extension4 = (ITextViewerExtension4) sourceViewer;
                if (extension4.moveFocusToWidgetToken())
                    return;
            }

            if (!(sourceViewer instanceof ITextViewerExtension2)) {
                fTextOperationAction.run();
                return;
            }

            ITextViewerExtension2 textViewerExtension2 = (ITextViewerExtension2) sourceViewer;

            // does a text hover exist?
            ITextHover textHover = textViewerExtension2.getCurrentTextHover();
            if (textHover == null) {
                fTextOperationAction.run();
                return;
            }

            Point hoverEventLocation = textViewerExtension2.getHoverEventLocation();
            int offset = computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
            if (offset == -1) {
                fTextOperationAction.run();
                return;
            }

            try {
                // get the text hover content
                String contentType = TextUtilities.getContentType(sourceViewer.getDocument(),
                        IJavaPartitions.JAVA_PARTITIONING, offset);

                IRegion hoverRegion = textHover.getHoverRegion(sourceViewer, offset);
                if (hoverRegion == null)
                    return;

                String hoverInfo = textHover.getHoverInfo(sourceViewer, hoverRegion);

                IInformationControlCreator controlCreator = null;
                if (textHover instanceof IInformationProviderExtension2)
                    controlCreator = ((IInformationProviderExtension2) textHover)
                            .getInformationPresenterControlCreator();

                IInformationProvider informationProvider = new InformationProvider(hoverRegion, hoverInfo,
                        controlCreator);

                fInformationPresenter.setOffset(offset);
                fInformationPresenter.setInformationProvider(informationProvider, contentType);
                fInformationPresenter.showInformation();

            } catch (BadLocationException e) {
            }
        }

        // modified version from TextViewer
        private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {

            StyledText styledText = textViewer.getTextWidget();
            IDocument document = textViewer.getDocument();

            if (document == null)
                return -1;

            try {
                int widgetLocation = styledText.getOffsetAtLocation(new Point(x, y));
                if (textViewer instanceof ITextViewerExtension3) {
                    ITextViewerExtension3 extension = (ITextViewerExtension3) textViewer;
                    return extension.widgetOffset2ModelOffset(widgetLocation);
                } else {
                    IRegion visibleRegion = textViewer.getVisibleRegion();
                    return widgetLocation + visibleRegion.getOffset();
                }
            } catch (IllegalArgumentException e) {
                return -1;
            }

        }
    }

    static protected class AnnotationAccess extends DefaultMarkerAnnotationAccess {

        public AnnotationAccess(MarkerAnnotationPreferences markerAnnotationPreferences) {
            super(markerAnnotationPreferences);
        }

        /*
         * @see org.eclipse.jface.text.source.IAnnotationAccess#getType(org.eclipse.jface.text.source.Annotation)
         */
        public Object getType(Annotation annotation) {
            if (annotation instanceof IJavaAnnotation) {
                IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation;
                if (javaAnnotation.isRelevant())
                    return javaAnnotation.getAnnotationType();
                return null;
            }
            return super.getType(annotation);
        }

        /*
         * @see org.eclipse.jface.text.source.IAnnotationAccess#isMultiLine(org.eclipse.jface.text.source.Annotation)
         */
        public boolean isMultiLine(Annotation annotation) {
            return true;
        }

        /*
         * @see org.eclipse.jface.text.source.IAnnotationAccess#isTemporary(org.eclipse.jface.text.source.Annotation)
         */
        public boolean isTemporary(Annotation annotation) {
            if (annotation instanceof IJavaAnnotation) {
                IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation;
                if (javaAnnotation.isRelevant())
                    return javaAnnotation.isTemporary();
            }
            return false;
        }
    }

    private class PropertyChangeListener implements org.eclipse.core.runtime.Preferences.IPropertyChangeListener {
        /*
         * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
         */
        public void propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) {
            handlePreferencePropertyChanged(event);
        }
    }

    /**
     * This action implements smart home.
     * 
     * Instead of going to the start of a line it does the following:
     * 
     * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it, taking JavaDoc and multi-line comments into account.
     * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
     * - if the caret is at the beginning of the line see first case.
     * 
     * @since 3.0
     */
    protected class SmartLineStartAction extends LineStartAction {

        /**
         * Creates a new smart line start action
         * 
         * @param textWidget the styled text widget
         * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
         */
        public SmartLineStartAction(final StyledText textWidget, final boolean doSelect) {
            super(textWidget, doSelect);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor.LineStartAction#getLineStartPosition(java.lang.String, int, java.lang.String)
         */
        protected int getLineStartPosition(final IDocument document, final String line, final int length,
                final int offset) {

            String type = IDocument.DEFAULT_CONTENT_TYPE;
            try {
                type = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset).getType();
            } catch (BadLocationException exception) {
                // Should not happen
            }

            int index = super.getLineStartPosition(document, line, length, offset);
            if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) {
                if (index < length - 1 && line.charAt(index) == '*' && line.charAt(index + 1) != '/') {
                    do {
                        ++index;
                    } while (index < length && Character.isWhitespace(line.charAt(index)));
                }
            } else {
                if (index < length - 1 && line.charAt(index) == '/' && line.charAt(++index) == '/') {
                    do {
                        ++index;
                    } while (index < length && Character.isWhitespace(line.charAt(index)));
                }
            }
            return index;
        }
    }

    /**
     * Text navigation action to navigate to the next sub-word.
     * 
     * @since 3.0
     */
    protected abstract class NextSubWordAction extends TextNavigationAction {

        /** Collator to determine the sub-word boundaries */
        private final RuleBasedCollator fCollator = (RuleBasedCollator) Collator.getInstance();

        /**
         * Creates a new next sub-word action.
         * 
         * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
         */
        protected NextSubWordAction(int code) {
            super(getSourceViewer().getTextWidget(), code);

            // Only compare upper-/lower case
            fCollator.setStrength(Collator.TERTIARY);
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            try {

                final ISourceViewer viewer = getSourceViewer();
                final IDocument document = viewer.getDocument();

                int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());

                // Check whether we are in a java code partititon and the preference is enabled
                final IPreferenceStore store = getPreferenceStore();
                final ITypedRegion region = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING,
                        position);
                if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) {
                    super.run();
                    return;
                }

                // Check whether right hand character of caret is valid identifier start
                if (Character.isJavaIdentifierStart(document.getChar(position))) {

                    int offset = 0;
                    int order = CollationElementIterator.NULLORDER;
                    short previous = Short.MAX_VALUE;
                    short next = Short.MAX_VALUE;

                    // Acquire collator for partition around caret
                    final String buffer = document.get(position,
                            region.getOffset() + region.getLength() - position);
                    final CollationElementIterator iterator = fCollator.getCollationElementIterator(buffer);

                    // Iterate to first upper-case character
                    do {
                        // Check whether we reached end of word
                        offset = iterator.getOffset();
                        if (!Character.isJavaIdentifierPart(document.getChar(position + offset)))
                            throw new BadLocationException();

                        // Test next characters
                        order = iterator.next();
                        next = CollationElementIterator.tertiaryOrder(order);
                        if (next <= previous)
                            previous = next;
                        else
                            break;

                    } while (order != CollationElementIterator.NULLORDER);

                    // Check for leading underscores            
                    position += offset;
                    if (Character.getType(document.getChar(position - 1)) != Character.CONNECTOR_PUNCTUATION) {
                        setCaretPosition(position);
                        getTextWidget().showSelection();
                        fireSelectionChanged();
                        return;
                    }
                }
            } catch (BadLocationException exception) {
                // Use default behavior
            }
            super.run();
        }

        /**
         * Sets the caret position to the sub-word boundary given with <code>position</code>.
         * 
         * @param position Position where the action should move the caret
         */
        protected abstract void setCaretPosition(int position);
    }

    /**
     * Text navigation action to navigate to the next sub-word.
     * 
     * @since 3.0
     */
    protected class NavigateNextSubWordAction extends NextSubWordAction {

        /**
         * Creates a new navigate next sub-word action.
         */
        public NavigateNextSubWordAction() {
            super(ST.WORD_NEXT);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
        }
    }

    /**
     * Text operation action to delete the next sub-word.
     * 
     * @since 3.0
     */
    protected class DeleteNextSubWordAction extends NextSubWordAction {

        /**
         * Creates a new delete next sub-word action.
         */
        public DeleteNextSubWordAction() {
            super(ST.DELETE_WORD_NEXT);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            final ISourceViewer viewer = getSourceViewer();
            final int caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());

            try {
                viewer.getDocument().replace(caret, position - caret, ""); //$NON-NLS-1$
            } catch (BadLocationException exception) {
                // Should not happen
            }
        }
    }

    /**
     * Text operation action to select the next sub-word.
     * 
     * @since 3.0
     */
    protected class SelectNextSubWordAction extends NextSubWordAction {

        /**
         * Creates a new select next sub-word action.
         */
        public SelectNextSubWordAction() {
            super(ST.SELECT_WORD_NEXT);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            final ISourceViewer viewer = getSourceViewer();

            final StyledText text = viewer.getTextWidget();
            if (text != null && !text.isDisposed()) {

                final Point selection = text.getSelection();
                final int caret = text.getCaretOffset();
                final int offset = modelOffset2WidgetOffset(viewer, position);

                if (caret == selection.x)
                    text.setSelectionRange(selection.y, offset - selection.y);
                else
                    text.setSelectionRange(selection.x, offset - selection.x);
            }
        }
    }

    /**
     * Text navigation action to navigate to the previous sub-word.
     * 
     * @since 3.0
     */
    protected abstract class PreviousSubWordAction extends TextNavigationAction {

        /** Collator to determine the sub-word boundaries */
        private final RuleBasedCollator fCollator = (RuleBasedCollator) Collator.getInstance();

        /**
         * Creates a new previous sub-word action.
         * 
         * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
         */
        protected PreviousSubWordAction(final int code) {
            super(getSourceViewer().getTextWidget(), code);

            // Only compare upper-/lower case
            fCollator.setStrength(Collator.TERTIARY);
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            try {

                final ISourceViewer viewer = getSourceViewer();
                final IDocument document = viewer.getDocument();

                int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()) - 1;

                // Check whether we are in a java code partititon and the preference is enabled
                final IPreferenceStore store = getPreferenceStore();
                if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) {
                    super.run();
                    return;
                }

                // Ignore trailing white spaces
                char character = document.getChar(position);
                while (position > 0 && Character.isWhitespace(character)) {
                    --position;
                    character = document.getChar(position);
                }

                // Check whether left hand character of caret is valid identifier part
                if (Character.isJavaIdentifierPart(character)) {

                    int offset = 0;
                    int order = CollationElementIterator.NULLORDER;
                    short previous = Short.MAX_VALUE;
                    short next = Short.MAX_VALUE;

                    // Acquire collator for partition around caret
                    final ITypedRegion region = TextUtilities.getPartition(document,
                            IJavaPartitions.JAVA_PARTITIONING, position);
                    final String buffer = document.get(region.getOffset(), position - region.getOffset() + 1);
                    final CollationElementIterator iterator = fCollator.getCollationElementIterator(buffer);

                    // Iterate to first upper-case character
                    iterator.setOffset(buffer.length() - 1);
                    do {

                        // Check whether we reached begin of word or single upper-case start
                        offset = iterator.getOffset();
                        character = document.getChar(region.getOffset() + offset);
                        if (!Character.isJavaIdentifierPart(character))
                            throw new BadLocationException();
                        else if (Character.isUpperCase(character)) {
                            ++offset;
                            break;
                        }

                        // Test next characters
                        order = iterator.previous();
                        next = CollationElementIterator.tertiaryOrder(order);
                        if (next <= previous)
                            previous = next;
                        else
                            break;

                    } while (order != CollationElementIterator.NULLORDER);

                    // Check left character for multiple upper-case characters
                    position = position - buffer.length() + offset - 1;
                    character = document.getChar(position);

                    while (position >= 0 && Character.isUpperCase(character))
                        character = document.getChar(--position);

                    setCaretPosition(position + 1);
                    getTextWidget().showSelection();
                    fireSelectionChanged();
                    return;
                }
            } catch (BadLocationException exception) {
                // Use default behavior
            }
            super.run();
        }

        /**
         * Sets the caret position to the sub-word boundary given with <code>position</code>.
         * 
         * @param position Position where the action should move the caret
         */
        protected abstract void setCaretPosition(int position);
    }

    /**
     * Text navigation action to navigate to the previous sub-word.
     * 
     * @since 3.0
     */
    protected class NavigatePreviousSubWordAction extends PreviousSubWordAction {

        /**
         * Creates a new navigate previous sub-word action.
         */
        public NavigatePreviousSubWordAction() {
            super(ST.WORD_PREVIOUS);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
        }
    }

    /**
     * Text operation action to delete the previous sub-word.
     * 
     * @since 3.0
     */
    protected class DeletePreviousSubWordAction extends PreviousSubWordAction {

        /**
         * Creates a new delete previous sub-word action.
         */
        public DeletePreviousSubWordAction() {
            super(ST.DELETE_WORD_PREVIOUS);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            final ISourceViewer viewer = getSourceViewer();
            final int caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());

            try {
                viewer.getDocument().replace(position, caret - position, ""); //$NON-NLS-1$
            } catch (BadLocationException exception) {
                // Should not happen
            }
        }
    }

    /**
     * Text operation action to select the previous sub-word.
     * 
     * @since 3.0
     */
    protected class SelectPreviousSubWordAction extends PreviousSubWordAction {

        /**
         * Creates a new select previous sub-word action.
         */
        public SelectPreviousSubWordAction() {
            super(ST.SELECT_WORD_PREVIOUS);
        }

        /*
         * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
         */
        protected void setCaretPosition(final int position) {
            final ISourceViewer viewer = getSourceViewer();

            final StyledText text = viewer.getTextWidget();
            if (text != null && !text.isDisposed()) {

                final Point selection = text.getSelection();
                final int caret = text.getCaretOffset();
                final int offset = modelOffset2WidgetOffset(viewer, position);

                if (caret == selection.x)
                    text.setSelectionRange(selection.y, offset - selection.y);
                else
                    text.setSelectionRange(selection.x, offset - selection.x);
            }
        }
    }

    /**
     * Quick format action to format the enclosing java element.
     * <p>
     * The quick format action works as follows:
     * <ul>
     * <li>If there is no selection and the caret is positioned on a Java element,
     * only this element is formatted. If the element has some accompanying comment,
     * then the comment is formatted as well.</li>
     * <li>If the selection spans one or more partitions of the document, then all
     * partitions covered by the selection are entirely formatted.</li>
     * <p>
     * Partitions at the end of the selection are not completed, except for comments.
     * 
     * @since 3.0
     */
    protected class QuickFormatAction extends Action {

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {

            final JavaSourceViewer viewer = (JavaSourceViewer) getSourceViewer();
            if (viewer.isEditable()) {

                final Point selection = viewer.rememberSelection();
                try {
                    viewer.setRedraw(false);

                    final String type = TextUtilities.getContentType(viewer.getDocument(),
                            IJavaPartitions.JAVA_PARTITIONING, selection.x);
                    if (type.equals(IDocument.DEFAULT_CONTENT_TYPE) && selection.y == 0) {

                        try {
                            final IJavaElement element = getElementAt(selection.x, true);
                            if (element != null && element.exists()) {

                                final int kind = element.getElementType();
                                if (kind == IJavaElement.TYPE || kind == IJavaElement.METHOD
                                        || kind == IJavaElement.INITIALIZER) {

                                    final ISourceReference reference = (ISourceReference) element;
                                    final ISourceRange range = reference.getSourceRange();

                                    if (range != null) {
                                        viewer.setSelectedRange(range.getOffset(), range.getLength());
                                        viewer.doOperation(ISourceViewer.FORMAT);
                                    }
                                }
                            }
                        } catch (JavaModelException exception) {
                            // Should not happen
                        }
                    } else {
                        viewer.setSelectedRange(selection.x, 1);
                        viewer.doOperation(ISourceViewer.FORMAT);
                    }
                } catch (BadLocationException exception) {
                    // Can not happen
                } finally {

                    viewer.setRedraw(true);
                    viewer.restoreSelection();
                }
            }
        }
    }

    /** Preference key for the link color */
    protected final static String LINK_COLOR = PreferenceConstants.EDITOR_LINK_COLOR;
    /** Preference key for matching brackets */
    protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS;
    /** Preference key for matching brackets color */
    protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR;
    /** Preference key for compiler task tags */
    private final static String COMPILER_TASK_TAGS = JavaCore.COMPILER_TASK_TAGS;
    /** Preference key for browser like links */
    private final static String BROWSER_LIKE_LINKS = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS;
    /** Preference key for key modifier of browser like links */
    private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER;
    /**
     * Preference key for key modifier mask of browser like links.
     * The value is only used if the value of <code>EDITOR_BROWSER_LIKE_LINKS</code>
     * cannot be resolved to valid SWT modifier bits.
     * 
     * @since 2.1.1
     */
    private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK;

    protected final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };

    /** The outline page */
    protected JavaOutlinePage fOutlinePage;
    /** Outliner context menu Id */
    protected String fOutlinerContextMenuId;
    /**
     * The editor selection changed listener.
     * 
     * @since 3.0
     */
    private EditorSelectionChangedListener fEditorSelectionChangedListener;
    /** The selection changed listener */
    protected AbstractSelectionChangedListener fOutlineSelectionChangedListener = new OutlineSelectionChangedListener();
    /** The editor's bracket matcher */
    protected JavaPairMatcher fBracketMatcher = new JavaPairMatcher(BRACKETS);
    /** This editor's encoding support */
    private DefaultEncodingSupport fEncodingSupport;
    /** The mouse listener */
    private MouseClickListener fMouseListener;
    /** The information presenter. */
    private InformationPresenter fInformationPresenter;
    /** History for structure select action */
    private SelectionHistory fSelectionHistory;
    /** The preference property change listener for java core. */
    private org.eclipse.core.runtime.Preferences.IPropertyChangeListener fPropertyChangeListener = new PropertyChangeListener();
    /**
     * Indicates whether this editor is about to update any annotation views.
     * @since 3.0
     */
    private boolean fIsUpdatingAnnotationViews = false;
    /**
     * The marker that served as last target for a goto marker request.
     * @since 3.0
     */
    private IMarker fLastMarkerTarget = null;
    protected CompositeActionGroup fActionGroups;
    private CompositeActionGroup fContextMenuGroup;
    /**
     * Holds the current occurrence annotations.
     * @since 3.0
     */
    private ArrayList fOccurrenceAnnotations = new ArrayList();
    /**
     * Counts the number of background computation requests.
     * @since 3.0
     */
    private volatile int fComputeCount;
    private boolean fMarkOccurrenceAnnotations;

    /**
     * Returns the most narrow java element including the given offset.
     * 
     * @param offset the offset inside of the requested element
     * @return the most narrow java element
     */
    abstract protected IJavaElement getElementAt(int offset);

    /**
     * Returns the java element of this editor's input corresponding to the given IJavaElement
     */
    abstract protected IJavaElement getCorrespondingElement(IJavaElement element);

    /**
     * Sets the input of the editor's outline page.
     */
    abstract protected void setOutlinePageInput(JavaOutlinePage page, IEditorInput input);

    /**
     * Default constructor.
     */
    public JavaEditor() {
        super();
        JavaTextTools textTools = JavaPlugin.getDefault().getJavaTextTools();
        setSourceViewerConfiguration(
                new JavaSourceViewerConfiguration(textTools, this, IJavaPartitions.JAVA_PARTITIONING));
        setRangeIndicator(new DefaultRangeIndicator());
        IPreferenceStore store = JavaPlugin.getDefault().getPreferenceStore();
        setPreferenceStore(store);
        setKeyBindingScopes(new String[] { "org.eclipse.jdt.ui.javaEditorScope" }); //$NON-NLS-1$
        fMarkOccurrenceAnnotations = store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES);
    }

    /*
     * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
     */
    protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRuler verticalRuler, int styles) {

        ISourceViewer viewer = createJavaSourceViewer(parent, verticalRuler, getOverviewRuler(),
                isOverviewRulerVisible(), styles);

        StyledText text = viewer.getTextWidget();
        text.addBidiSegmentListener(new BidiSegmentListener() {
            public void lineGetSegments(BidiSegmentEvent event) {
                event.segments = getBidiLineSegments(event.lineOffset, event.lineText);
            }
        });

        JavaUIHelp.setHelp(this, text, IJavaHelpContextIds.JAVA_EDITOR);

        // ensure source viewer decoration support has been created and configured
        getSourceViewerDecorationSupport(viewer);

        return viewer;
    }

    /*
     * @see org.eclipse.ui.texteditor.ExtendedTextEditor#createAnnotationAccess()
     */
    protected IAnnotationAccess createAnnotationAccess() {
        return new AnnotationAccess(new MarkerAnnotationPreferences());
    }

    public final ISourceViewer getViewer() {
        return getSourceViewer();
    }

    /*
     * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
     */
    protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler verticalRuler,
            IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles) {
        return new JavaSourceViewer(parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles);
    }

    /*
     * @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
     */
    protected boolean affectsTextPresentation(PropertyChangeEvent event) {
        JavaTextTools textTools = JavaPlugin.getDefault().getJavaTextTools();
        return textTools.affectsBehavior(event);
    }

    /**
     * Sets the outliner's context menu ID.
     */
    protected void setOutlinerContextMenuId(String menuId) {
        fOutlinerContextMenuId = menuId;
    }

    /**
     *  Returns the standard action group of this editor.
     */
    protected ActionGroup getActionGroup() {
        return fActionGroups;
    }

    /*
     * @see AbstractTextEditor#editorContextMenuAboutToShow
     */
    public void editorContextMenuAboutToShow(IMenuManager menu) {

        super.editorContextMenuAboutToShow(menu);
        menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, new Separator(IContextMenuConstants.GROUP_OPEN));
        menu.insertAfter(IContextMenuConstants.GROUP_OPEN, new GroupMarker(IContextMenuConstants.GROUP_SHOW));

        ActionContext context = new ActionContext(getSelectionProvider().getSelection());
        fContextMenuGroup.setContext(context);
        fContextMenuGroup.fillContextMenu(menu);
        fContextMenuGroup.setContext(null);
    }

    /**
     * Creates the outline page used with this editor.
     */
    protected JavaOutlinePage createOutlinePage() {
        JavaOutlinePage page = new JavaOutlinePage(fOutlinerContextMenuId, this);
        fOutlineSelectionChangedListener.install(page);
        setOutlinePageInput(page, getEditorInput());
        return page;
    }

    /**
     * Informs the editor that its outliner has been closed.
     */
    public void outlinePageClosed() {
        if (fOutlinePage != null) {
            fOutlineSelectionChangedListener.uninstall(fOutlinePage);
            fOutlinePage = null;
            resetHighlightRange();
        }
    }

    /**
     * Synchronizes the outliner selection with the given element
     * position in the editor.
     * 
     * @param element the java element to select
     */
    protected void synchronizeOutlinePage(ISourceReference element) {
        synchronizeOutlinePage(element, true);
    }

    /**
     * Synchronizes the outliner selection with the given element
     * position in the editor.
     * 
     * @param element the java element to select
     * @param checkIfOutlinePageActive <code>true</code> if check for active outline page needs to be done
     */
    protected void synchronizeOutlinePage(ISourceReference element, boolean checkIfOutlinePageActive) {
        if (fOutlinePage != null && element != null && !(checkIfOutlinePageActive && isJavaOutlinePageActive())) {
            fOutlineSelectionChangedListener.uninstall(fOutlinePage);
            fOutlinePage.select(element);
            fOutlineSelectionChangedListener.install(fOutlinePage);
        }
    }

    /**
     * Synchronizes the outliner selection with the actual cursor
     * position in the editor.
     */
    public void synchronizeOutlinePageSelection() {
        synchronizeOutlinePage(computeHighlightRangeSourceReference());
    }

    /*
     * Get the desktop's StatusLineManager
     */
    protected IStatusLineManager getStatusLineManager() {
        IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor();
        if (contributor instanceof EditorActionBarContributor) {
            return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager();
        }
        return null;
    }

    /*
     * @see AbstractTextEditor#getAdapter(Class)
     */
    public Object getAdapter(Class required) {

        if (IContentOutlinePage.class.equals(required)) {
            if (fOutlinePage == null)
                fOutlinePage = createOutlinePage();
            return fOutlinePage;
        }

        if (IEncodingSupport.class.equals(required))
            return fEncodingSupport;

        if (required == IShowInTargetList.class) {
            return new IShowInTargetList() {
                public String[] getShowInTargetIds() {
                    return new String[] { JavaUI.ID_PACKAGES, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV };
                }

            };
        }

        return super.getAdapter(required);
    }

    protected void setSelection(ISourceReference reference, boolean moveCursor) {

        ISelection selection = getSelectionProvider().getSelection();
        if (selection instanceof TextSelection) {
            TextSelection textSelection = (TextSelection) selection;
            // PR 39995: [navigation] Forward history cleared after going back in navigation history:
            // mark only in navigation history if the cursor is being moved (which it isn't if
            // this is called from a PostSelectionEvent that should only update the magnet)
            if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0))
                markInNavigationHistory();
        }

        if (reference != null) {

            StyledText textWidget = null;

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer != null)
                textWidget = sourceViewer.getTextWidget();

            if (textWidget == null)
                return;

            try {

                ISourceRange range = reference.getSourceRange();
                if (range == null)
                    return;

                int offset = range.getOffset();
                int length = range.getLength();

                if (offset < 0 || length < 0)
                    return;

                setHighlightRange(offset, length, moveCursor);

                if (!moveCursor)
                    return;

                offset = -1;
                length = -1;

                if (reference instanceof IMember) {
                    range = ((IMember) reference).getNameRange();
                    if (range != null) {
                        offset = range.getOffset();
                        length = range.getLength();
                    }
                } else if (reference instanceof IImportDeclaration) {
                    String name = ((IImportDeclaration) reference).getElementName();
                    if (name != null && name.length() > 0) {
                        String content = reference.getSource();
                        if (content != null) {
                            offset = range.getOffset() + content.indexOf(name);
                            length = name.length();
                        }
                    }
                } else if (reference instanceof IPackageDeclaration) {
                    String name = ((IPackageDeclaration) reference).getElementName();
                    if (name != null && name.length() > 0) {
                        String content = reference.getSource();
                        if (content != null) {
                            offset = range.getOffset() + content.indexOf(name);
                            length = name.length();
                        }
                    }
                }

                if (offset > -1 && length > 0) {

                    try {
                        textWidget.setRedraw(false);
                        sourceViewer.revealRange(offset, length);
                        sourceViewer.setSelectedRange(offset, length);
                    } finally {
                        textWidget.setRedraw(true);
                    }

                    markInNavigationHistory();
                }

            } catch (JavaModelException x) {
            } catch (IllegalArgumentException x) {
            }

        } else if (moveCursor) {
            resetHighlightRange();
            markInNavigationHistory();
        }
    }

    public void setSelection(IJavaElement element) {

        if (element == null || element instanceof ICompilationUnit || element instanceof IClassFile) {
            /*
             * If the element is an ICompilationUnit this unit is either the input
             * of this editor or not being displayed. In both cases, nothing should
             * happened. (http://dev.eclipse.org/bugs/show_bug.cgi?id=5128)
             */
            return;
        }

        IJavaElement corresponding = getCorrespondingElement(element);
        if (corresponding instanceof ISourceReference) {
            ISourceReference reference = (ISourceReference) corresponding;
            // set hightlight range
            setSelection(reference, true);
            // set outliner selection
            if (fOutlinePage != null) {
                fOutlineSelectionChangedListener.uninstall(fOutlinePage);
                fOutlinePage.select(reference);
                fOutlineSelectionChangedListener.install(fOutlinePage);
            }
        }
    }

    protected void doSelectionChanged(SelectionChangedEvent event) {

        ISourceReference reference = null;

        ISelection selection = event.getSelection();
        Iterator iter = ((IStructuredSelection) selection).iterator();
        while (iter.hasNext()) {
            Object o = iter.next();
            if (o instanceof ISourceReference) {
                reference = (ISourceReference) o;
                break;
            }
        }
        if (!isActivePart() && JavaPlugin.getActivePage() != null)
            JavaPlugin.getActivePage().bringToTop(this);

        setSelection(reference, !isActivePart());
    }

    /*
     * @see AbstractTextEditor#adjustHighlightRange(int, int)
     */
    protected void adjustHighlightRange(int offset, int length) {

        try {

            IJavaElement element = getElementAt(offset);
            while (element instanceof ISourceReference) {
                ISourceRange range = ((ISourceReference) element).getSourceRange();
                if (offset < range.getOffset() + range.getLength() && range.getOffset() < offset + length) {
                    setHighlightRange(range.getOffset(), range.getLength(), true);
                    if (fOutlinePage != null) {
                        fOutlineSelectionChangedListener.uninstall(fOutlinePage);
                        fOutlinePage.select((ISourceReference) element);
                        fOutlineSelectionChangedListener.install(fOutlinePage);
                    }
                    return;
                }
                element = element.getParent();
            }

        } catch (JavaModelException x) {
            JavaPlugin.log(x.getStatus());
        }

        resetHighlightRange();
    }

    protected boolean isActivePart() {
        IWorkbenchPart part = getActivePart();
        return part != null && part.equals(this);
    }

    private boolean isJavaOutlinePageActive() {
        IWorkbenchPart part = getActivePart();
        return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage;
    }

    private IWorkbenchPart getActivePart() {
        IWorkbenchWindow window = getSite().getWorkbenchWindow();
        IPartService service = window.getPartService();
        IWorkbenchPart part = service.getActivePart();
        return part;
    }

    /*
     * @see StatusTextEditor#getStatusHeader(IStatus)
     */
    protected String getStatusHeader(IStatus status) {
        if (fEncodingSupport != null) {
            String message = fEncodingSupport.getStatusHeader(status);
            if (message != null)
                return message;
        }
        return super.getStatusHeader(status);
    }

    /*
     * @see StatusTextEditor#getStatusBanner(IStatus)
     */
    protected String getStatusBanner(IStatus status) {
        if (fEncodingSupport != null) {
            String message = fEncodingSupport.getStatusBanner(status);
            if (message != null)
                return message;
        }
        return super.getStatusBanner(status);
    }

    /*
     * @see StatusTextEditor#getStatusMessage(IStatus)
     */
    protected String getStatusMessage(IStatus status) {
        if (fEncodingSupport != null) {
            String message = fEncodingSupport.getStatusMessage(status);
            if (message != null)
                return message;
        }
        return super.getStatusMessage(status);
    }

    /*
     * @see AbstractTextEditor#doSetInput
     */
    protected void doSetInput(IEditorInput input) throws CoreException {
        super.doSetInput(input);
        if (fEncodingSupport != null)
            fEncodingSupport.reset();
        setOutlinePageInput(fOutlinePage, input);
    }

    /*
     * @see IWorkbenchPart#dispose()
     */
    public void dispose() {
        // cancel possible running computation
        fMarkOccurrenceAnnotations = false;
        fComputeCount++;

        if (isBrowserLikeLinks())
            disableBrowserLikeLinks();

        if (fEncodingSupport != null) {
            fEncodingSupport.dispose();
            fEncodingSupport = null;
        }

        if (fPropertyChangeListener != null) {
            Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
            preferences.removePropertyChangeListener(fPropertyChangeListener);
            fPropertyChangeListener = null;
        }

        if (fBracketMatcher != null) {
            fBracketMatcher.dispose();
            fBracketMatcher = null;
        }

        if (fSelectionHistory != null) {
            fSelectionHistory.dispose();
            fSelectionHistory = null;
        }

        if (fEditorSelectionChangedListener != null) {
            fEditorSelectionChangedListener.uninstall(getSelectionProvider());
            fEditorSelectionChangedListener = null;
        }

        super.dispose();
    }

    protected void createActions() {
        super.createActions();

        ResourceAction resAction = new AddTaskAction(JavaEditorMessages.getResourceBundle(), "AddTask.", this); //$NON-NLS-1$
        resAction.setHelpContextId(IAbstractTextEditorHelpContextIds.ADD_TASK_ACTION);
        resAction.setActionDefinitionId(ITextEditorActionDefinitionIds.ADD_TASK);
        setAction(ITextEditorActionConstants.ADD_TASK, resAction);

        ActionGroup oeg, ovg, jsg, sg;
        fActionGroups = new CompositeActionGroup(
                new ActionGroup[] { oeg = new OpenEditorActionGroup(this), sg = new ShowActionGroup(this),
                        ovg = new OpenViewActionGroup(this), jsg = new JavaSearchActionGroup(this) });
        fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] { oeg, ovg, sg, jsg });

        resAction = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "ShowJavaDoc.", this, //$NON-NLS-1$
                ISourceViewer.INFORMATION, true);
        resAction = new InformationDispatchAction(JavaEditorMessages.getResourceBundle(), "ShowJavaDoc.", //$NON-NLS-1$
                (TextOperationAction) resAction);
        resAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_JAVADOC);
        setAction("ShowJavaDoc", resAction); //$NON-NLS-1$
        WorkbenchHelp.setHelp(resAction, IJavaHelpContextIds.SHOW_JAVADOC_ACTION);

        Action action = new GotoMatchingBracketAction(this);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_MATCHING_BRACKET);
        setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action);

        action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "ShowOutline.", this, //$NON-NLS-1$
                JavaSourceViewer.SHOW_OUTLINE, true);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_OUTLINE);
        setAction(IJavaEditorActionDefinitionIds.SHOW_OUTLINE, action);
        WorkbenchHelp.setHelp(action, IJavaHelpContextIds.SHOW_OUTLINE_ACTION);

        action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "OpenStructure.", this, //$NON-NLS-1$
                JavaSourceViewer.OPEN_STRUCTURE, true);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_STRUCTURE);
        setAction(IJavaEditorActionDefinitionIds.OPEN_STRUCTURE, action);
        WorkbenchHelp.setHelp(action, IJavaHelpContextIds.OPEN_STRUCTURE_ACTION);

        action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "OpenHierarchy.", this, //$NON-NLS-1$
                JavaSourceViewer.SHOW_HIERARCHY, true);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY);
        setAction(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY, action);
        WorkbenchHelp.setHelp(action, IJavaHelpContextIds.OPEN_HIERARCHY_ACTION);

        fEncodingSupport = new DefaultEncodingSupport();
        fEncodingSupport.initialize(this);

        fSelectionHistory = new SelectionHistory(this);

        action = new StructureSelectEnclosingAction(this, fSelectionHistory);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_ENCLOSING);
        setAction(StructureSelectionAction.ENCLOSING, action);

        action = new StructureSelectNextAction(this, fSelectionHistory);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_NEXT);
        setAction(StructureSelectionAction.NEXT, action);

        action = new StructureSelectPreviousAction(this, fSelectionHistory);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_PREVIOUS);
        setAction(StructureSelectionAction.PREVIOUS, action);

        StructureSelectHistoryAction historyAction = new StructureSelectHistoryAction(this, fSelectionHistory);
        historyAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_LAST);
        setAction(StructureSelectionAction.HISTORY, historyAction);
        fSelectionHistory.setHistoryAction(historyAction);

        action = GoToNextPreviousMemberAction.newGoToNextMemberAction(this);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_NEXT_MEMBER);
        setAction(GoToNextPreviousMemberAction.NEXT_MEMBER, action);

        action = GoToNextPreviousMemberAction.newGoToPreviousMemberAction(this);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_PREVIOUS_MEMBER);
        setAction(GoToNextPreviousMemberAction.PREVIOUS_MEMBER, action);

        action = new QuickFormatAction();
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.QUICK_FORMAT);
        setAction(IJavaEditorActionDefinitionIds.QUICK_FORMAT, action);

        action = new RemoveOccurrenceAnnotations(this);
        action.setActionDefinitionId(IJavaEditorActionDefinitionIds.REMOVE_OCCURRENCE_ANNOTATIONS);
        setAction("RemoveOccurrenceAnnotations", action); //$NON-NLS-1$
    }

    public void updatedTitleImage(Image image) {
        setTitleImage(image);
    }

    /*
     * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
     */
    protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {

        try {

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer == null)
                return;

            String property = event.getProperty();

            if (PreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
                Object value = event.getNewValue();
                if (value instanceof Integer) {
                    sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
                } else if (value instanceof String) {
                    sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value));
                }
                return;
            }

            if (isJavaEditorHoverProperty(property))
                updateHoverBehavior();

            if (BROWSER_LIKE_LINKS.equals(property)) {
                if (isBrowserLikeLinks())
                    enableBrowserLikeLinks();
                else
                    disableBrowserLikeLinks();
                return;
            }

            if (PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE.equals(property)) {
                if ((event.getNewValue() instanceof Boolean) && ((Boolean) event.getNewValue()).booleanValue()) {
                    fEditorSelectionChangedListener = new EditorSelectionChangedListener();
                    fEditorSelectionChangedListener.install(getSelectionProvider());
                    fEditorSelectionChangedListener.selectionChanged();
                } else {
                    fEditorSelectionChangedListener.uninstall(getSelectionProvider());
                    fEditorSelectionChangedListener = null;
                }
                return;
            }

            if (PreferenceConstants.EDITOR_DISABLE_OVERWRITE_MODE.equals(property)) {
                if (event.getNewValue() instanceof Boolean) {
                    Boolean disable = (Boolean) event.getNewValue();
                    configureInsertMode(OVERWRITE, !disable.booleanValue());
                }
            }

            if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) {
                if (event.getNewValue() instanceof Boolean) {
                    fMarkOccurrenceAnnotations = ((Boolean) event.getNewValue()).booleanValue();
                    if (!fMarkOccurrenceAnnotations) {
                        fComputeCount++;
                        removeOccurrenceAnnotations();
                    }
                }
            }

        } finally {
            super.handlePreferenceStoreChanged(event);
        }
    }

    /**
     * Initializes the given viewer's colors.
     * 
     * @param viewer the viewer to be initialized
     * @since 3.0
     */
    protected void initializeViewerColors(ISourceViewer viewer) {
        // is handled by JavaSourceViewer
    }

    private boolean isJavaEditorHoverProperty(String property) {
        return PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property);
    }

    /**
     * Return whether the browser like links should be enabled
     * according to the preference store settings.
     * @return <code>true</code> if the browser like links should be enabled
     */
    private boolean isBrowserLikeLinks() {
        IPreferenceStore store = getPreferenceStore();
        return store.getBoolean(BROWSER_LIKE_LINKS);
    }

    /**
     * Enables browser like links.
     */
    private void enableBrowserLikeLinks() {
        if (fMouseListener == null) {
            fMouseListener = new MouseClickListener();
            fMouseListener.install();
        }
    }

    /**
     * Disables browser like links.
     */
    private void disableBrowserLikeLinks() {
        if (fMouseListener != null) {
            fMouseListener.uninstall();
            fMouseListener = null;
        }
    }

    /**
     * Handles a property change event describing a change
     * of the java core's preferences and updates the preference
     * related editor properties.
     * 
     * @param event the property change event
     */
    protected void handlePreferencePropertyChanged(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) {
        if (COMPILER_TASK_TAGS.equals(event.getProperty())) {
            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer != null && affectsTextPresentation(new PropertyChangeEvent(event.getSource(),
                    event.getProperty(), event.getOldValue(), event.getNewValue())))
                sourceViewer.invalidateTextPresentation();
        }
    }

    /**
     * Returns a segmentation of the line of the given viewer's input document appropriate for
     * bidi rendering. The default implementation returns only the string literals of a java code
     * line as segments.
     * 
     * @param viewer the text viewer
     * @param lineOffset the offset of the line
     * @return the line's bidi segmentation
     * @throws BadLocationException in case lineOffset is not valid in document
     */
    public static int[] getBidiLineSegments(ITextViewer viewer, int lineOffset) throws BadLocationException {

        IDocument document = viewer.getDocument();
        if (document == null)
            return null;

        IRegion line = document.getLineInformationOfOffset(lineOffset);
        ITypedRegion[] linePartitioning = TextUtilities.computePartitioning(document,
                IJavaPartitions.JAVA_PARTITIONING, lineOffset, line.getLength());

        List segmentation = new ArrayList();
        for (int i = 0; i < linePartitioning.length; i++) {
            if (IJavaPartitions.JAVA_STRING.equals(linePartitioning[i].getType()))
                segmentation.add(linePartitioning[i]);
        }

        if (segmentation.size() == 0)
            return null;

        int size = segmentation.size();
        int[] segments = new int[size * 2 + 1];

        int j = 0;
        for (int i = 0; i < size; i++) {
            ITypedRegion segment = (ITypedRegion) segmentation.get(i);

            if (i == 0)
                segments[j++] = 0;

            int offset = segment.getOffset() - lineOffset;
            if (offset > segments[j - 1])
                segments[j++] = offset;

            if (offset + segment.getLength() >= line.getLength())
                break;

            segments[j++] = offset + segment.getLength();
        }

        if (j < segments.length) {
            int[] result = new int[j];
            System.arraycopy(segments, 0, result, 0, j);
            segments = result;
        }

        return segments;
    }

    /**
     * Returns a segmentation of the given line appropriate for bidi rendering. The default
     * implementation returns only the string literals of a java code line as segments.
     * 
     * @param lineOffset the offset of the line
     * @param line the content of the line
     * @return the line's bidi segmentation
     */
    protected int[] getBidiLineSegments(int widgetLineOffset, String line) {
        if (line != null && line.length() > 0) {
            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer != null) {
                int lineOffset;
                if (sourceViewer instanceof ITextViewerExtension3) {
                    ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer;
                    lineOffset = extension.widgetOffset2ModelOffset(widgetLineOffset);
                } else {
                    IRegion visible = sourceViewer.getVisibleRegion();
                    lineOffset = visible.getOffset() + widgetLineOffset;
                }
                try {
                    return getBidiLineSegments(sourceViewer, lineOffset);
                } catch (BadLocationException x) {
                    // don't segment line in this case
                }
            }
        }
        return null;
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#updatePropertyDependentActions()
     */
    protected void updatePropertyDependentActions() {
        super.updatePropertyDependentActions();
        if (fEncodingSupport != null)
            fEncodingSupport.reset();
    }

    /*
     * Update the hovering behavior depending on the preferences.
     */
    private void updateHoverBehavior() {
        SourceViewerConfiguration configuration = getSourceViewerConfiguration();
        String[] types = configuration.getConfiguredContentTypes(getSourceViewer());

        for (int i = 0; i < types.length; i++) {

            String t = types[i];

            ISourceViewer sourceViewer = getSourceViewer();
            if (sourceViewer instanceof ITextViewerExtension2) {
                // Remove existing hovers         
                ((ITextViewerExtension2) sourceViewer).removeTextHovers(t);

                int[] stateMasks = configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t);

                if (stateMasks != null) {
                    for (int j = 0; j < stateMasks.length; j++) {
                        int stateMask = stateMasks[j];
                        ITextHover textHover = configuration.getTextHover(sourceViewer, t, stateMask);
                        ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, stateMask);
                    }
                } else {
                    ITextHover textHover = configuration.getTextHover(sourceViewer, t);
                    ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t,
                            ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
                }
            } else
                sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t);
        }
    }

    /*
     * @see org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput()
     */
    public Object getViewPartInput() {
        return getEditorInput().getAdapter(IJavaElement.class);
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetSelection(ISelection)
     */
    protected void doSetSelection(ISelection selection) {
        super.doSetSelection(selection);
        synchronizeOutlinePageSelection();
    }

    /*
     * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);

        Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
        preferences.addPropertyChangeListener(fPropertyChangeListener);

        IInformationControlCreator informationControlCreator = new IInformationControlCreator() {
            public IInformationControl createInformationControl(Shell shell) {
                boolean cutDown = false;
                int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL);
                return new DefaultInformationControl(shell, SWT.RESIZE, style, new HTMLTextPresenter(cutDown));
            }
        };

        fInformationPresenter = new InformationPresenter(informationControlCreator);
        fInformationPresenter.setSizeConstraints(60, 10, true, true);
        fInformationPresenter.install(getSourceViewer());

        if (PreferenceConstants.getPreferenceStore()
                .getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE)) {
            fEditorSelectionChangedListener = new EditorSelectionChangedListener();
            fEditorSelectionChangedListener.install(getSelectionProvider());
        }

        if (isBrowserLikeLinks())
            enableBrowserLikeLinks();

        if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_DISABLE_OVERWRITE_MODE))
            configureInsertMode(OVERWRITE, false);
    }

    protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {

        support.setCharacterPairMatcher(fBracketMatcher);
        support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR);

        super.configureSourceViewerDecorationSupport(support);
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#gotoMarker(org.eclipse.core.resources.IMarker)
     */
    public void gotoMarker(IMarker marker) {
        fLastMarkerTarget = marker;
        if (!fIsUpdatingAnnotationViews)
            super.gotoMarker(marker);
    }

    /**
     * Jumps to the next enabled annotation according to the given direction.
     * An annotation type is enabled if it is configured to be in the
     * Next/Previous tool bar drop down menu and if it is checked.
     * 
     * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
     */
    public void gotoAnnotation(boolean forward) {
        ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection();
        Position position = new Position(0, 0);
        if (false /* delayed - see bug 18316 */) {
            getNextAnnotation(selection.getOffset(), selection.getLength(), forward, position);
            selectAndReveal(position.getOffset(), position.getLength());
        } else /* no delay - see bug 18316 */ {
            Annotation annotation = getNextAnnotation(selection.getOffset(), selection.getLength(), forward,
                    position);
            setStatusLineErrorMessage(null);
            setStatusLineMessage(null);
            if (annotation != null) {
                updateAnnotationViews(annotation);
                selectAndReveal(position.getOffset(), position.getLength());
                if (annotation instanceof IJavaAnnotation && ((IJavaAnnotation) annotation).isProblem())
                    setStatusLineMessage(((IJavaAnnotation) annotation).getMessage());
            }
        }
    }

    /**
     * Updates the annotation views that show the given annotation.
     * 
     * @param annotation the annotation
     */
    private void updateAnnotationViews(Annotation annotation) {
        IMarker marker = null;
        if (annotation instanceof MarkerAnnotation)
            marker = ((MarkerAnnotation) annotation).getMarker();
        else if (annotation instanceof IJavaAnnotation) {
            Iterator e = ((IJavaAnnotation) annotation).getOverlaidIterator();
            if (e != null) {
                while (e.hasNext()) {
                    Object o = e.next();
                    if (o instanceof MarkerAnnotation) {
                        marker = ((MarkerAnnotation) o).getMarker();
                        break;
                    }
                }
            }
        }

        if (marker != null && !marker.equals(fLastMarkerTarget)) {
            try {
                boolean isProblem = marker.isSubtypeOf(IMarker.PROBLEM);
                IWorkbenchPage page = getSite().getPage();
                IViewPart view = page.findView(isProblem ? IPageLayout.ID_PROBLEM_VIEW : IPageLayout.ID_TASK_LIST); //$NON-NLS-1$  //$NON-NLS-2$
                if (view != null) {
                    Method method = view.getClass().getMethod("setSelection", //$NON-NLS-1$
                            new Class[] { IStructuredSelection.class, boolean.class });
                    method.invoke(view, new Object[] { new StructuredSelection(marker), Boolean.TRUE });
                }
            } catch (CoreException x) {
            } catch (NoSuchMethodException x) {
            } catch (IllegalAccessException x) {
            } catch (InvocationTargetException x) {
            }
            // ignore exceptions, don't update any of the lists, just set statusline
        }
    }

    /**
     * Finds and marks occurrence annotations.
     * 
     * @since 3.0
     */
    class OccurrencesFinder implements Runnable, IDocumentListener {

        private int fCount;
        private IDocument fDocument;
        private ITextSelection fSelection;
        private boolean fCancelled = false;

        public OccurrencesFinder(int count, IDocument document, ITextSelection selection) {
            fCount = count;
            fDocument = document;
            fSelection = selection;
            fDocument.addDocumentListener(this);
        }

        private boolean isCancelled() {
            return fCount != fComputeCount || fCancelled;
        }

        /*
         * @see java.lang.Runnable#run()
         */
        public void run() {

            try {

                if (isCancelled())
                    return;

                // Find occurrences
                FindOccurrencesEngine engine = FindOccurrencesEngine.create(getInputJavaElement());
                List matches = new ArrayList();
                try {
                    matches = engine.findOccurrences(fSelection.getOffset(), fSelection.getLength());
                } catch (JavaModelException e) {
                    JavaPlugin.log(e);
                    return;
                }

                if (matches == null || matches.isEmpty())
                    return;

                if (isCancelled())
                    return;

                removeOccurrenceAnnotations();

                if (isCancelled())
                    return;

                ITextViewer textViewer = getViewer();
                if (textViewer == null)
                    return;

                IDocument document = textViewer.getDocument();
                if (document == null)
                    return;

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

                IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput());
                if (annotationModel == null)
                    return;

                // Add occurrence annotations
                ArrayList annotations = new ArrayList();
                ArrayList positions = new ArrayList();
                for (Iterator each = matches.iterator(); each.hasNext();) {

                    if (isCancelled())
                        return;

                    ASTNode node = (ASTNode) each.next();
                    if (node == null)
                        continue;

                    String message;
                    // Create & add annotation
                    try {
                        message = document.get(node.getStartPosition(), node.getLength());
                    } catch (BadLocationException ex) {
                        // Skip this match
                        continue;
                    }
                    annotations.add(
                            new DefaultAnnotation(SearchUI.SEARCH_MARKER, IMarker.SEVERITY_INFO, true, message));
                    positions.add(new Position(node.getStartPosition(), node.getLength()));
                }

                if (isCancelled())
                    return;

                synchronized (annotationModel) {
                    fOccurrenceAnnotations = annotations;
                    for (int i = 0, size = annotations.size(); i < size; i++)
                        annotationModel.addAnnotation((Annotation) annotations.get(i), (Position) positions.get(i));
                }

            } finally {
                fDocument.removeDocumentListener(this);
            }
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
         */
        public void documentAboutToBeChanged(DocumentEvent event) {
            fCancelled = true;
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
         */
        public void documentChanged(DocumentEvent event) {
        }
    }

    /**
    * Updates the occurrences annotations based
    * on the current selection.
    *
    * @since 3.0
    */
    protected void updateOccurrences() {

        if (!fMarkOccurrenceAnnotations)
            return;

        IDocument document = getSourceViewer().getDocument();
        if (document == null)
            return;

        OccurrencesFinder finder = new OccurrencesFinder(++fComputeCount, document,
                (ITextSelection) getSelectionProvider().getSelection());
        Thread thread = new Thread(finder, "Occurrences Marker"); //$NON-NLS-1$
        thread.setDaemon(true);
        thread.start();
    }

    void removeOccurrenceAnnotations() {
        IDocumentProvider documentProvider = getDocumentProvider();
        if (documentProvider == null)
            return;

        IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput());
        if (annotationModel == null)
            return;

        synchronized (annotationModel) {
            for (int i = 0, size = fOccurrenceAnnotations.size(); i < size; i++)
                annotationModel.removeAnnotation((Annotation) fOccurrenceAnnotations.get(i));
            fOccurrenceAnnotations.clear();
        }
    }

    /**
     * Returns the Java element wrapped by this editors input.
     * 
     * @return the Java element wrapped by this editors input.
     * @since 3.0
     */
    abstract protected IJavaElement getInputJavaElement();

    protected void updateStatusLine() {
        ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection();
        Annotation annotation = getAnnotation(selection.getOffset(), selection.getLength());
        setStatusLineErrorMessage(null);
        setStatusLineMessage(null);
        if (annotation != null) {
            try {
                fIsUpdatingAnnotationViews = true;
                updateAnnotationViews(annotation);
            } finally {
                fIsUpdatingAnnotationViews = false;
            }
            if (annotation instanceof IJavaAnnotation && ((IJavaAnnotation) annotation).isProblem())
                setStatusLineMessage(((IJavaAnnotation) annotation).getMessage());
        }
    }

    /**
     * Jumps to the matching bracket.
     */
    public void gotoMatchingBracket() {

        ISourceViewer sourceViewer = getSourceViewer();
        IDocument document = sourceViewer.getDocument();
        if (document == null)
            return;

        IRegion selection = getSignedSelection(sourceViewer);

        int selectionLength = Math.abs(selection.getLength());
        if (selectionLength > 1) {
            setStatusLineErrorMessage(JavaEditorMessages.getString("GotoMatchingBracket.error.invalidSelection")); //$NON-NLS-1$      
            sourceViewer.getTextWidget().getDisplay().beep();
            return;
        }

        // #26314
        int sourceCaretOffset = selection.getOffset() + selection.getLength();
        if (isSurroundedByBrackets(document, sourceCaretOffset))
            sourceCaretOffset -= selection.getLength();

        IRegion region = fBracketMatcher.match(document, sourceCaretOffset);
        if (region == null) {
            setStatusLineErrorMessage(JavaEditorMessages.getString("GotoMatchingBracket.error.noMatchingBracket")); //$NON-NLS-1$      
            sourceViewer.getTextWidget().getDisplay().beep();
            return;
        }

        int offset = region.getOffset();
        int length = region.getLength();

        if (length < 1)
            return;

        int anchor = fBracketMatcher.getAnchor();
        // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195
        int targetOffset = (JavaPairMatcher.RIGHT == anchor) ? offset + 1 : offset + length;

        boolean visible = false;
        if (sourceViewer instanceof ITextViewerExtension3) {
            ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer;
            visible = (extension.modelOffset2WidgetOffset(targetOffset) > -1);
        } else {
            IRegion visibleRegion = sourceViewer.getVisibleRegion();
            // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195
            visible = (targetOffset >= visibleRegion.getOffset()
                    && targetOffset <= visibleRegion.getOffset() + visibleRegion.getLength());
        }

        if (!visible) {
            setStatusLineErrorMessage(
                    JavaEditorMessages.getString("GotoMatchingBracket.error.bracketOutsideSelectedElement")); //$NON-NLS-1$      
            sourceViewer.getTextWidget().getDisplay().beep();
            return;
        }

        if (selection.getLength() < 0)
            targetOffset -= selection.getLength();

        sourceViewer.setSelectedRange(targetOffset, selection.getLength());
        sourceViewer.revealRange(targetOffset, selection.getLength());
    }

    /**
     * Sets the given message as error message to this editor's status line.
     * 
     * @param msg message to be set
     */
    protected void setStatusLineErrorMessage(String msg) {
        IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
        if (statusLine != null)
            statusLine.setMessage(true, msg, null);
    }

    /**
     * Sets the given message as message to this editor's status line.
     * 
     * @param msg message to be set
     * @since 3.0
     */
    protected void setStatusLineMessage(String msg) {
        IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
        if (statusLine != null)
            statusLine.setMessage(false, msg, null);
    }

    private static IRegion getSignedSelection(ITextViewer viewer) {

        StyledText text = viewer.getTextWidget();
        int caretOffset = text.getCaretOffset();
        Point selection = text.getSelection();

        // caret left
        int offset, length;
        if (caretOffset == selection.x) {
            offset = selection.y;
            length = selection.x - selection.y;

            // caret right
        } else {
            offset = selection.x;
            length = selection.y - selection.x;
        }

        return new Region(offset, length);
    }

    private static boolean isBracket(char character) {
        for (int i = 0; i != BRACKETS.length; ++i)
            if (character == BRACKETS[i])
                return true;
        return false;
    }

    private static boolean isSurroundedByBrackets(IDocument document, int offset) {
        if (offset == 0 || offset == document.getLength())
            return false;

        try {
            return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset));

        } catch (BadLocationException e) {
            return false;
        }
    }

    /**
     * Returns the annotation closest to the given range respecting the given
     * direction. If an annotation is found, the annotations current position
     * is copied into the provided annotation position.
     * 
     * @param offset the region offset
     * @param length the region length
     * @param forward <code>true</code> for forwards, <code>false</code> for backward
     * @param annotationPosition the position of the found annotation
     * @return the found annotation
     */
    private Annotation getNextAnnotation(int offset, int length, boolean forward, Position annotationPosition) {

        Annotation nextAnnotation = null;
        Position nextAnnotationPosition = null;
        Annotation containingAnnotation = null;
        Position containingAnnotationPosition = null;
        boolean currentAnnotation = false;

        IDocument document = getDocumentProvider().getDocument(getEditorInput());
        int endOfDocument = document.getLength();
        int distance = 0;

        IAnnotationAccess access = getAnnotationAccess();

        IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput());
        Iterator e = new JavaAnnotationIterator(model, true, true);
        while (e.hasNext()) {
            Annotation a = (Annotation) e.next();
            Object type;
            if (a instanceof IJavaAnnotation)
                type = ((IJavaAnnotation) a).getAnnotationType();
            else
                type = access.getType(a);
            boolean isNavigationTarget = isNavigationTargetType(type);

            if ((a instanceof IJavaAnnotation) && ((IJavaAnnotation) a).hasOverlay() || !isNavigationTarget)
                continue;

            Position p = model.getPosition(a);
            if (p == null)
                continue;

            if (!(p.includes(offset) || (p.getLength() == 0 && offset == p.offset))) {

                int currentDistance = 0;

                if (forward) {
                    currentDistance = p.getOffset() - offset;
                    if (currentDistance < 0)
                        currentDistance = endOfDocument - offset + p.getOffset();
                } else {
                    currentDistance = offset - p.getOffset();
                    if (currentDistance < 0)
                        currentDistance = offset + endOfDocument - p.getOffset();
                }

                if (nextAnnotation == null || currentDistance < distance) {
                    distance = currentDistance;
                    nextAnnotation = a;
                    nextAnnotationPosition = p;
                }
            } else {
                if (containingAnnotationPosition == null || containingAnnotationPosition.length > p.length) {
                    containingAnnotation = a;
                    containingAnnotationPosition = p;
                    if (length == p.length)
                        currentAnnotation = true;
                }
            }
        }
        if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) {
            annotationPosition.setOffset(containingAnnotationPosition.getOffset());
            annotationPosition.setLength(containingAnnotationPosition.getLength());
            return containingAnnotation;
        }
        if (nextAnnotationPosition != null) {
            annotationPosition.setOffset(nextAnnotationPosition.getOffset());
            annotationPosition.setLength(nextAnnotationPosition.getLength());
        }

        return nextAnnotation;
    }

    /**
     * Returns the annotation overlapping with the given range or <code>null</code>.
     * 
     * @param offset the region offset
     * @param length the region length
     * @return the found annotation or <code>null</code>
     * @since 3.0
     */
    private Annotation getAnnotation(int offset, int length) {
        IAnnotationAccess access = getAnnotationAccess();
        IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput());
        Iterator e = new JavaAnnotationIterator(model, true, true);
        while (e.hasNext()) {
            Annotation a = (Annotation) e.next();
            if (a instanceof IJavaAnnotation) {
                IJavaAnnotation annotation = (IJavaAnnotation) a;
                if (annotation.hasOverlay() || !isNavigationTargetType(annotation.getAnnotationType()))
                    continue;
            } else if (!isNavigationTargetType(access.getType(a)))
                continue;

            Position p = model.getPosition(a);
            if (p != null && p.overlapsWith(offset, length))
                return a;
        }

        return null;
    }

    /**
     * Returns whether the given annotation type is configured as a target type
     * for the "Go to Next/Previous Annotation" actions
     * 
     * @param type the annotation type
     * @return <code>true</code> if this is a target type, <code>false</code>
     *            otherwise
     * @since 3.0
     */
    private boolean isNavigationTargetType(Object type) {
        Preferences preferences = Platform.getPlugin("org.eclipse.ui.workbench.texteditor").getPluginPreferences(); //$NON-NLS-1$
        Iterator i = getAnnotationPreferences().getAnnotationPreferences().iterator();
        while (i.hasNext()) {
            AnnotationPreference annotationPref = (AnnotationPreference) i.next();
            if (annotationPref.getAnnotationType().equals(type)) {
                //            See bug 41689
                //            String key= forward ? annotationPref.getIsGoToNextNavigationTargetKey() : annotationPref.getIsGoToPreviousNavigationTargetKey();
                String key = annotationPref.getIsGoToNextNavigationTargetKey();
                if (key != null && preferences.getBoolean(key))
                    return true;
            }
        }
        return false;
    }

    /**
     * Computes and returns the source reference that includes the caret and
     * serves as provider for the outline page selection and the editor range
     * indication.
     * 
     * @return the computed source reference
     * @since 3.0
     */
    protected ISourceReference computeHighlightRangeSourceReference() {
        ISourceViewer sourceViewer = getSourceViewer();
        if (sourceViewer == null)
            return null;

        StyledText styledText = sourceViewer.getTextWidget();
        if (styledText == null)
            return null;

        int caret = 0;
        if (sourceViewer instanceof ITextViewerExtension3) {
            ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer;
            caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset());
        } else {
            int offset = sourceViewer.getVisibleRegion().getOffset();
            caret = offset + styledText.getCaretOffset();
        }

        IJavaElement element = getElementAt(caret, false);

        if (!(element instanceof ISourceReference))
            return null;

        if (element.getElementType() == IJavaElement.IMPORT_DECLARATION) {

            IImportDeclaration declaration = (IImportDeclaration) element;
            IImportContainer container = (IImportContainer) declaration.getParent();
            ISourceRange srcRange = null;

            try {
                srcRange = container.getSourceRange();
            } catch (JavaModelException e) {
            }

            if (srcRange != null && srcRange.getOffset() == caret)
                return container;
        }

        return (ISourceReference) element;
    }

    /**
     * Returns the most narrow java element including the given offset.
     * 
     * @param offset the offset inside of the requested element
     * @param reconcile <code>true</code> if editor input should be reconciled in advance
     * @return the most narrow java element
     * @since 3.0
     */
    protected IJavaElement getElementAt(int offset, boolean reconcile) {
        return getElementAt(offset);
    }

    /*
     * @see org.eclipse.ui.texteditor.ExtendedTextEditor#createChangeHover()
     */
    protected LineChangeHover createChangeHover() {
        return new JavaChangeHover(IJavaPartitions.JAVA_PARTITIONING);
    }

    protected boolean isPrefQuickDiffAlwaysOn() {
        return false; // never show change ruler for the non-editable java editor. Overridden in subclasses like CompilationUnitEditor
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#createNavigationActions()
     */
    protected void createNavigationActions() {
        super.createNavigationActions();

        final StyledText textWidget = getSourceViewer().getTextWidget();

        IAction action = new SmartLineStartAction(textWidget, false);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
        setAction(ITextEditorActionDefinitionIds.LINE_START, action);

        action = new SmartLineStartAction(textWidget, true);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
        setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action);

        action = new NavigatePreviousSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS);
        setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL);

        action = new NavigateNextSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT);
        setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL);

        action = new DeletePreviousSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD);
        setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL);

        action = new DeleteNextSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD);
        setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL);

        action = new SelectPreviousSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS);
        setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL);

        action = new SelectNextSubWordAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT);
        setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action);
        textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL);
    }
}