/*******************************************************************************
 * Copyright (c) 2000, 2022 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Tom Eicher <eclipse@tom.eicher.name> - [formatting] 'Format Element' in JavaDoc does also format method body - https://bugs.eclipse.org/bugs/show_bug.cgi?id=238746
 *     Tom Eicher (Avaloq Evolution AG) - block selection mode
 *     Stefan Xenos (sxenos@gmail.com) - bug 306646, make editor margins follow the java formatter preference
 *     Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Update CodeMinings with IJavaReconcilingListener - Bug 530825
 *     Red Hat Inc. - Add raw paste functionality - Bug 522218
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.javaeditor;

import java.lang.StackWalker.StackFrame;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;

import java.text.BreakIterator;

import org.osgi.service.prefs.BackingStoreException;

import org.eclipse.help.IContextProvider;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IUndoContext;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ProjectScope;

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.MenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
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.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISelectionValidator;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension5;
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.codemining.ICodeMiningProvider;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationModelExtension2;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.jface.text.source.ISourceViewerExtension5;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.LineChangeHover;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;

import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.operations.NonLocalUndoUserApprover;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.TextNavigationAction;
import org.eclipse.ui.texteditor.TextOperationAction;

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

import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
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.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
import org.eclipse.jdt.core.util.IModifierConstants;

import org.eclipse.jdt.internal.core.manipulation.search.BreakContinueTargetFinder;
import org.eclipse.jdt.internal.core.manipulation.search.ExceptionOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder.OccurrenceLocation;
import org.eclipse.jdt.internal.core.manipulation.search.ImplementOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.MethodExitsFinder;
import org.eclipse.jdt.internal.core.manipulation.search.OccurrencesFinder;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;

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.text.IJavaPartitionerManager;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProvider;
import org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension;

import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
import org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction;
import org.eclipse.jdt.internal.ui.actions.FoldingActionGroup;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.EditorBreadcrumb;
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.IBreadcrumb;
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.text.DocumentCharacterIterator;
import org.eclipse.jdt.internal.ui.text.JavaChangeHover;
import org.eclipse.jdt.internal.ui.text.JavaPairMatcher;
import org.eclipse.jdt.internal.ui.text.JavaPartitionerManager;
import org.eclipse.jdt.internal.ui.text.JavaWordFinder;
import org.eclipse.jdt.internal.ui.text.JavaWordIterator;
import org.eclipse.jdt.internal.ui.text.PreferencesAdapter;
import org.eclipse.jdt.internal.ui.text.java.hover.JavaExpandHover;
import org.eclipse.jdt.internal.ui.text.java.hover.SourceViewerInformationControl;
import org.eclipse.jdt.internal.ui.util.ElementValidator;
import org.eclipse.jdt.internal.ui.util.JavaUIHelp;
import org.eclipse.jdt.internal.ui.viewsupport.ISelectionListenerWithAST;
import org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider;
import org.eclipse.jdt.internal.ui.viewsupport.SelectionListenerWithASTManager;


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

	/**
	 * Internal implementation class for a change listener.
	 * @since 3.0
	 */
	protected abstract static 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 the selection provider
		 */
		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 the selection provider
		 */
		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)
		 */
		@Override
		public void selectionChanged(SelectionChangedEvent event) {
			// XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161
			JavaEditor.this.selectionChanged();
		}
	}


	/**
	 * Adapts an options {@link IEclipsePreferences} to {@link org.eclipse.jface.preference.IPreferenceStore}.
	 * <p>
	 * This preference store is read-only i.e. write access
	 * throws an {@link java.lang.UnsupportedOperationException}.
	 * </p>
	 *
	 * @since 3.1
	 */
	private static class EclipsePreferencesAdapter implements IPreferenceStore {

		/**
		 * Preference change listener. Listens for events preferences
		 * fires a {@link org.eclipse.jface.util.PropertyChangeEvent}
		 * on this adapter with arguments from the received event.
		 */
		private class PreferenceChangeListener implements IEclipsePreferences.IPreferenceChangeListener {

			@Override
			public void preferenceChange(final IEclipsePreferences.PreferenceChangeEvent event) {
				if (Display.getCurrent() == null) {
					Display.getDefault().asyncExec(() -> firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue()));
				} else {
					firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue());
				}
			}
		}

		/** Listeners on on this adapter */
		private ListenerList<IPropertyChangeListener> fListeners= new ListenerList<>(ListenerList.IDENTITY);

		/** Listener on the node */
		private IEclipsePreferences.IPreferenceChangeListener fListener= new PreferenceChangeListener();

		/** wrapped node */
		private final IScopeContext fContext;
		private final String fQualifier;

		/**
		 * Initialize with the node to wrap
		 *
		 * @param context the context to access
		 * @param qualifier the qualifier
		 */
		public EclipsePreferencesAdapter(IScopeContext context, String qualifier) {
			fContext= context;
			fQualifier= qualifier;
		}

		private IEclipsePreferences getNode() {
			return fContext.getNode(fQualifier);
		}

		@Override
		public void addPropertyChangeListener(IPropertyChangeListener listener) {
			if (fListeners.size() == 0)
				getNode().addPreferenceChangeListener(fListener);
			fListeners.add(listener);
		}

		@Override
		public void removePropertyChangeListener(IPropertyChangeListener listener) {
			fListeners.remove(listener);
			if (fListeners.size() == 0) {
				getNode().removePreferenceChangeListener(fListener);
			}
		}

		@Override
		public boolean contains(String name) {
			return getNode().get(name, null) != null;
		}

		@Override
		public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) {
			PropertyChangeEvent event= new PropertyChangeEvent(this, name, oldValue, newValue);
			for (IPropertyChangeListener listener : fListeners) {
				listener.propertyChange(event);
			}
		}

		@Override
		public boolean getBoolean(String name) {
			return getNode().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT);
		}

		@Override
		public boolean getDefaultBoolean(String name) {
			return BOOLEAN_DEFAULT_DEFAULT;
		}

		@Override
		public double getDefaultDouble(String name) {
			return DOUBLE_DEFAULT_DEFAULT;
		}

		@Override
		public float getDefaultFloat(String name) {
			return FLOAT_DEFAULT_DEFAULT;
		}

		@Override
		public int getDefaultInt(String name) {
			return INT_DEFAULT_DEFAULT;
		}

		@Override
		public long getDefaultLong(String name) {
			return LONG_DEFAULT_DEFAULT;
		}

		@Override
		public String getDefaultString(String name) {
			return STRING_DEFAULT_DEFAULT;
		}

		@Override
		public double getDouble(String name) {
			return getNode().getDouble(name, DOUBLE_DEFAULT_DEFAULT);
		}

		@Override
		public float getFloat(String name) {
			return getNode().getFloat(name, FLOAT_DEFAULT_DEFAULT);
		}

		@Override
		public int getInt(String name) {
			return getNode().getInt(name, INT_DEFAULT_DEFAULT);
		}

		@Override
		public long getLong(String name) {
			return getNode().getLong(name, LONG_DEFAULT_DEFAULT);
		}

		@Override
		public String getString(String name) {
			return getNode().get(name, STRING_DEFAULT_DEFAULT);
		}

		@Override
		public boolean isDefault(String name) {
			return false;
		}

		@Override
		public boolean needsSaving() {
			try {
				return getNode().keys().length > 0;
			} catch (BackingStoreException e) {
				// ignore
			}
			return true;
		}

		@Override
		public void putValue(String name, String value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, double value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, float value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, int value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, long value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, String defaultObject) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setDefault(String name, boolean value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setToDefault(String name) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, double value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, float value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, int value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, long value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, String value) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setValue(String name, boolean value) {
			throw new UnsupportedOperationException();
		}

	}


	/**
	 * Cancels the occurrences finder job upon document changes.
	 *
	 * @since 3.0
	 */
	class OccurrencesFinderJobCanceler implements IDocumentListener, ITextInputListener {

		public void install() {
			ISourceViewer sourceViewer= getSourceViewer();
			if (sourceViewer == null)
				return;

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

			sourceViewer.addTextInputListener(this);

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

		public void uninstall() {
			ISourceViewer sourceViewer= getSourceViewer();
			if (sourceViewer != null)
				sourceViewer.removeTextInputListener(this);

			IDocumentProvider documentProvider= getDocumentProvider();
			if (documentProvider != null) {
				IDocument document= documentProvider.getDocument(getEditorInput());
				if (document != null)
					document.removeDocumentListener(this);
			}
		}


		/*
		 * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
		 */
		@Override
		public void documentAboutToBeChanged(DocumentEvent event) {
			if (fOccurrencesFinderJob != null)
				fOccurrencesFinderJob.doCancel();
		}

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

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

			oldInput.removeDocumentListener(this);
		}

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

	/**
	 * 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)
		 */
		@Override
		protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) {

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

			int index= super.getLineStartPosition(document, line, length, offset);
			if (IJavaPartitions.JAVA_DOC.equals(type) || IJavaPartitions.JAVA_MULTI_LINE_COMMENT.equals(type)) {
				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 + 1) == '/') {
					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 {

		protected JavaWordIterator fIterator= new JavaWordIterator();

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

		/*
		 * @see org.eclipse.jface.action.IAction#run()
		 */
		@Override
		public void run() {
			// Check whether we are in a java code partition and the preference is enabled
			final IPreferenceStore store= getPreferenceStore();
			if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) {
				super.run();
				return;
			}

			final ISourceViewer viewer= getSourceViewer();
			final IDocument document= viewer.getDocument();
			try {
				fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
				int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
				if (position == -1)
					return;

				int next= findNextPosition(position);
				if (isBlockSelectionModeEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) {
					super.run(); // may navigate into virtual white space
				} else if (next != BreakIterator.DONE) {
					setCaretPosition(next);
					getTextWidget().showSelection();
					fireSelectionChanged();
				}
			} catch (BadLocationException x) {
				// ignore
			}
		}

		/**
		 * Finds the next position after the given position.
		 *
		 * @param position the current position
		 * @return the next position
		 */
		protected int findNextPosition(int position) {
			ISourceViewer viewer= getSourceViewer();
			int widget= -1;
			int next= position;
			while (next != BreakIterator.DONE && widget == -1) { // XXX: optimize
				next= fIterator.following(next);
				if (next != BreakIterator.DONE)
					widget= modelOffset2WidgetOffset(viewer, next);
			}

			IDocument document= viewer.getDocument();
			LinkedModeModel model= LinkedModeModel.getModel(document, position);
			if (model != null && next != BreakIterator.DONE) {
				LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
				if (linkedPosition != null) {
					int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
					if (position != linkedPositionEnd && linkedPositionEnd < next)
						next= linkedPositionEnd;
				} else {
					LinkedPosition nextLinkedPosition= model.findPosition(new LinkedPosition(document, next, 0));
					if (nextLinkedPosition != null) {
						int nextLinkedPositionOffset= nextLinkedPosition.getOffset();
						if (position != nextLinkedPositionOffset && nextLinkedPositionOffset < next)
							next= nextLinkedPositionOffset;
					}
				}
			}

			return next;
		}

		/**
		 * 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)
		 */
		@Override
		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 implements IUpdate {

		/**
		 * 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)
		 */
		@Override
		protected void setCaretPosition(final int position) {
			if (!validateEditorInputState())
				return;

			final ISourceViewer viewer= getSourceViewer();
			StyledText text= viewer.getTextWidget();
			Point widgetSelection= text.getSelection();
			if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
				final int caret= text.getCaretOffset();
				final int offset= modelOffset2WidgetOffset(viewer, position);

				if (caret == widgetSelection.x)
					text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
				else
					text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
				text.invokeAction(ST.DELETE_NEXT);
			} else {
				Point selection= viewer.getSelectedRange();
				final int caret, length;
				if (selection.y != 0) {
					caret= selection.x;
					length= selection.y;
				} else {
					caret= widgetOffset2ModelOffset(viewer, text.getCaretOffset());
					length= position - caret;
				}

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

		/*
		 * @see org.eclipse.ui.texteditor.IUpdate#update()
		 */
		@Override
		public void update() {
			setEnabled(isEditorInputModifiable());
		}
	}

	/**
	 * 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)
		 */
		@Override
		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 {

		protected JavaWordIterator fIterator= new JavaWordIterator();

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

		/*
		 * @see org.eclipse.jface.action.IAction#run()
		 */
		@Override
		public void run() {
			// Check whether we are in a java code partition and the preference is enabled
			final IPreferenceStore store= getPreferenceStore();
			if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) {
				super.run();
				return;
			}

			final ISourceViewer viewer= getSourceViewer();
			final IDocument document= viewer.getDocument();
			try {
				fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
				int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
				if (position == -1)
					return;

				int previous= findPreviousPosition(position);
				if (isBlockSelectionModeEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) {
					super.run(); // may navigate into virtual white space
				} else if (previous != BreakIterator.DONE) {
					setCaretPosition(previous);
					getTextWidget().showSelection();
					fireSelectionChanged();
				}
			} catch (BadLocationException x) {
				// ignore - getLineOfOffset failed
			}

		}

		/**
		 * Finds the previous position before the given position.
		 *
		 * @param position the current position
		 * @return the previous position
		 */
		protected int findPreviousPosition(int position) {
			ISourceViewer viewer= getSourceViewer();
			int widget= -1;
			int previous= position;
			while (previous != BreakIterator.DONE && widget == -1) { // XXX: optimize
				previous= fIterator.preceding(previous);
				if (previous != BreakIterator.DONE)
					widget= modelOffset2WidgetOffset(viewer, previous);
			}

			IDocument document= viewer.getDocument();
			LinkedModeModel model= LinkedModeModel.getModel(document, position);
			if (model != null && previous != BreakIterator.DONE) {
				LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
				if (linkedPosition != null) {
					int linkedPositionOffset= linkedPosition.getOffset();
					if (position != linkedPositionOffset && previous < linkedPositionOffset)
						previous= linkedPositionOffset;
				} else {
					LinkedPosition previousLinkedPosition= model.findPosition(new LinkedPosition(document, previous, 0));
					if (previousLinkedPosition != null) {
						int previousLinkedPositionEnd= previousLinkedPosition.getOffset() + previousLinkedPosition.getLength();
						if (position != previousLinkedPositionEnd && previous < previousLinkedPositionEnd)
							previous= previousLinkedPositionEnd;
					}
				}
			}

			return previous;
		}

		/**
		 * 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)
		 */
		@Override
		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 implements IUpdate {

		/**
		 * 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)
		 */
		@Override
		protected void setCaretPosition(int position) {
			if (!validateEditorInputState())
				return;

			final int length;
			final ISourceViewer viewer= getSourceViewer();
			StyledText text= viewer.getTextWidget();
			Point widgetSelection= text.getSelection();
			if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
				final int caret= text.getCaretOffset();
				final int offset= modelOffset2WidgetOffset(viewer, position);

				if (caret == widgetSelection.x)
					text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
				else
					text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
				text.invokeAction(ST.DELETE_PREVIOUS);
			} else {
				Point selection= viewer.getSelectedRange();
				if (selection.y != 0) {
					position= selection.x;
					length= selection.y;
				} else {
					length= widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position;
				}

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

		/*
		 * @see org.eclipse.ui.texteditor.IUpdate#update()
		 */
		@Override
		public void update() {
			setEnabled(isEditorInputModifiable());
		}
	}

	/**
	 * 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)
		 */
		@Override
		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);
			}
		}
	}

	/**
	 * Format element action to format the enclosing java element.
	 * <p>
	 * The format element 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 there is no selection and the caret is positioned within a comment
	 * (javadoc, multi-line or single-line), then only the comment is formatted, but
	 * not its enclosing Java element.</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 FormatElementAction extends Action implements IUpdate {

		/*
		 * @since 3.2
		 */
		FormatElementAction() {
			setText(JavaEditorMessages.JavaEditor_FormatElementAction_label);
			setDescription(JavaEditorMessages.JavaEditor_FormatElementAction_description);
			setEnabled(isEditorInputModifiable());
		}

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

			final JavaSourceViewer viewer= (JavaSourceViewer) getSourceViewer();
			if (viewer.isEditable() && ElementValidator.check(getInputJavaElement(), getSite().getShell(), JavaEditorMessages.JavaEditor_FormatElementDialog_label, true)) {

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

					boolean emptySelection= selection.y == 0;
					if (emptySelection) {
						final ITypedRegion partition= TextUtilities.getPartition(viewer.getDocument(), IJavaPartitions.JAVA_PARTITIONING, selection.x, true);
						String type= partition.getType();
						if (IJavaPartitions.JAVA_DOC.equals(type) || IJavaPartitions.JAVA_MULTI_LINE_COMMENT.equals(type) || IJavaPartitions.JAVA_SINGLE_LINE_COMMENT.equals(type)) {
							viewer.setSelectedRange(partition.getOffset(), partition.getLength());
							viewer.doOperation(ISourceViewer.FORMAT);
							return;
						}
					}
					final IJavaElement element= getElementAt(selection.x, true);
					if (element != null && element.exists()) {
						try {
							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();
								final ISourceRange nameRange= reference.getNameRange();
								final boolean seletionInNameRange= nameRange != null && selection.x >= nameRange.getOffset()
										&& selection.x + selection.y <= nameRange.getOffset() + nameRange.getLength();
								if (range != null && (emptySelection || seletionInNameRange))
									viewer.setSelectedRange(range.getOffset(), range.getLength());
							}
						} catch (JavaModelException exception) {
							// Should not happen
						}
					}
					viewer.doOperation(ISourceViewer.FORMAT);
				} catch (BadLocationException e) {
					// Cannot happen
				} finally {

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

		/*
		 * @see org.eclipse.ui.texteditor.IUpdate#update()
		 * @since 3.2
		 */
		@Override
		public void update() {
			setEnabled(isEditorInputModifiable());
		}
	}

	/**
	 * Internal activation listener.
	 * @since 3.0
	 */
	private class ActivationListener implements IWindowListener {

		/*
		 * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
		 * @since 3.1
		 */
		@Override
		public void windowActivated(IWorkbenchWindow window) {
			if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) {
				fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection();
				ITypeRoot inputJavaElement= getInputJavaElement();
				if (inputJavaElement != null) {
					IProgressMonitor monitor = getProgressMonitor();
					try {
						updateOccurrenceAnnotations((ITextSelection)fForcedMarkOccurrencesSelection, SharedASTProviderCore.getAST(inputJavaElement, SharedASTProviderCore.WAIT_NO, monitor));
					} finally {
						monitor.done();
					}
				}
			}
		}

		/*
		 * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
		 * @since 3.1
		 */
		@Override
		public void windowDeactivated(IWorkbenchWindow window) {
			if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart())
				removeOccurrenceAnnotations();
		}

		/*
		 * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
		 * @since 3.1
		 */
		@Override
		public void windowClosed(IWorkbenchWindow window) {
		}

		/*
		 * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
		 * @since 3.1
		 */
		@Override
		public void windowOpened(IWorkbenchWindow window) {
		}
	}

	/**
	 * Runner that will toggle folding either instantly (if the editor is
	 * visible) or the next time it becomes visible. If a runner is started when
	 * there is already one registered, the registered one is canceled as
	 * toggling folding twice is a no-op.
	 * <p>
	 * The access to the fFoldingRunner field is not thread-safe, it is assumed
	 * that <code>runWhenNextVisible</code> is only called from the UI thread.
	 * </p>
	 *
	 * @since 3.1
	 */
	private final class ToggleFoldingRunner implements IPartListener2 {
		/**
		 * The workbench page we registered the part listener with, or
		 * <code>null</code>.
		 */
		private IWorkbenchPage fPage;

		/**
		 * Does the actual toggling of projection.
		 */
		private void toggleFolding() {
			ISourceViewer sourceViewer= getSourceViewer();
			if (sourceViewer instanceof ProjectionViewer) {
				ProjectionViewer pv= (ProjectionViewer) sourceViewer;
				if (pv.isProjectionMode() != isFoldingEnabled()) {
					if (pv.canDoOperation(ProjectionViewer.TOGGLE))
						pv.doOperation(ProjectionViewer.TOGGLE);
				}
			}
		}

		/**
		 * Makes sure that the editor's folding state is correct the next time
		 * it becomes visible. If it already is visible, it toggles the folding
		 * state. If not, it either registers a part listener to toggle folding
		 * when the editor becomes visible, or cancels an already registered
		 * runner.
		 */
		public void runWhenNextVisible() {
			// if there is one already: toggling twice is the identity
			if (fFoldingRunner != null) {
				fFoldingRunner.cancel();
				return;
			}
			IWorkbenchPartSite site= getSite();
			if (site != null) {
				IWorkbenchPage page= site.getPage();
				if (!page.isPartVisible(JavaEditor.this)) {
					// if we're not visible - defer until visible
					fPage= page;
					fFoldingRunner= this;
					page.addPartListener(this);
					return;
				}
			}
			// we're visible - run now
			toggleFolding();
		}

		/**
		 * Remove the listener and clear the field.
		 */
		private void cancel() {
			if (fPage != null) {
				fPage.removePartListener(this);
				fPage= null;
			}
			if (fFoldingRunner == this)
				fFoldingRunner= null;
		}

		/*
		 * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
		 */
		@Override
		public void partVisible(IWorkbenchPartReference partRef) {
			if (JavaEditor.this.equals(partRef.getPart(false))) {
				cancel();
				toggleFolding();
			}
		}

		/*
		 * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
		 */
		@Override
		public void partClosed(IWorkbenchPartReference partRef) {
			if (JavaEditor.this.equals(partRef.getPart(false))) {
				cancel();
			}
		}

		@Override
		public void partActivated(IWorkbenchPartReference partRef) {}
		@Override
		public void partBroughtToTop(IWorkbenchPartReference partRef) {}
		@Override
		public void partDeactivated(IWorkbenchPartReference partRef) {}
		@Override
		public void partOpened(IWorkbenchPartReference partRef) {}
		@Override
		public void partHidden(IWorkbenchPartReference partRef) {}
		@Override
		public void partInputChanged(IWorkbenchPartReference partRef) {}
	}


	/**
	 * Editor specific selection provider which wraps the source viewer's selection provider.
	 * @since 3.4
	 */
	class JdtSelectionProvider extends SelectionProvider {

		private ListenerList<ISelectionChangedListener> fSelectionListeners= new ListenerList<>();
		private ListenerList<ISelectionChangedListener> fPostSelectionListeners= new ListenerList<>();
		private ITextSelection fInvalidSelection;
		private ISelection fValidSelection;

		/*
		 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
		 */
		@Override
		public void addSelectionChangedListener(ISelectionChangedListener listener) {
			super.addSelectionChangedListener(listener);
			if (getSourceViewer() != null)
				fSelectionListeners.add(listener);
		}

		/*
		 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
		 */
		@Override
		public ISelection getSelection() {
			if (fInvalidSelection != null)
				return fInvalidSelection;
			return super.getSelection();
		}

		/*
		 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
		 */
		@Override
		public void removeSelectionChangedListener(ISelectionChangedListener listener) {
			super.removeSelectionChangedListener(listener);
			if (getSourceViewer() != null)
				fSelectionListeners.remove(listener);
		}

		/*
		 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection)
		 */
		@Override
		public void setSelection(ISelection selection) {
			if (selection instanceof ITextSelection) {
				if (fInvalidSelection != null) {
					fInvalidSelection= null;

					ITextSelection newSelection= (ITextSelection) selection;
					ITextSelection oldSelection= (ITextSelection) getSelection();

					if (newSelection.getOffset() == oldSelection.getOffset() && newSelection.getLength() == oldSelection.getLength()) {
						markValid();
					} else {
						super.setSelection(selection);
					}
				} else {
					super.setSelection(selection);
				}
			} else if (selection instanceof IStructuredSelection && ((IStructuredSelection) selection).getFirstElement() instanceof EditorBreadcrumb) {
				markInvalid();
			}
		}

		/*
		 * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
		 */
		@Override
		public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
			super.addPostSelectionChangedListener(listener);
			if (getSourceViewer() != null && getSourceViewer().getSelectionProvider() instanceof IPostSelectionProvider)
				fPostSelectionListeners.add(listener);
		}

		/*
		 * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
		 */
		@Override
		public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
			super.removePostSelectionChangedListener(listener);
			if (getSourceViewer() != null)
				fPostSelectionListeners.remove(listener);
		}

		/*
		 * @see org.eclipse.jface.text.IPostSelectionValidator#isValid()
		 */
		@Override
		public boolean isValid(ISelection postSelection) {
			return fInvalidSelection == null && super.isValid(postSelection);
		}

		/**
		 * Marks this selection provider as currently being invalid. An invalid
		 * selection is one which can not be selected in the source viewer.
		 */
		private void markInvalid() {
			fValidSelection= getSelection();
			fInvalidSelection= new TextSelection(0, 0);

			SelectionChangedEvent event= new SelectionChangedEvent(this, fInvalidSelection);

			for (ISelectionChangedListener listener : fSelectionListeners) {
				listener.selectionChanged(event);
			}

			for (ISelectionChangedListener listener : fPostSelectionListeners) {
				listener.selectionChanged(event);
			}
		}

		/**
		 * Marks this selection provider as being valid.
		 */
		private void markValid() {
			fInvalidSelection= null;

			SelectionChangedEvent event= new SelectionChangedEvent(this, fValidSelection);

			for (ISelectionChangedListener listener : fSelectionListeners) {
				listener.selectionChanged(event);
			}

			for (ISelectionChangedListener listener : fPostSelectionListeners) {
				listener.selectionChanged(event);
			}
		}
	}



	/** Preference key for matching brackets. */
	protected final static String MATCHING_BRACKETS=  PreferenceConstants.EDITOR_MATCHING_BRACKETS;

	/**
	 * Preference key for highlighting bracket at caret location.
	 *
	 * @since 3.8
	 */
	protected final static String HIGHLIGHT_BRACKET_AT_CARET_LOCATION= PreferenceConstants.EDITOR_HIGHLIGHT_BRACKET_AT_CARET_LOCATION;

	/**
	 * Preference key for enclosing brackets.
	 *
	 * @since 3.8
	 */
	protected final static String ENCLOSING_BRACKETS= PreferenceConstants.EDITOR_ENCLOSING_BRACKETS;

	/** Preference key for matching brackets color. */
	protected final static String MATCHING_BRACKETS_COLOR=  PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR;
	/**
	 * A named preference that controls whether the editor shows a breadcrumb.
	 * <p>
	 * Value is of type <code>Boolean</code>.
	 * </p>
	 *
	 * @since 3.4
	 */
	public static final String EDITOR_SHOW_BREADCRUMB= "breadcrumb"; //$NON-NLS-1$


	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;

	/**
	 * DO NOT REMOVE, used in a product.
	 * @deprecated As of 3.5
	 */
	@Deprecated
	protected AbstractSelectionChangedListener fOutlineSelectionChangedListener= new AbstractSelectionChangedListener() {
		@Override
		public void selectionChanged(SelectionChangedEvent event) {
		}
		@Override
		public void install(ISelectionProvider selectionProvider) {
		}
	};

	/** The editor's bracket matcher */
	protected JavaPairMatcher fBracketMatcher= new JavaPairMatcher(BRACKETS);
	/** This editor's encoding support */
	private DefaultEncodingSupport fEncodingSupport;
	/** History for structure select action */
	private SelectionHistory fSelectionHistory;
	protected CompositeActionGroup fActionGroups;

	/**
	 * The action group for folding.
	 *
	 * @since 3.0
	 */
	private FoldingActionGroup fFoldingGroup;

	private CompositeActionGroup fContextMenuGroup;
	/**
	 * Holds the current occurrence annotations.
	 * @since 3.0
	 */
	private Annotation[] fOccurrenceAnnotations= null;
	/**
	 * Tells whether all occurrences of the element at the
	 * current caret location are automatically marked in
	 * this editor.
	 * @since 3.0
	 */
	private boolean fMarkOccurrenceAnnotations;
	/**
	 * Tells whether the editor should show a breadcrumb.
	 * The breadcrumb shows the parent chain of the active
	 * editor element.
	 * @since 3.4
	 */
	private boolean fIsBreadcrumbVisible;
	/**
	 * Tells whether the occurrence annotations are sticky
	 * i.e. whether they stay even if there's no valid Java
	 * element at the current caret position.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fStickyOccurrenceAnnotations;
	/**
	 * Tells whether to mark type occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkTypeOccurrences;
	/**
	 * Tells whether to mark method occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkMethodOccurrences;
	/**
	 * Tells whether to mark constant occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkConstantOccurrences;
	/**
	 * Tells whether to mark field occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkFieldOccurrences;
	/**
	 * Tells whether to mark local variable occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkLocalVariableypeOccurrences;
	/**
	 * Tells whether to mark exception occurrences in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkExceptions;
	/**
	 * Tells whether to mark method exits in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.0
	 */
	private boolean fMarkMethodExitPoints;

	/**
	 * Tells whether to mark targets of <code>break</code> and <code>continue</code> statements in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.2
	 */
	private boolean fMarkBreakContinueTargets;

	/**
	 * Tells whether to mark implementors in this editor.
	 * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>.
	 * @since 3.1
	 */
	private boolean fMarkImplementors;
	/**
	 * The selection used when forcing occurrence marking
	 * through code.
	 * @since 3.0
	 */
	private ISelection fForcedMarkOccurrencesSelection;
	/**
	 * The document modification stamp at the time when the last
	 * occurrence marking took place.
	 * @since 3.1
	 */
	private long fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
	/**
	 * The region of the word under the caret used to when
	 * computing the current occurrence markings.
	 * @since 3.1
	 */
	private IRegion fMarkOccurrenceTargetRegion;

	/**
	 * The internal shell activation listener for updating occurrences.
	 * @since 3.0
	 */
	private ActivationListener fActivationListener= new ActivationListener();
	private ISelectionListenerWithAST fPostSelectionListenerWithAST;
	private OccurrencesFinderJob fOccurrencesFinderJob;
	/** The occurrences finder job canceler */
	private OccurrencesFinderJobCanceler fOccurrencesFinderJobCanceler;
	/**
	 * This editor's projection support
	 * @since 3.0
	 */
	private ProjectionSupport fProjectionSupport;
	/**
	 * This editor's projection model updater
	 * @since 3.0
	 */
	private IJavaFoldingStructureProvider fProjectionModelUpdater;
	/**
	 * The override and implements indicator manager for this editor.
	 * @since 3.0
	 */
	protected OverrideIndicatorManager fOverrideIndicatorManager;
	/**
	 * Semantic highlighting manager
	 * @since 3.0, protected as of 3.3
	 */
	protected SemanticHighlightingManager fSemanticManager;
	/**
	 * The folding runner.
	 * @since 3.1
	 */
	private ToggleFoldingRunner fFoldingRunner;

	/**
	 * Tells whether the selection changed event is caused
	 * by a call to {@link #gotoAnnotation(boolean)}.
	 *
	 * @since 3.2
	 */
	private boolean fSelectionChangedViaGotoAnnotation;
	/**
	 * The cached selected range.
	 *
	 * @see ITextViewer#getSelectedRange()
	 * @since 3.3
	 */
	private Point fCachedSelectedRange;

	/**
	 * The editor breadcrumb.
	 *
	 * @since 3.4
	 */
	private IBreadcrumb fBreadcrumb;

	/**
	 * The composite containing the breadcrumb.
	 *
	 * @since 3.4
	 */
	private Composite fBreadcrumbComposite;

	/**
	 * This editor's selection provider.
	 * @since 3.4
	 */
	private SelectionProvider fSelectionProvider= new JdtSelectionProvider();

	/**
	 * Time when last error message got set.
	 *
	 * @since 3.5
	 */
	private long fErrorMessageTime;

	/**
	 * Timeout for the error message.
	 *
	 * @since 3.5
	 */
	private static final long ERROR_MESSAGE_TIMEOUT= 1000;

	/**
	 * Previous location history for goto matching bracket action.
	 *
	 * @since 3.8
	 */
	private List<IRegion> fPreviousSelections;

	/**
	 * Java code mining manager
	 *
	 * @since 3.14
	 */
	private JavaCodeMiningManager fJavaCodeMiningManager;

	/**
	 * 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.
	 *
	 * @param element the java element
	 * @return the corresponding Java element
	 */
	abstract protected IJavaElement getCorrespondingElement(IJavaElement element);


	/**
	 * Sets the input of the editor's outline page.
	 *
	 * @param page the Java outline page
	 * @param input the editor input
	 */
	protected void setOutlinePageInput(JavaOutlinePage page, IEditorInput input) {
		if (page == null)
			return;

		IJavaElement je= getInputJavaElement();
		if (je != null && je.exists())
			page.setInput(je);
		else
			page.setInput(null);

	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#initializeKeyBindingScopes()
	 */
	@Override
	protected void initializeKeyBindingScopes() {
		setKeyBindingScopes(new String[] { "org.eclipse.jdt.ui.javaEditorScope" });  //$NON-NLS-1$
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#initializeEditor()
	 */
	@Override
	protected void initializeEditor() {
	}

	/**
	 * Returns a new Java source viewer configuration.
	 *
	 * @return a new <code>JavaSourceViewerConfiguration</code>
	 * @since 3.3
	 */
	protected JavaSourceViewerConfiguration createJavaSourceViewerConfiguration() {
		JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
		return new JavaSourceViewerConfiguration(textTools.getColorManager(), getPreferenceStore(), this, IJavaPartitions.JAVA_PARTITIONING);
	}

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

		Composite composite= new Composite(parent, SWT.NONE);
		GridLayout layout= new GridLayout(1, false);
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		layout.horizontalSpacing= 0;
		layout.verticalSpacing= 0;
		composite.setLayout(layout);

		fBreadcrumbComposite= new Composite(composite, SWT.NONE);
		GridData layoutData= new GridData(SWT.FILL, SWT.TOP, true, false);
		fBreadcrumbComposite.setLayoutData(layoutData);
		layout= new GridLayout(1, false);
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		layout.horizontalSpacing= 0;
		layout.verticalSpacing= 0;
		layoutData.exclude= true;
		fBreadcrumbComposite.setLayout(layout);

		Composite editorComposite= new Composite(composite, SWT.NONE);
		editorComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		FillLayout fillLayout= new FillLayout(SWT.VERTICAL);
		fillLayout.marginHeight= 0;
		fillLayout.marginWidth= 0;
		fillLayout.spacing= 0;
		editorComposite.setLayout(fillLayout);

		IPreferenceStore store= getPreferenceStore();
		ISourceViewer sourceViewer= createJavaSourceViewer(editorComposite, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles, store);

		JavaUIHelp.setHelp(this, sourceViewer.getTextWidget(), IJavaHelpContextIds.JAVA_EDITOR);

		/*
		 * This is a performance optimization to reduce the computation of
		 * the text presentation triggered by {@link #setVisibleDocument(IDocument)}
		 */
		if (sourceViewer instanceof JavaSourceViewer && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS)))
			((JavaSourceViewer)sourceViewer).prepareDelayedProjection();

		if (sourceViewer instanceof ProjectionViewer) {
			fProjectionSupport= new ProjectionSupport((ProjectionViewer)sourceViewer, getAnnotationAccess(), getSharedColors());
			MarkerAnnotationPreferences markerAnnotationPreferences= getAdapter(MarkerAnnotationPreferences.class);
			if (markerAnnotationPreferences != null) {
				Iterator<AnnotationPreference> e= markerAnnotationPreferences.getAnnotationPreferences().iterator();
				while (e.hasNext()) {
					AnnotationPreference annotationPreference= e.next();
					Object annotationType= annotationPreference.getAnnotationType();
					if (annotationType instanceof String)
						fProjectionSupport.addSummarizableAnnotationType((String)annotationType);
				}
			} else {
				fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
				fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
			}
			fProjectionSupport.setHoverControlCreator(shell -> new SourceViewerInformationControl(shell, false, getOrientation(), EditorsUI.getTooltipAffordanceString()));
			fProjectionSupport.setInformationPresenterControlCreator(shell -> new SourceViewerInformationControl(shell, true, getOrientation(), null));
			fProjectionSupport.install();

			fProjectionModelUpdater= JavaPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider();
			if (fProjectionModelUpdater != null)
				fProjectionModelUpdater.install(this, (ProjectionViewer)sourceViewer);
		}

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

		return sourceViewer;
	}

	/**
	 * Creates the breadcrumb to be used by this editor.
	 * Returns <code>null</code> if this editor can not show a breadcrumb.
	 *
	 * @return the breadcrumb or <code>null</code>
	 * @since 3.4
	 */
	protected IBreadcrumb createBreadcrumb() {
		return new JavaEditorBreadcrumb(this);
	}

	/**
	 * @return the breadcrumb used by this viewer if any.
	 * @since 3.4
	 */
	public IBreadcrumb getBreadcrumb() {
		return fBreadcrumb;
	}

	/**
	 * Returns the preference key for the breadcrumb. The
	 * value depends on the current perspective.
	 *
	 * @return the preference key or <code>null</code> if there's no perspective
	 * @since 3.4
	 */
	String getBreadcrumbPreferenceKey() {
		IPerspectiveDescriptor perspective= getSite().getPage().getPerspective();
		if (perspective == null)
			return null;
		return JavaEditor.EDITOR_SHOW_BREADCRUMB + "." + perspective.getId(); //$NON-NLS-1$
	}

	/**
	 * Returns true if the breadcrumb is active. If true
	 * then the breadcrumb has the focus if this part
	 * is the active part.
	 *
	 * @return true if the breadcrumb is active.
	 * @since 3.4
	 */
	public boolean isBreadcrumbActive() {
		return fBreadcrumb != null && fBreadcrumb.isActive();
	}

	/**
	 * Makes the breadcrumb visible. Creates its content
	 * if this is the first time it is made visible.
	 *
	 * @since 3.4
	 */
	private void showBreadcrumb() {
		if (fBreadcrumb == null)
			return;

		if (fBreadcrumbComposite.isDisposed()) {
			// not expected
			SWT.error(SWT.ERROR_WIDGET_DISPOSED, null,
					". Editor not properly disposed; please report scenario to <https://bugs.eclipse.org/446203>. Editor input: " //$NON-NLS-1$
							+ getInputJavaElement());
		}
		if (fBreadcrumbComposite.getChildren().length == 0) {
			fBreadcrumb.createContent(fBreadcrumbComposite);
		}

		((GridData) fBreadcrumbComposite.getLayoutData()).exclude= false;
		fBreadcrumbComposite.setVisible(true);

		ISourceReference selection= computeHighlightRangeSourceReference();
		if (selection == null)
			selection= getInputJavaElement();
		setBreadcrumbInput(selection);
		fBreadcrumbComposite.getParent().layout(true, true);
	}

	/**
	 * Hides the breadcrumb
	 *
	 * @since 3.4
	 */
	private void hideBreadcrumb() {
		if (fBreadcrumb == null)
			return;
		((GridData) fBreadcrumbComposite.getLayoutData()).exclude= true;
		fBreadcrumbComposite.setVisible(false);
		fBreadcrumbComposite.getParent().layout(true, true);
	}

	/**
	 * Sets the breadcrumb input to the given element.
	 * @param element the element to use as input for the breadcrumb
	 * @since 3.4
	 */
	private void setBreadcrumbInput(ISourceReference element) {
		if (fBreadcrumb == null)
			return;

		fBreadcrumb.setInput(element);
	}

	/**
	 * Returns the editor's source viewer. May return <code>null</code> before
	 * the editor's part has been created and after disposal.
	 *
	 * @return the editor's source viewer, which may be <code>null</code>
	 */
	public final ISourceViewer getViewer() {
		return getSourceViewer();
	}

	/**
	 * Creates the Java source viewer to be used by this editor.
	 * Subclasses may re-implement this method.
	 *
	 * @param parent the parent control
	 * @param verticalRuler the vertical ruler
	 * @param overviewRuler the overview ruler
	 * @param isOverviewRulerVisible <code>true</code> if the overview ruler is visible
	 * @param styles style bits, <code>SWT.WRAP</code> is currently not supported
	 * @param store the preference store
	 * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
	 * @return the source viewer
	 */
	protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles, IPreferenceStore store) {
		return new JavaSourceViewer(parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles, store);
	}

	/*
	 * @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
	 */
	@Override
	protected boolean affectsTextPresentation(PropertyChangeEvent event) {
		return ((JavaSourceViewerConfiguration)getSourceViewerConfiguration()).affectsTextPresentation(event) || super.affectsTextPresentation(event);
	}

	/**
	 * Creates and returns the preference store for this Java editor with the given input.
	 *
	 * @param input The editor input for which to create the preference store
	 * @return the preference store for this editor
	 *
	 * @since 3.0
	 */
	private IPreferenceStore createCombinedPreferenceStore(IEditorInput input) {
		List<IPreferenceStore> stores= new ArrayList<>(3);

		IJavaProject project= EditorUtility.getJavaProject(input);
		if (project != null) {
			stores.add(new EclipsePreferencesAdapter(new ProjectScope(project.getProject()), JavaCore.PLUGIN_ID));
		}

		stores.add(JavaPlugin.getDefault().getPreferenceStore());
		stores.add(new PreferencesAdapter(JavaPlugin.getJavaCorePluginPreferences()));
		stores.add(EditorsUI.getPreferenceStore());
		stores.add(PlatformUI.getPreferenceStore());

		return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()]));
	}

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

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

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

		super.editorContextMenuAboutToShow(menu);
		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);

		//Breadcrumb
		IAction action= getAction(IJavaEditorActionDefinitionIds.SHOW_IN_BREADCRUMB);
		menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action);

		// Quick views
		action= getAction(IJavaEditorActionDefinitionIds.SHOW_OUTLINE);
		menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action);
		action= getAction(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY);
		menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action);

		// Copy qualified name
		action= getAction(IJavaEditorActionConstants.COPY_QUALIFIED_NAME);
		action.setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_COPY_QUALIFIED_NAME);
		action.setImageDescriptor(JavaPluginImages.DESC_ELCL_COPY_QUALIFIED_NAME);
		if (menu.find(ITextEditorActionConstants.COPY) != null)
			menu.insertAfter(ITextEditorActionConstants.COPY, action);
		else
			addAction(menu, ITextEditorActionConstants.GROUP_COPY, IJavaEditorActionConstants.COPY_QUALIFIED_NAME);

		// Raw Paste
		action= getAction(IJavaEditorActionConstants.RAW_PASTE);
		action.setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_COPY_QUALIFIED_NAME);
		action.setImageDescriptor(JavaPluginImages.DESC_ELCL_COPY_QUALIFIED_NAME);
		if (menu.find(ITextEditorActionConstants.PASTE) != null)
			menu.insertAfter(ITextEditorActionConstants.PASTE, action);
		else
			addAction(menu, ITextEditorActionConstants.GROUP_COPY, IJavaEditorActionConstants.RAW_PASTE);

	}

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

	/**
	 * Informs the editor that its outliner has been closed.
	 */
	public void outlinePageClosed() {
		if (fOutlinePage != null) {
			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())) {
			fOutlinePage.select(element);
		}
	}

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

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getAdapter(Class<T> required) {

		if (IContentOutlinePage.class.equals(required)) {
			if (fOutlinePage == null && getSourceViewer() != null && isCalledByOutline())
				fOutlinePage= createOutlinePage();
			return (T) fOutlinePage;
		}

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

		if (required == IShowInTargetList.class) {
			return (T) (IShowInTargetList) () -> new String[] { JavaUI.ID_PACKAGES, IPageLayout.ID_OUTLINE };
		}

		if (required == IShowInSource.class) {
			IJavaElement inputJE= getInputJavaElement();
			if (inputJE instanceof ICompilationUnit && !JavaModelUtil.isPrimary((ICompilationUnit) inputJE))
				return null;

			return (T) (IShowInSource) () -> new ShowInContext(null, null) {
				/*
				 * @see org.eclipse.ui.part.ShowInContext#getInput()
				 * @since 3.4
				 */
				@Override
				public Object getInput() {
					if (isBreadcrumbActive())
						return null;

					return getEditorInput();
				}

				/*
				 * @see org.eclipse.ui.part.ShowInContext#getSelection()
				 * @since 3.3
				 */
				@Override
				public ISelection getSelection() {
					if (isBreadcrumbActive())
						return getBreadcrumb().getSelectionProvider().getSelection();

					try {
						IJavaElement je= SelectionConverter.getElementAtOffset(JavaEditor.this);
						if (je != null)
							return new StructuredSelection(je);
						return null;
					} catch (JavaModelException ex) {
						return null;
					}
				}
			};
		}

		if (required == IJavaFoldingStructureProvider.class)
			return (T) fProjectionModelUpdater;

		if (fProjectionSupport != null) {
			T adapter= fProjectionSupport.getAdapter(getSourceViewer(), required);
			if (adapter != null)
				return adapter;
		}

		if (required == IContextProvider.class) {
			String contextId= isBreadcrumbActive() ? IJavaHelpContextIds.JAVA_EDITOR_BREADCRUMB : IJavaHelpContextIds.JAVA_EDITOR;
			return (T) JavaUIHelp.getHelpContextProvider(this, contextId);
		}

		return super.getAdapter(required);
	}

	/**
	 * React to changed selection.
	 *
	 * @since 3.0
	 */
	protected void selectionChanged() {
		if (getSelectionProvider() == null)
			return;
		ISourceReference element= computeHighlightRangeSourceReference();
		if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE))
			synchronizeOutlinePage(element);
		if (fIsBreadcrumbVisible && fBreadcrumb != null && !fBreadcrumb.isActive())
			setBreadcrumbInput(element);
		setSelection(element, false);
		if (!fSelectionChangedViaGotoAnnotation)
			updateStatusLine();
		fSelectionChangedViaGotoAnnotation= false;
	}

	protected void setSelection(ISourceReference reference, boolean moveCursor) {
		if (getSelectionProvider() == null)
			return;

		ISelection selection= getSelectionProvider().getSelection();
		if (selection instanceof ITextSelection) {
			ITextSelection textSelection= (ITextSelection) 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= null;
				if (reference instanceof ILocalVariable || reference instanceof ITypeParameter || reference instanceof IAnnotation) {
					IJavaElement je= ((IJavaElement)reference).getParent();
					if (je instanceof ISourceReference)
						range= ((ISourceReference)je).getSourceRange();
				} else
					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;

				range= reference.getNameRange();
				if (range != null) {
					offset= range.getOffset();
					length= range.getLength();
				}

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

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

					markInNavigationHistory();
				}

			} catch (JavaModelException | IllegalArgumentException x) {
			}

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

	@Override
	public void setFocus() {
		if (fBreadcrumb != null && fBreadcrumb.isActive()) {
			fBreadcrumb.activate();
			return;
		}

		super.setFocus();
	}

	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 highlight range
			setSelection(reference, true);
			// set outliner selection
			if (fOutlinePage != null)
				fOutlinePage.select(reference);
		}
	}

	protected void doSelectionChanged(ISelection selection) {

		ISourceReference reference= null;

		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());

		ISelectionProvider selectionProvider= getSelectionProvider();
		if (selectionProvider == null )
			return;

		ISelection textSelection= selectionProvider.getSelection();
		if (!(textSelection instanceof ITextSelection))
			return;

		ITypeRoot inputJavaElement= getInputJavaElement();
		if (inputJavaElement == null)
			return;

		IProgressMonitor monitor = getProgressMonitor();
		CompilationUnit ast;
		try {
			ast= SharedASTProviderCore.getAST(inputJavaElement, SharedASTProviderCore.WAIT_NO /* DO NOT USE WAIT_ACTIVE_ONLY */ , monitor);
		} finally {
			monitor.done();
		}
		if (ast != null) {
			fForcedMarkOccurrencesSelection= textSelection;
			updateOccurrenceAnnotations((ITextSelection)textSelection, ast);
		}

	}

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

		try {

			IJavaElement element= getElementAt(offset, false);
			while (element instanceof ISourceReference) {
				ISourceRange range= ((ISourceReference) element).getSourceRange();
				if (range != null && offset < range.getOffset() + range.getLength() && range.getOffset() < offset + length) {

					ISourceViewer viewer= getSourceViewer();
					if (viewer instanceof ITextViewerExtension5) {
						ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
						extension.exposeModelRange(new Region(range.getOffset(), range.getLength()));
					}

					setHighlightRange(range.getOffset(), range.getLength(), true);
					if (fOutlinePage != null)
						fOutlinePage.select((ISourceReference) element);

					return;
				}
				element= element.getParent();
			}

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

		ISourceViewer viewer= getSourceViewer();
		if (viewer instanceof ITextViewerExtension5) {
			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
			extension.exposeModelRange(new Region(offset, length));
		} else {
			resetHighlightRange();
		}

	}

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

	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)
	 */
	@Override
	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)
	 */
	@Override
	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)
	 */
	@Override
	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
	 */
	@Override
	protected void doSetInput(IEditorInput input) throws CoreException {
		ISourceViewer sourceViewer= getSourceViewer();
		if (!(sourceViewer instanceof ISourceViewerExtension2)) {
			setPreferenceStore(createCombinedPreferenceStore(input));
			internalDoSetInput(input);
			return;
		}

		// uninstall & unregister preference store listener
		getSourceViewerDecorationSupport(sourceViewer).uninstall();
		((ISourceViewerExtension2)sourceViewer).unconfigure();

		setPreferenceStore(createCombinedPreferenceStore(input));

		// install & register preference store listener
		sourceViewer.configure(getSourceViewerConfiguration());
		getSourceViewerDecorationSupport(sourceViewer).install(getPreferenceStore());

		internalDoSetInput(input);
	}

	private void internalDoSetInput(IEditorInput input) throws CoreException {
		ISourceViewer sourceViewer= getSourceViewer();
		JavaSourceViewer javaSourceViewer= null;
		if (sourceViewer instanceof JavaSourceViewer)
			javaSourceViewer= (JavaSourceViewer)sourceViewer;

		IPreferenceStore store= getPreferenceStore();
		if (javaSourceViewer != null && isFoldingEnabled() &&(store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS)))
			javaSourceViewer.prepareDelayedProjection();

		super.doSetInput(input);

		if (javaSourceViewer != null && javaSourceViewer.getReconciler() == null) {
			IReconciler reconciler= getSourceViewerConfiguration().getReconciler(javaSourceViewer);
			if (reconciler != null) {
				reconciler.install(javaSourceViewer);
				javaSourceViewer.setReconciler(reconciler);
			}
		}

		if (fEncodingSupport != null)
			fEncodingSupport.reset();

		setOutlinePageInput(fOutlinePage, input);

		if (isShowingOverrideIndicators())
			installOverrideIndicator(false);
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#setPreferenceStore(org.eclipse.jface.preference.IPreferenceStore)
	 * @since 3.0
	 */
	@Override
	protected void setPreferenceStore(IPreferenceStore store) {
		super.setPreferenceStore(store);
		SourceViewerConfiguration sourceViewerConfiguration= getSourceViewerConfiguration();
		if (sourceViewerConfiguration == null || sourceViewerConfiguration instanceof JavaSourceViewerConfiguration) {
			JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
			setEditorInfoInPartitionerManager(this);
			setSourceViewerConfiguration(new JavaSourceViewerConfiguration(textTools.getColorManager(), store, this, IJavaPartitions.JAVA_PARTITIONING));
		}

		if (getSourceViewer() instanceof JavaSourceViewer)
			((JavaSourceViewer)getSourceViewer()).setPreferenceStore(store);

		fMarkOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES);
		fStickyOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_STICKY_OCCURRENCES);
		fMarkTypeOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES);
		fMarkMethodOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES);
		fMarkConstantOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES);
		fMarkFieldOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_FIELD_OCCURRENCES);
		fMarkLocalVariableypeOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES);
		fMarkExceptions= store.getBoolean(PreferenceConstants.EDITOR_MARK_EXCEPTION_OCCURRENCES);
		fMarkImplementors= store.getBoolean(PreferenceConstants.EDITOR_MARK_IMPLEMENTORS);
		fMarkMethodExitPoints= store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS);
		fMarkBreakContinueTargets= store.getBoolean(PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS);
	}

	/*
	 * @see IWorkbenchPart#dispose()
	 */
	@Override
	public void dispose() {

		if (fProjectionModelUpdater != null) {
			fProjectionModelUpdater.uninstall();
			fProjectionModelUpdater= null;
		}

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

		// cancel possible running computation
		fMarkOccurrenceAnnotations= false;
		uninstallOccurrencesFinder();

		uninstallOverrideIndicator();

		uninstallSemanticHighlighting();

		if (fActivationListener != null) {
			PlatformUI.getWorkbench().removeWindowListener(fActivationListener);
			fActivationListener= null;
		}

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

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

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

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

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

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

		clearEditorInfoInPartitionerManager(this);

		uninstallJavaCodeMining();

		super.dispose();
		fSelectionProvider= null;
	}

	@Override
	protected void createActions() {
		installEncodingSupport();

		super.createActions();

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

		fFoldingGroup= new FoldingActionGroup(this, getViewer());

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

		action= new ShowInBreadcrumbAction(this);
		action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_IN_BREADCRUMB);
		setAction(IJavaEditorActionDefinitionIds.SHOW_IN_BREADCRUMB, action);

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

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

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

		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 FormatElementAction();
		action.setActionDefinitionId(IJavaEditorActionDefinitionIds.QUICK_FORMAT);
		setAction("QuickFormat", action); //$NON-NLS-1$
		markAsStateDependentAction("QuickFormat", true); //$NON-NLS-1$

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

		// add annotation actions for roll-over expand hover
		action= new JavaSelectMarkerRulerAction2(JavaEditorMessages.getBundleForConstructedKeys(), "Editor.RulerAnnotationSelection.", this); //$NON-NLS-1$
		setAction("AnnotationAction", action); //$NON-NLS-1$

		createDeprecatedShowInPackageExplorerAction();

		// replace cut/copy paste actions with a version that implement 'add imports on paste'

		action= new ClipboardOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$
		setAction(ITextEditorActionConstants.CUT, action);

		action= new ClipboardOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY); //$NON-NLS-1$
		setAction(ITextEditorActionConstants.COPY, action);

		action= new ClipboardOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$
		setAction(ITextEditorActionConstants.PASTE, action);

		action= new ClipboardOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Editor.RawPaste.", this, ClipboardOperationAction.RAW_PASTE); //$NON-NLS-1$
		setAction(IJavaEditorActionConstants.RAW_PASTE, action);
		action.setEnabled(true);

		action= new CopyQualifiedNameAction(this);
		action.setActionDefinitionId(CopyQualifiedNameAction.ACTION_DEFINITION_ID);
		action.setImageDescriptor(null);
		setAction(IJavaEditorActionConstants.COPY_QUALIFIED_NAME, action);
	}

	/**
	 * @deprecated As of 3.5, got replaced by generic Navigate &gt; Show In &gt;
	 */
	@Deprecated
	private void createDeprecatedShowInPackageExplorerAction() {
		IAction action= new org.eclipse.jdt.ui.actions.ShowInPackageViewAction(this);
		action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_IN_PACKAGE_VIEW);
		setAction("ShowInPackageView", action); //$NON-NLS-1$
	}

	/**
	 * Sets this editor's actions into activated (default) or deactived state.
	 * <p>
	 * XXX: Currently this is done by using a private method from {@link AbstractTextEditor} as we
	 * don't want to make this risky method API at this point, since Java editor breadcrumb might
	 * become a Platform UI feature during 3.5 and hence we can then delete this workaround.
	 * </p>
	 *
	 * @param state <code>true</code> if activated
	 * @since 3.4
	 */
	protected void setActionsActivated(boolean state) {
		Method method= null;
		try {
			method= AbstractTextEditor.class.getDeclaredMethod("setActionActivation", boolean.class); //$NON-NLS-1$
		} catch (SecurityException | NoSuchMethodException ex) {
			JavaPlugin.log(ex);
		}
		Assert.isNotNull(method);
		method.setAccessible(true);
		try {
			method.invoke(this, Boolean.valueOf(state));
		} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) {
			JavaPlugin.log(ex);
		}
	}

	/**
	 * Installs the encoding support on the given text editor.
	 * <p>
 	 * Subclasses may override to install their own encoding
 	 * support or to disable the default encoding support.
 	 * </p>
	 * @since 3.2
	 */
	protected void installEncodingSupport() {
		fEncodingSupport= new DefaultEncodingSupport();
		fEncodingSupport.initialize(this);
	}


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

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

		final String property= event.getProperty();

		if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
			/*
			 * Ignore tab setting since we rely on the formatter preferences.
			 * We do this outside the try-finally block to avoid that EDITOR_TAB_WIDTH
			 * is handled by the sub-class (AbstractDecoratedTextEditor).
			 */
			return;
		}

		try {

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

			if (isJavaEditorHoverProperty(property))
				updateHoverBehavior();

			boolean newBooleanValue= false;
			Object newValue= event.getNewValue();
			if (newValue != null)
				newBooleanValue= Boolean.parseBoolean(newValue.toString());

			if (PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE.equals(property)) {
				if (newBooleanValue)
					selectionChanged();
				return;
			}

			if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) {
				if (newBooleanValue != fMarkOccurrenceAnnotations) {
					fMarkOccurrenceAnnotations= newBooleanValue;
					if (!fMarkOccurrenceAnnotations)
						uninstallOccurrencesFinder();
					else
						installOccurrencesFinder(true);
				}
				return;
			}
			if (property.equals(getBreadcrumbPreferenceKey())) {
				if (newBooleanValue != fIsBreadcrumbVisible) {
					fIsBreadcrumbVisible= newBooleanValue;
					if (fIsBreadcrumbVisible)
						showBreadcrumb();
					else
						hideBreadcrumb();
				}
				return;
			}
			switch (property) {
				case PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES:
					fMarkTypeOccurrences= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES:
					fMarkMethodOccurrences= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES:
					fMarkConstantOccurrences= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_FIELD_OCCURRENCES:
					fMarkFieldOccurrences= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES:
					fMarkLocalVariableypeOccurrences= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_EXCEPTION_OCCURRENCES:
					fMarkExceptions= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS:
					fMarkMethodExitPoints= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS:
					fMarkBreakContinueTargets= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_MARK_IMPLEMENTORS:
					fMarkImplementors= newBooleanValue;
					return;
				case PreferenceConstants.EDITOR_STICKY_OCCURRENCES:
					fStickyOccurrenceAnnotations= newBooleanValue;
					return;
				default:
					break;
			}
			if (SemanticHighlightings.affectsEnablement(getPreferenceStore(), event)) {
				if (isSemanticHighlightingEnabled())
					installSemanticHighlighting();
				else
					uninstallSemanticHighlighting();
				return;
			}

			if (affectsJavaCodeMining(event)) {
				// It's a code mining preference, recompute the list of code mining providers.
				installCodeMiningProviders();
				if (isJavaCodeMiningEnabled()) {
					installJavaCodeMining();
				} else {
					uninstallJavaCodeMining();
				}
				return;
			}

			if (JavaCore.COMPILER_SOURCE.equals(property)) {
				if (event.getNewValue() instanceof String)
					fBracketMatcher.setSourceVersion((String) event.getNewValue());
				// fall through as others are interested in source change as well.
			}

			((JavaSourceViewerConfiguration)getSourceViewerConfiguration()).handlePropertyChangeEvent(event);

			if (affectsOverrideIndicatorAnnotations(event)) {
				if (isShowingOverrideIndicators()) {
					if (fOverrideIndicatorManager == null)
						installOverrideIndicator(true);
				} else {
					if (fOverrideIndicatorManager != null)
						uninstallOverrideIndicator();
				}
				return;
			}

			switch (property) {
				case PreferenceConstants.EDITOR_FOLDING_PROVIDER:
					if (sourceViewer instanceof ProjectionViewer) {
						ProjectionViewer projectionViewer= (ProjectionViewer) sourceViewer;
						if (fProjectionModelUpdater != null)
							fProjectionModelUpdater.uninstall();
						// either freshly enabled or provider changed
						fProjectionModelUpdater= JavaPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider();
						if (fProjectionModelUpdater != null) {
							fProjectionModelUpdater.install(this, projectionViewer);
						}
					}
					return;
				case DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE:
				case DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE:
				case DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR:
					StyledText textWidget= sourceViewer.getTextWidget();
					int tabWidth= getSourceViewerConfiguration().getTabWidth(sourceViewer);
					if (textWidget.getTabs() != tabWidth)
						textWidget.setTabs(tabWidth);
					return;
				case PreferenceConstants.EDITOR_FOLDING_ENABLED:
					if (sourceViewer instanceof ProjectionViewer) {
						new ToggleFoldingRunner().runWhenNextVisible();
					}
					return;
				default:
					break;
			}

		} finally {
			super.handlePreferenceStoreChanged(event);
		}

		if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) {
			// superclass already installed the range indicator
			Object newValue= event.getNewValue();
			ISourceViewer viewer= getSourceViewer();
			if (newValue != null && viewer != null) {
				if (Boolean.parseBoolean(newValue.toString())) {
					// adjust the highlightrange in order to get the magnet right after changing the selection
					Point selection= viewer.getSelectedRange();
					adjustHighlightRange(selection.x, selection.y);
				}
			}

		}
	}

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

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

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

	/*
	 * Update the hovering behavior depending on the preferences.
	 */
	private void updateHoverBehavior() {
		SourceViewerConfiguration configuration= getSourceViewerConfiguration();

		for (String t : configuration.getConfiguredContentTypes(getSourceViewer())) {
			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 stateMask : stateMasks) {
						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()
	 */
	@Override
	public Object getViewPartInput() {
		return getEditorInput().getAdapter(IJavaElement.class);
	}

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

	boolean isFoldingEnabled() {
		return JavaPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED);
	}

	/*
	 * @see org.eclipse.ui.part.WorkbenchPart#getOrientation()
	 * @since 3.1
	 */
	@Override
	public int getOrientation() {
		return SWT.LEFT_TO_RIGHT;	//Java editors are always left to right by default
	}

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

		fEditorSelectionChangedListener= new EditorSelectionChangedListener();
		fEditorSelectionChangedListener.install(getSelectionProvider());

		if (isSemanticHighlightingEnabled())
			installSemanticHighlighting();

		fBreadcrumb= createBreadcrumb();
		fIsBreadcrumbVisible= isBreadcrumbShown();
		if (fIsBreadcrumbVisible)
			showBreadcrumb();

		if (isJavaCodeMiningEnabled()) {
			installJavaCodeMining();
		}

		PlatformUI.getWorkbench().addWindowListener(fActivationListener);
	}

	@Override
	protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {

		fBracketMatcher.setSourceVersion(getPreferenceStore().getString(JavaCore.COMPILER_SOURCE));
		support.setCharacterPairMatcher(fBracketMatcher);
		support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS);

		super.configureSourceViewerDecorationSupport(support);

		// The base class will have already called setMarginPainterPreferenceKeys. We override it
		// here with more specific values for the java editor. Note that this needs to go after the
		// call to super since the last invocation wins.
		support.setMarginPainterPreferenceKeys(
				AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN,
				AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR,
				DefaultCodeFormatterConstants.FORMATTER_LINE_SPLIT);
	}

	/**
	 * Returns the lock object for the given annotation model.
	 *
	 * @param annotationModel the annotation model
	 * @return the annotation model's lock object
	 * @since 3.0
	 */
	private Object getLockObject(IAnnotationModel annotationModel) {
		if (annotationModel instanceof ISynchronizable) {
			Object lock= ((ISynchronizable)annotationModel).getLockObject();
			if (lock != null)
				return lock;
		}
		return annotationModel;
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#updateMarkerViews(org.eclipse.jface.text.source.Annotation)
	 * @since 3.2
	 */
	@Override
	protected void updateMarkerViews(Annotation annotation) {
		if (annotation instanceof IJavaAnnotation) {
			Iterator<IJavaAnnotation> e= ((IJavaAnnotation) annotation).getOverlaidIterator();
			if (e != null) {
				while (e.hasNext()) {
					Object o= e.next();
					if (o instanceof MarkerAnnotation) {
						super.updateMarkerViews((MarkerAnnotation)o);
						return;
					}
				}
			}
			return;
		}
		super.updateMarkerViews(annotation);
	}

	/**
	 * Finds and marks occurrence annotations.
	 *
	 * @since 3.0
	 */
	class OccurrencesFinderJob extends Job {

		private final IDocument fDocument;
		private final ISelection fSelection;
		private final ISelectionValidator fPostSelectionValidator;
		private boolean fCanceled= false;
		private final OccurrenceLocation[] fLocations;

		public OccurrencesFinderJob(IDocument document, OccurrenceLocation[] locations, ISelection selection) {
			super(JavaEditorMessages.JavaEditor_markOccurrences_job_name);
			fDocument= document;
			fSelection= selection;
			fLocations= locations;

			if (getSelectionProvider() instanceof ISelectionValidator)
				fPostSelectionValidator= (ISelectionValidator)getSelectionProvider();
			else
				fPostSelectionValidator= null;
		}

		// cannot use cancel() because it is declared final
		void doCancel() {
			fCanceled= true;
			cancel();
		}

		private boolean isCanceled(IProgressMonitor progressMonitor) {
			return fCanceled || progressMonitor.isCanceled()
				||  fPostSelectionValidator != null && (!fPostSelectionValidator.isValid(fSelection) && (fForcedMarkOccurrencesSelection != fSelection))
				|| LinkedModeModel.hasInstalledModel(fDocument);
		}

		/*
		 * @see Job#run(org.eclipse.core.runtime.IProgressMonitor)
		 */
		@Override
		public IStatus run(IProgressMonitor progressMonitor) {
			if (isCanceled(progressMonitor))
				return Status.CANCEL_STATUS;

			ITextViewer textViewer= getViewer();
			if (textViewer == null)
				return Status.CANCEL_STATUS;

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

			IDocumentProvider documentProvider= getDocumentProvider();
			if (documentProvider == null)
				return Status.CANCEL_STATUS;

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

			// Add occurrence annotations
			int length= fLocations.length;
			Map<Annotation, Position> annotationMap= new HashMap<>(length);
			for (OccurrenceLocation location : fLocations) {

				if (isCanceled(progressMonitor))
					return Status.CANCEL_STATUS;

				Position position= new Position(location.getOffset(), location.getLength());

				String description= location.getDescription();
				String annotationType= (location.getFlags() == IOccurrencesFinder.F_WRITE_OCCURRENCE) ? "org.eclipse.jdt.ui.occurrences.write" : "org.eclipse.jdt.ui.occurrences"; //$NON-NLS-1$ //$NON-NLS-2$

				annotationMap.put(new Annotation(annotationType, false, description), position);
			}

			if (isCanceled(progressMonitor))
				return Status.CANCEL_STATUS;

			synchronized (getLockObject(annotationModel)) {
				if (annotationModel instanceof IAnnotationModelExtension) {
					((IAnnotationModelExtension)annotationModel).replaceAnnotations(fOccurrenceAnnotations, annotationMap);
				} else {
					removeOccurrenceAnnotations();
					Iterator<Entry<Annotation, Position>> iter= annotationMap.entrySet().iterator();
					while (iter.hasNext()) {
						Entry<Annotation, Position> mapEntry= iter.next();
						annotationModel.addAnnotation(mapEntry.getKey(), mapEntry.getValue());
					}
				}
				fOccurrenceAnnotations= annotationMap.keySet().toArray(new Annotation[annotationMap.size()]);
			}

			return Status.OK_STATUS;
		}
	}

	/**
	 * Updates the occurrences annotations based
	 * on the current selection.
	 *
	 * @param selection the text selection
	 * @param astRoot the compilation unit AST
	 * @since 3.0
	 */
	protected void updateOccurrenceAnnotations(ITextSelection selection, CompilationUnit astRoot) {

		if (fOccurrencesFinderJob != null)
			fOccurrencesFinderJob.cancel();

		if (!fMarkOccurrenceAnnotations)
			return;

		if (astRoot == null || selection == null)
			return;

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

		boolean hasChanged= false;
		if (document instanceof IDocumentExtension4) {
			int offset= selection.getOffset();
			long currentModificationStamp= ((IDocumentExtension4)document).getModificationStamp();
			IRegion markOccurrenceTargetRegion= fMarkOccurrenceTargetRegion;
			hasChanged= currentModificationStamp != fMarkOccurrenceModificationStamp;
			if (markOccurrenceTargetRegion != null && !hasChanged) {
				if (markOccurrenceTargetRegion.getOffset() <= offset && offset <= markOccurrenceTargetRegion.getOffset() + markOccurrenceTargetRegion.getLength())
					return;
			}
			fMarkOccurrenceTargetRegion= JavaWordFinder.findWord(document, offset);
			fMarkOccurrenceModificationStamp= currentModificationStamp;
		}

		OccurrenceLocation[] locations= null;

		ASTNode selectedNode= NodeFinder.perform(astRoot, selection.getOffset(), selection.getLength());
		if (fMarkExceptions) {
			ExceptionOccurrencesFinder finder= new ExceptionOccurrencesFinder();
			if (finder.initialize(astRoot, selectedNode) == null) {
				locations= finder.getOccurrences();
			}
		}

		if (locations == null && fMarkMethodExitPoints) {
			MethodExitsFinder finder= new MethodExitsFinder();
			if (finder.initialize(astRoot, selectedNode) == null) {
				locations= finder.getOccurrences();
			}
		}

		if (locations == null && fMarkBreakContinueTargets) {
			BreakContinueTargetFinder finder= new BreakContinueTargetFinder();
			if (finder.initialize(astRoot, selectedNode) == null) {
				locations= finder.getOccurrences();
			}
		}

		if (locations == null && fMarkImplementors) {
			ImplementOccurrencesFinder finder= new ImplementOccurrencesFinder();
			if (finder.initialize(astRoot, selectedNode) == null) {
				locations= finder.getOccurrences();
			}
		}

		if (locations == null && selectedNode instanceof Name) {
			IBinding binding= ((Name)selectedNode).resolveBinding();
			if (binding != null && markOccurrencesOfType(binding)) {
				OccurrencesFinder finder= new OccurrencesFinder();
				if (finder.initialize(astRoot, selectedNode) == null) {
					locations= finder.getOccurrences();
				}
			}
		}

		if (locations == null) {
			if (!fStickyOccurrenceAnnotations
					|| hasChanged) { // check consistency of current annotations
				removeOccurrenceAnnotations();
			}
			return;
		}

		fOccurrencesFinderJob= new OccurrencesFinderJob(document, locations, selection);
		//fOccurrencesFinderJob.setPriority(Job.DECORATE);
		//fOccurrencesFinderJob.setSystem(true);
		//fOccurrencesFinderJob.schedule();
		fOccurrencesFinderJob.run(new NullProgressMonitor());
	}

	protected void installOccurrencesFinder(boolean forceUpdate) {
		fMarkOccurrenceAnnotations= true;

		fPostSelectionListenerWithAST= (part, selection, astRoot) -> updateOccurrenceAnnotations(selection, astRoot);
		SelectionListenerWithASTManager.getDefault().addListener(this, fPostSelectionListenerWithAST);
		if (forceUpdate && getSelectionProvider() != null) {
			fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection();
			ITypeRoot inputJavaElement= getInputJavaElement();
			if (inputJavaElement != null) {
				IProgressMonitor monitor = getProgressMonitor();
				try {
					updateOccurrenceAnnotations((ITextSelection)fForcedMarkOccurrencesSelection, SharedASTProviderCore.getAST(inputJavaElement, SharedASTProviderCore.WAIT_NO, monitor));
				} finally {
					monitor.done();
				}
			}
		}

		if (fOccurrencesFinderJobCanceler == null) {
			fOccurrencesFinderJobCanceler= new OccurrencesFinderJobCanceler();
			fOccurrencesFinderJobCanceler.install();
		}
	}

	protected void uninstallOccurrencesFinder() {
		fMarkOccurrenceAnnotations= false;

		if (fOccurrencesFinderJob != null) {
			fOccurrencesFinderJob.cancel();
			fOccurrencesFinderJob= null;
		}

		if (fOccurrencesFinderJobCanceler != null) {
			fOccurrencesFinderJobCanceler.uninstall();
			fOccurrencesFinderJobCanceler= null;
		}

		if (fPostSelectionListenerWithAST != null) {
			SelectionListenerWithASTManager.getDefault().removeListener(this, fPostSelectionListenerWithAST);
			fPostSelectionListenerWithAST= null;
		}

		removeOccurrenceAnnotations();
	}

	protected boolean isMarkingOccurrences() {
		IPreferenceStore store= getPreferenceStore();
		return store != null && store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES);
	}

	/**
	 * @return true if editor breadcrumbs are enabled
	 * @since 3.4
	 */
	protected boolean isBreadcrumbShown() {
		IPreferenceStore store= getPreferenceStore();
		String key= getBreadcrumbPreferenceKey();
		return store != null && key != null && store.getBoolean(key);
	}

	boolean markOccurrencesOfType(IBinding binding) {

		if (binding == null)
			return false;

		int kind= binding.getKind();

		if (fMarkTypeOccurrences && kind == IBinding.TYPE)
			return true;

		if (fMarkMethodOccurrences && kind == IBinding.METHOD)
			return true;

		if (fMarkMethodOccurrences && kind == IBinding.MODULE)
			return true;

		if (kind == IBinding.VARIABLE) {
			IVariableBinding variableBinding= (IVariableBinding)binding;
			if (variableBinding.isField()) {
				int constantModifier= IModifierConstants.ACC_STATIC | IModifierConstants.ACC_FINAL;
				boolean isConstant= (variableBinding.getModifiers() & constantModifier) == constantModifier;
				if (isConstant)
					return fMarkConstantOccurrences;
				else
					return fMarkFieldOccurrences;
			}

			return fMarkLocalVariableypeOccurrences;
		}

		return false;
	}

	void removeOccurrenceAnnotations() {
		fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
		fMarkOccurrenceTargetRegion= null;

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

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

		synchronized (getLockObject(annotationModel)) {
			if (annotationModel instanceof IAnnotationModelExtension) {
				((IAnnotationModelExtension)annotationModel).replaceAnnotations(fOccurrenceAnnotations, null);
			} else {
				for (Annotation fOccurrenceAnnotation : fOccurrenceAnnotations)
					annotationModel.removeAnnotation(fOccurrenceAnnotation);
			}
			fOccurrenceAnnotations= null;
		}
	}

	protected void uninstallOverrideIndicator() {
		if (fOverrideIndicatorManager != null) {
			fOverrideIndicatorManager.removeAnnotations();
			fOverrideIndicatorManager= null;
		}
	}

	protected void installOverrideIndicator(boolean provideAST) {
		uninstallOverrideIndicator();
		IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
		final ITypeRoot inputElement= getInputJavaElement();

		if (model == null || inputElement == null)
			return;

		fOverrideIndicatorManager= new OverrideIndicatorManager(model, inputElement, null);

		if (provideAST) {
			IProgressMonitor monitor = getProgressMonitor();
			try {
				SubMonitor subMonitor= SubMonitor.convert(monitor, 2);
				CompilationUnit ast= SharedASTProviderCore.getAST(inputElement, SharedASTProviderCore.WAIT_ACTIVE_ONLY,
						subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE));
				fOverrideIndicatorManager.reconciled(ast, true, subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE));
			} finally {
				monitor.done();
			}
		}
	}

	/**
	 * Tells whether override indicators are shown.
	 *
	 * @return <code>true</code> if the override indicators are shown
	 * @since 3.0
	 */
	protected boolean isShowingOverrideIndicators() {
		AnnotationPreference preference= getAnnotationPreferenceLookup().getAnnotationPreference(OverrideIndicatorManager.ANNOTATION_TYPE);
		IPreferenceStore store= getPreferenceStore();
		return getBoolean(store, preference.getHighlightPreferenceKey())
			|| getBoolean(store, preference.getVerticalRulerPreferenceKey())
			|| getBoolean(store, preference.getOverviewRulerPreferenceKey())
			|| getBoolean(store, preference.getTextPreferenceKey());
	}

	/**
	 * Returns the boolean preference for the given key.
	 *
	 * @param store the preference store
	 * @param key the preference key
	 * @return <code>true</code> if the key exists in the store and its value is <code>true</code>
	 * @since 3.0
	 */
	private boolean getBoolean(IPreferenceStore store, String key) {
		return key != null && store.getBoolean(key);
	}

	/**
	 * Determines whether the preference change encoded by the given event
	 * changes the override indication.
	 *
	 * @param event the event to be investigated
	 * @return <code>true</code> if event causes a change
	 * @since 3.0
	 */
	protected boolean affectsOverrideIndicatorAnnotations(PropertyChangeEvent event) {
		String key= event.getProperty();
		AnnotationPreference preference= getAnnotationPreferenceLookup().getAnnotationPreference(OverrideIndicatorManager.ANNOTATION_TYPE);
		if (key == null || preference == null)
			return false;

		return key.equals(preference.getHighlightPreferenceKey())
			|| key.equals(preference.getVerticalRulerPreferenceKey())
			|| key.equals(preference.getOverviewRulerPreferenceKey())
			|| key.equals(preference.getTextPreferenceKey());
	}

	/**
	 * @return <code>true</code> if Semantic Highlighting is enabled.
	 *
	 * @since 3.0
	 */
	private boolean isSemanticHighlightingEnabled() {
		return SemanticHighlightings.isEnabled(getPreferenceStore());
	}

	/**
	 * Install Semantic Highlighting.
	 *
	 * @since 3.0
	 */
	protected void installSemanticHighlighting() {
		if (fSemanticManager == null) {
			fSemanticManager= new SemanticHighlightingManager();
			fSemanticManager.install(this, (JavaSourceViewer) getSourceViewer(), JavaPlugin.getDefault().getJavaTextTools().getColorManager(), getPreferenceStore());
		}
	}

	/**
	 * Uninstall Semantic Highlighting.
	 *
	 * @since 3.0
	 */
	private void uninstallSemanticHighlighting() {
		if (fSemanticManager != null) {
			fSemanticManager.uninstall();
			fSemanticManager= null;
		}
	}

	/**
	 * Calls the SemanticHighlightingReconciler refresh function which does the semantic
	 * highlighting of the editor content
	 *
	 * @since 3.26
	 */
	public void refreshSemanticHighlighting() {
		if (fSemanticManager != null) {
			SemanticHighlightingReconciler reconciler= fSemanticManager.getReconciler();
			if (reconciler != null) {
				reconciler.refresh();
			}
		}
	}

	/**
	 * Returns the Java element wrapped by this editors input.
	 *
	 * @return the Java element wrapped by this editors input.
	 * @since 3.0
	 */
	protected ITypeRoot getInputJavaElement() {
		return EditorUtility.getEditorInputJavaElement(this, false);
	}

	protected void updateStatusLine() {
		ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
		Annotation annotation= getAnnotation(selection.getOffset(), selection.getLength());
		String message= null;
		if (annotation != null) {
			updateMarkerViews(annotation);
			if (annotation instanceof IJavaAnnotation && ((IJavaAnnotation) annotation).isProblem() || isProblemMarkerAnnotation(annotation))
				message= annotation.getText();
		}
		setStatusLineErrorMessage(null);
		setStatusLineMessage(message);
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#setStatusLineErrorMessage(java.lang.String)
	 * @since 3.5
	 */
	@Override
	public void setStatusLineErrorMessage(String message) {
		long now= System.currentTimeMillis();
		if (message != null || now - fErrorMessageTime > ERROR_MESSAGE_TIMEOUT) {
			super.setStatusLineErrorMessage(message);
			fErrorMessageTime= message != null ? now : 0;
		}
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#setStatusLineMessage(java.lang.String)
	 * @since 3.5
	 */
	@Override
	protected void setStatusLineMessage(String message) {
		if (System.currentTimeMillis() - fErrorMessageTime > ERROR_MESSAGE_TIMEOUT)
			super.setStatusLineMessage(message);
	}

	/**
	 * Tells whether the given annotation stands for a problem marker.
	 *
	 * @param annotation the annotation
	 * @return <code>true</code> if it is a problem marker
	 * @since 3.4
	 */
	private static boolean isProblemMarkerAnnotation(Annotation annotation) {
		if (!(annotation instanceof MarkerAnnotation))
			return false;
		try {
			return(((MarkerAnnotation)annotation).getMarker().isSubtypeOf(IMarker.PROBLEM));
		} catch (CoreException e) {
			return false;
		}
	}

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

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

		IRegion selection= getSignedSelection(sourceViewer);
		if (fPreviousSelections == null)
			initializePreviousSelectionList();

		IRegion region= fBracketMatcher.match(document, selection.getOffset(), selection.getLength());
		if (region == null) {
			region= fBracketMatcher.findEnclosingPeerCharacters(document, selection.getOffset(), selection.getLength());
			initializePreviousSelectionList();
			fPreviousSelections.add(selection);
		} else {
			if (fPreviousSelections.size() == 2) {
				if (!selection.equals(fPreviousSelections.get(1))) {
					initializePreviousSelectionList();
				}
			} else if (fPreviousSelections.size() == 3) {
				if (selection.equals(fPreviousSelections.get(2)) && !selection.equals(fPreviousSelections.get(0))) {
					IRegion originalSelection= fPreviousSelections.get(0);
					sourceViewer.setSelectedRange(originalSelection.getOffset(), originalSelection.getLength());
					sourceViewer.revealRange(originalSelection.getOffset(), originalSelection.getLength());
					initializePreviousSelectionList();
					return;
				}
				initializePreviousSelectionList();
			}
		}

		if (region == null) {
			setStatusLineErrorMessage(JavaEditorMessages.GotoMatchingBracket_error_noMatchingBracket);
			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= (ICharacterPairMatcher.RIGHT == anchor) ? offset + 1 : offset + length - 1;

		boolean visible= false;
		if (sourceViewer instanceof ITextViewerExtension5) {
			ITextViewerExtension5 extension= (ITextViewerExtension5) 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.GotoMatchingBracket_error_bracketOutsideSelectedElement);
			sourceViewer.getTextWidget().getDisplay().beep();
			return;
		}

		int adjustment= getOffsetAdjustment(document, selection.getOffset() + selection.getLength(), selection.getLength());
		targetOffset+= adjustment;
		int direction= (selection.getLength() == 0) ? 0 : ((selection.getLength() > 0) ? 1 : -1);
		if (fPreviousSelections.size() == 1 && direction < 0) {
			targetOffset++;
		}

		if (fPreviousSelections.size() > 0) {
			fPreviousSelections.add(new Region(targetOffset, direction));
		}
		sourceViewer.setSelectedRange(targetOffset, direction);
		sourceViewer.revealRange(targetOffset, direction);
	}

	private void initializePreviousSelectionList() {
		fPreviousSelections= new ArrayList<>(3);
	}

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

	private static boolean isClosingBracket(char character) {
		for (int i= 1; i < BRACKETS.length; i+= 2) {
			if (character == BRACKETS[i])
				return true;
		}
		return false;
	}

	/*
	 * Copy of org.eclipse.jface.text.source.DefaultCharacterPairMatcher.getOffsetAdjustment(IDocument, int, int)
	 */
	private static int getOffsetAdjustment(IDocument document, int offset, int length) {
		if (length == 0 || Math.abs(length) > 1)
			return 0;
		try {
			if (length < 0) {
				if (isOpeningBracket(document.getChar(offset))) {
					return 1;
				}
			} else {
				if (isClosingBracket(document.getChar(offset - 1))) {
					return -1;
				}
			}
		} catch (BadLocationException e) {
			//do nothing
		}
		return 0;
	}

	/*
	 * Copy of org.eclipse.jface.text.source.MatchingCharacterPainter.getSignedSelection(ISourceViewer)
	 */
	private static final IRegion getSignedSelection(ISourceViewer sourceViewer) {
		Point viewerSelection= sourceViewer.getSelectedRange();

		StyledText text= sourceViewer.getTextWidget();
		Point selection= text.getSelectionRange();
		if (text.getCaretOffset() == selection.x) {
			viewerSelection.x= viewerSelection.x + viewerSelection.y;
			viewerSelection.y= -viewerSelection.y;
		}

		return new Region(viewerSelection.x, viewerSelection.y);
	}

	@Override
	public ISelectionProvider getSelectionProvider() {
		return fSelectionProvider;
	}

	/**
	 * Returns the cached selected range, which allows
	 * to query it from a non-UI thread.
	 * <p>
	 * The result might be outdated if queried from a non-UI thread.</em></p>
	 *
	 * @return the caret offset in the master document
	 * @see ITextViewer#getSelectedRange()
	 * @since 3.3
	 */
	public Point getCachedSelectedRange() {
		return fCachedSelectedRange;
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#handleCursorPositionChanged()
	 * @since 3.3
	 */
	@Override
	protected void handleCursorPositionChanged() {
		super.handleCursorPositionChanged();
		fCachedSelectedRange= getViewer().getSelectedRange();
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Overrides the default implementation to handle {@link IJavaAnnotation}.
	 * </p>
	 *
	 * @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
	 * @since 3.2
	 */
	@Override
	protected Annotation findAnnotation(final int offset, final 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= Integer.MAX_VALUE;

		IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
		if (model == null)
			return null;

		Iterator<Annotation> e= new JavaAnnotationIterator(model.getAnnotationIterator(), true);
		while (e.hasNext()) {
			Annotation a= e.next();
			if ((a instanceof IJavaAnnotation) && ((IJavaAnnotation)a).hasOverlay() || !isNavigationTarget(a))
				continue;

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

			if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
				if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) {
					containingAnnotation= a;
					containingAnnotationPosition= p;
					currentAnnotation= p.length == length;
				}
			} else {
				int currentDistance= 0;

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

					if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
						distance= currentDistance;
						nextAnnotation= a;
						nextAnnotationPosition= p;
					}
				} else {
					currentDistance= offset + length - (p.getOffset() + p.length);
					if (currentDistance < 0)
						currentDistance= endOfDocument + currentDistance;

					if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
						distance= currentDistance;
						nextAnnotation= a;
						nextAnnotationPosition= p;
					}
				}
			}
		}
		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) {
		IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
		if (model == null)
			return null;

		Iterator<Annotation> parent;
		if (model instanceof IAnnotationModelExtension2)
			parent= ((IAnnotationModelExtension2)model).getAnnotationIterator(offset, length, true, true);
		else
			parent= model.getAnnotationIterator();

		Iterator<Annotation> e= new JavaAnnotationIterator(parent, false);
		while (e.hasNext()) {
			Annotation a= e.next();
			Position p= model.getPosition(a);
			if (p != null && p.overlapsWith(offset, length))
				return a;
		}
		return null;
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#gotoAnnotation(boolean)
	 * @since 3.2
	 */
	@Override
	public Annotation gotoAnnotation(boolean forward) {
		fSelectionChangedViaGotoAnnotation= true;
		return super.gotoAnnotation(forward);
	}

	/**
	 * 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 ITextViewerExtension5) {
			ITextViewerExtension5 extension= (ITextViewerExtension5)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.AbstractDecoratedTextEditor#createChangeHover()
	 */
	@Override
	protected LineChangeHover createChangeHover() {
		return new JavaChangeHover(IJavaPartitions.JAVA_PARTITIONING, getOrientation());
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#createNavigationActions()
	 */
	@Override
	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 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);
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createAnnotationRulerColumn(org.eclipse.jface.text.source.CompositeRuler)
	 * @since 3.2
	 */
	@Override
	protected IVerticalRulerColumn createAnnotationRulerColumn(CompositeRuler ruler) {
		if (!getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER))
			return super.createAnnotationRulerColumn(ruler);

		AnnotationRulerColumn column= new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess());
		column.setHover(new JavaExpandHover(ruler, getAnnotationAccess(), new IDoubleClickListener() {

			@Override
			public void doubleClick(DoubleClickEvent event) {
				// for now: just invoke ruler double click action
				triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK);
			}

			private void triggerAction(String actionID) {
				IAction action= getAction(actionID);
				if (action != null) {
					if (action instanceof IUpdate)
						((IUpdate) action).update();
					// hack to propagate line change
					if (action instanceof ISelectionListener) {
						((ISelectionListener)action).selectionChanged(null, null);
					}
					if (action.isEnabled())
						action.run();
				}
			}

		}));

		return column;
	}

	/**
	 * Returns the folding action group, or <code>null</code> if there is none.
	 *
	 * @return the folding action group, or <code>null</code> if there is none
	 * @since 3.0
	 */
	protected FoldingActionGroup getFoldingActionGroup() {
		return fFoldingGroup;
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#performRevert()
	 */
	@Override
	protected void performRevert() {
		ProjectionViewer projectionViewer= (ProjectionViewer) getSourceViewer();
		projectionViewer.setRedraw(false);
		try {

			boolean projectionMode= projectionViewer.isProjectionMode();
			if (projectionMode) {
				projectionViewer.disableProjection();
				if (fProjectionModelUpdater != null)
					fProjectionModelUpdater.uninstall();
			}

			super.performRevert();

			if (projectionMode) {
				if (fProjectionModelUpdater != null)
					fProjectionModelUpdater.install(this, projectionViewer);
				projectionViewer.enableProjection();
			}

		} finally {
			projectionViewer.setRedraw(true);
		}
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractTextEditor#rulerContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
	 */
	@Override
	protected void rulerContextMenuAboutToShow(IMenuManager menu) {
		super.rulerContextMenuAboutToShow(menu);
		IMenuManager foldingMenu= new MenuManager(JavaEditorMessages.Editor_FoldingMenu_name, "projection"); //$NON-NLS-1$
		menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, foldingMenu);

		IAction action= getAction("FoldingToggle"); //$NON-NLS-1$
		foldingMenu.add(action);
		action= getAction("FoldingExpandAll"); //$NON-NLS-1$
		foldingMenu.add(action);
		action= getAction("FoldingCollapseAll"); //$NON-NLS-1$
		foldingMenu.add(action);
		action= getAction("FoldingRestore"); //$NON-NLS-1$
		foldingMenu.add(action);
		action= getAction("FoldingCollapseMembers"); //$NON-NLS-1$
		foldingMenu.add(action);
		action= getAction("FoldingCollapseComments"); //$NON-NLS-1$
		foldingMenu.add(action);
	}

	/*
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#collectContextMenuPreferencePages()
	 * @since 3.1
	 */
	@Override
	protected String[] collectContextMenuPreferencePages() {
		String[] inheritedPages= super.collectContextMenuPreferencePages();
		int length= 10;
		String[] result= new String[inheritedPages.length + length];
		result[0]= "org.eclipse.jdt.ui.preferences.JavaEditorPreferencePage"; //$NON-NLS-1$
		result[1]= "org.eclipse.jdt.ui.preferences.JavaTemplatePreferencePage"; //$NON-NLS-1$
		result[2]= "org.eclipse.jdt.ui.preferences.CodeAssistPreferencePage"; //$NON-NLS-1$
		result[3]= "org.eclipse.jdt.ui.preferences.CodeAssistPreferenceAdvanced"; //$NON-NLS-1$
		result[4]= "org.eclipse.jdt.ui.preferences.JavaEditorHoverPreferencePage"; //$NON-NLS-1$
		result[5]= "org.eclipse.jdt.ui.preferences.JavaEditorColoringPreferencePage"; //$NON-NLS-1$
		result[6]= "org.eclipse.jdt.ui.preferences.FoldingPreferencePage"; //$NON-NLS-1$
		result[7]= "org.eclipse.jdt.ui.preferences.MarkOccurrencesPreferencePage"; //$NON-NLS-1$
		result[8]= "org.eclipse.jdt.ui.preferences.SmartTypingPreferencePage"; //$NON-NLS-1$
		result[9]= "org.eclipse.jdt.ui.preferences.SaveParticipantPreferencePage"; //$NON-NLS-1$
		System.arraycopy(inheritedPages, 0, result, length, inheritedPages.length);
		return result;
	}

	/*
	 * @see AbstractTextEditor#getUndoRedoOperationApprover(IUndoContext)
	 * @since 3.1
	 */
	@Override
	protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
		// since IResource is a more general way to compare java elements, we
		// use this as the preferred class for comparing objects.
		return new NonLocalUndoUserApprover(undoContext, this, new Object [] { getInputJavaElement() }, IResource.class);
	}

	/**
	 * Resets the foldings structure according to the folding
	 * preferences.
	 *
	 * @since 3.2
	 */
	public void resetProjection() {
		if (fProjectionModelUpdater != null) {
			fProjectionModelUpdater.initialize();
		}
	}

	/**
	 * Collapses all foldable members if supported by the folding
	 * structure provider.
	 *
	 * @since 3.2
	 */
	public void collapseMembers() {
		if (fProjectionModelUpdater instanceof IJavaFoldingStructureProviderExtension) {
			IJavaFoldingStructureProviderExtension extension= (IJavaFoldingStructureProviderExtension) fProjectionModelUpdater;
			extension.collapseMembers();
		}
	}

	/**
	 * Collapses all foldable comments if supported by the folding
	 * structure provider.
	 *
	 * @since 3.2
	 */
	public void collapseComments() {
		if (fProjectionModelUpdater instanceof IJavaFoldingStructureProviderExtension) {
			IJavaFoldingStructureProviderExtension extension= (IJavaFoldingStructureProviderExtension) fProjectionModelUpdater;
			extension.collapseComments();
		}
	}

	/**
	 * Returns the bracket matcher.
	 *
	 * @return the bracket matcher
	 * @since 3.8
	 */
	public JavaPairMatcher getBracketMatcher() {
		return fBracketMatcher;
	}

	/**
	 * Checks whether called from Outline view.
	 *
	 * @return <code>true</code> if called by Outline view
	 * @since 3.9
	 */
	private static boolean isCalledByOutline() {
		return OutlineAccessChecker.isCalledByOutline();
	}

	private static final class OutlineAccessChecker  {
		private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
		private static final String OUTLINE_NAME = ContentOutline.class.getCanonicalName();
		private static final Predicate<? super StackFrame> CHECK_OUTLINE = frame -> OUTLINE_NAME.equals(frame.getClassName());

		static boolean isCalledByOutline() {
			Boolean wasCalled = STACK_WALKER.walk(fs -> Boolean.valueOf(fs.anyMatch(CHECK_OUTLINE)));
			return wasCalled.booleanValue();
		}
	}

	@Override
	protected void installCodeMiningProviders() {
		if (JavaPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_CODEMINING_ENABLED)) {
			super.installCodeMiningProviders();
		} else {
			((JavaSourceViewer) getSourceViewer()).resetCodeMinings();
		}
	}

	/**
	 * Install Java code mining.
	 *
	 * @since 3.14
	 */
	private void installJavaCodeMining() {
		if (fJavaCodeMiningManager == null) {
			fJavaCodeMiningManager= new JavaCodeMiningManager();
			fJavaCodeMiningManager.install(this, (JavaSourceViewer) getSourceViewer(), getPreferenceStore());
		}
	}

	/**
	 * Uninstall Java code mining.
	 *
	 * @since 3.14
	 */
	private void uninstallJavaCodeMining() {
		if (fJavaCodeMiningManager != null) {
			fJavaCodeMiningManager.uninstall();
			fJavaCodeMiningManager= null;
		}
	}

	/**
	 * @return <code>true</code> if Java code mining is enabled.
	 *
	 * @since 3.14
	 */
	boolean isJavaCodeMiningEnabled() {
		if (!JavaPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_CODEMINING_ENABLED)) {
			return false;
		}
		// check if there is a registered Java code mining.
		ISourceViewer viewer= getViewer();
		if (viewer instanceof ISourceViewerExtension5) {
			return ((ISourceViewerExtension5) viewer).hasCodeMiningProviders();
		}
		return false;
	}

	/**
	 * Determines whether the preference change encoded by the given event changes the Java code mining.
	 *
	 * @param event the event to be investigated
	 * @return <code>true</code> if event causes a change
	 * @since 3.14
	 */
	protected boolean affectsJavaCodeMining(PropertyChangeEvent event) {
		return isJavaCodeMiningPreference(event.getProperty());
	}

	/**
	 * Note that by convention, a Java code mining preference starts with
	 * {@link PreferenceConstants#EDITOR_JAVA_CODEMINING_PREFIX}. It provides the capability for
	 * external plug-ins to contribute with a custom {@link ICodeMiningProvider} and refresh the Java
	 * Editor mining when preferences changed.
	 *
	 * @param property the name of the preference property that changed
	 *
	 * @return <code>true</code> if the given property is a Java code mining preference.
	 *
	 * @since 3.14
	 */
	private boolean isJavaCodeMiningPreference(String property) {
		return PreferenceConstants.EDITOR_CODEMINING_ENABLED.equals(property) || property.startsWith(PreferenceConstants.EDITOR_JAVA_CODEMINING_PREFIX);
	}

	private void clearEditorInfoInPartitionerManager(JavaEditor editor) {
		JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
		IJavaPartitionerManager pManager= textTools.getJavaPartitionerManager();
		if (pManager instanceof JavaPartitionerManager) {
			JavaPartitionerManager jpManager= (JavaPartitionerManager) pManager;
			jpManager.clearEditorInfo(editor);
		}
	}

	private void setEditorInfoInPartitionerManager(JavaEditor editor) {
		JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
		IJavaPartitionerManager pManager= textTools.getJavaPartitionerManager();
		if (pManager instanceof JavaPartitionerManager) {
			JavaPartitionerManager jpManager= (JavaPartitionerManager) pManager;
			jpManager.setEditorInfo(editor);
		}
	}

}
