Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package test0501; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.CollationElementIterator; import java.text.Collator; import java.text.RuleBasedCollator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; import java.util.StringTokenizer; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Preferences; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BidiSegmentEvent; import org.eclipse.swt.custom.BidiSegmentListener; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.ITextViewerExtension3; import org.eclipse.jface.text.ITextViewerExtension4; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.information.IInformationProvider; import org.eclipse.jface.text.information.IInformationProviderExtension2; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.LineChangeHover; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.ui.editors.text.DefaultEncodingSupport; import org.eclipse.ui.editors.text.IEncodingSupport; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPartService; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.help.WorkbenchHelp; import org.eclipse.ui.part.EditorActionBarContributor; import org.eclipse.ui.part.IShowInTargetList; import org.eclipse.ui.texteditor.AddTaskAction; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.DefaultAnnotation; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; import org.eclipse.ui.texteditor.DefaultRangeIndicator; import org.eclipse.ui.texteditor.ExtendedTextEditor; import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; import org.eclipse.ui.texteditor.ResourceAction; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextEditorAction; import org.eclipse.ui.texteditor.TextNavigationAction; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.views.contentoutline.ContentOutline; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.search.ui.SearchUI; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICodeAssist; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IImportContainer; import org.eclipse.jdt.core.IImportDeclaration; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.ui.IContextMenuConstants; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds; import org.eclipse.jdt.ui.actions.JavaSearchActionGroup; import org.eclipse.jdt.ui.actions.OpenEditorActionGroup; import org.eclipse.jdt.ui.actions.OpenViewActionGroup; import org.eclipse.jdt.ui.actions.ShowActionGroup; import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration; import org.eclipse.jdt.ui.text.JavaTextTools; import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup; import org.eclipse.jdt.internal.ui.actions.SelectionConverter; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.GoToNextPreviousMemberAction; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.SelectionHistory; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectEnclosingAction; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectHistoryAction; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectNextAction; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectPreviousAction; import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectionAction; import org.eclipse.jdt.internal.ui.search.FindOccurrencesEngine; import org.eclipse.jdt.internal.ui.text.HTMLTextPresenter; import org.eclipse.jdt.internal.ui.text.IJavaPartitions; import org.eclipse.jdt.internal.ui.text.JavaChangeHover; import org.eclipse.jdt.internal.ui.text.JavaPairMatcher; import org.eclipse.jdt.internal.ui.util.JavaUIHelp; import org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider; /** * Java specific text editor. */ public abstract class JavaEditor extends ExtendedTextEditor implements IViewPartInputProvider { /** * Internal implementation class for a change listener. * @since 3.0 */ protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener { /** * Installs this selection changed listener with the given selection provider. If * the selection provider is a post selection provider, post selection changed * events are the preferred choice, otherwise normal selection changed events * are requested. * * @param selectionProvider */ public void install(ISelectionProvider selectionProvider) { if (selectionProvider == null) return; if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.addPostSelectionChangedListener(this); } else { selectionProvider.addSelectionChangedListener(this); } } /** * Removes this selection changed listener from the given selection provider. * * @param selectionProvider */ public void uninstall(ISelectionProvider selectionProvider) { if (selectionProvider == null) return; if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } } /** * Updates the Java outline page selection and this editor's range indicator. * * @since 3.0 */ private class EditorSelectionChangedListener extends AbstractSelectionChangedListener { /* * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event) { selectionChanged(); } public void selectionChanged() { ISourceReference element = computeHighlightRangeSourceReference(); synchronizeOutlinePage(element); setSelection(element, false); updateStatusLine(); updateOccurrences(); } } /** * Updates the selection in the editor's widget with the selection of the outline page. */ class OutlineSelectionChangedListener extends AbstractSelectionChangedListener { public void selectionChanged(SelectionChangedEvent event) { doSelectionChanged(event); } } /* * Link mode. */ class MouseClickListener implements KeyListener, MouseListener, MouseMoveListener, FocusListener, PaintListener, IPropertyChangeListener, IDocumentListener, ITextInputListener { /** The session is active. */ private boolean fActive; /** The currently active style range. */ private IRegion fActiveRegion; /** The currently active style range as position. */ private Position fRememberedPosition; /** The hand cursor. */ private Cursor fCursor; /** The link color. */ private Color fColor; /** The key modifier mask. */ private int fKeyModifierMask; /** * Style ranges before link mode. * @since 3.0 */ private StyleRange[] fOldStyleRanges; /** * Link mode style ranges region. * @since 3.0 */ IRegion fOldStyleRangeRegion; public void deactivate() { deactivate(false); } public void deactivate(boolean redrawAll) { if (!fActive) return; repairRepresentation(redrawAll); fActive = false; } public void install() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; StyledText text = sourceViewer.getTextWidget(); if (text == null || text.isDisposed()) return; updateColor(sourceViewer); sourceViewer.addTextInputListener(this); IDocument document = sourceViewer.getDocument(); if (document != null) document.addDocumentListener(this); text.addKeyListener(this); text.addMouseListener(this); text.addMouseMoveListener(this); text.addFocusListener(this); text.addPaintListener(this); updateKeyModifierMask(); IPreferenceStore preferenceStore = getPreferenceStore(); preferenceStore.addPropertyChangeListener(this); } private void updateKeyModifierMask() { String modifiers = getPreferenceStore().getString(BROWSER_LIKE_LINKS_KEY_MODIFIER); fKeyModifierMask = computeStateMask(modifiers); if (fKeyModifierMask == -1) { // Fallback to stored state mask fKeyModifierMask = getPreferenceStore().getInt(BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK); } } private int computeStateMask(String modifiers) { if (modifiers == null) return -1; if (modifiers.length() == 0) return SWT.NONE; int stateMask = 0; StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$ while (modifierTokenizer.hasMoreTokens()) { int modifier = EditorUtility.findLocalizedModifier(modifierTokenizer.nextToken()); if (modifier == 0 || (stateMask & modifier) == modifier) return -1; stateMask = stateMask | modifier; } return stateMask; } public void uninstall() { if (fColor != null) { fColor.dispose(); fColor = null; } if (fCursor != null) { fCursor.dispose(); fCursor = null; } ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; sourceViewer.removeTextInputListener(this); IDocument document = sourceViewer.getDocument(); if (document != null) document.removeDocumentListener(this); IPreferenceStore preferenceStore = getPreferenceStore(); if (preferenceStore != null) preferenceStore.removePropertyChangeListener(this); StyledText text = sourceViewer.getTextWidget(); if (text == null || text.isDisposed()) return; text.removeKeyListener(this); text.removeMouseListener(this); text.removeMouseMoveListener(this); text.removeFocusListener(this); text.removePaintListener(this); } /* * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(JavaEditor.LINK_COLOR)) { ISourceViewer viewer = getSourceViewer(); if (viewer != null) updateColor(viewer); } else if (event.getProperty().equals(BROWSER_LIKE_LINKS_KEY_MODIFIER)) { updateKeyModifierMask(); } } private void updateColor(ISourceViewer viewer) { if (fColor != null) fColor.dispose(); StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; Display display = text.getDisplay(); fColor = createColor(getPreferenceStore(), JavaEditor.LINK_COLOR, display); } /** * Creates a color from the information stored in the given preference store. * Returns <code>null</code> if there is no such information available. */ private Color createColor(IPreferenceStore store, String key, Display display) { RGB rgb = null; if (store.contains(key)) { if (store.isDefault(key)) rgb = PreferenceConverter.getDefaultColor(store, key); else rgb = PreferenceConverter.getColor(store, key); if (rgb != null) return new Color(display, rgb); } return null; } private void repairRepresentation() { repairRepresentation(false); } private void repairRepresentation(boolean redrawAll) { if (fActiveRegion == null) return; int offset = fActiveRegion.getOffset(); int length = fActiveRegion.getLength(); fActiveRegion = null; ISourceViewer viewer = getSourceViewer(); if (viewer != null) { resetCursor(viewer); // remove style if (!redrawAll && viewer instanceof ITextViewerExtension2) ((ITextViewerExtension2) viewer).invalidateTextPresentation(offset, length); else viewer.invalidateTextPresentation(); if (viewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) viewer; offset = extension.modelOffset2WidgetOffset(offset); } else { offset -= viewer.getVisibleRegion().getOffset(); } try { StyledText text = viewer.getTextWidget(); // Removes style text.replaceStyleRanges(fOldStyleRangeRegion.getOffset(), fOldStyleRangeRegion.getLength(), fOldStyleRanges); // text.replaceStyleRanges(offset, length, fOldStyleRanges); // Causes underline to disappear text.redrawRange(offset, length, false); } catch (IllegalArgumentException x) { JavaPlugin.log(x); } } } // will eventually be replaced by a method provided by jdt.core private IRegion selectWord(IDocument document, int anchor) { try { int offset = anchor; char c; while (offset >= 0) { c = document.getChar(offset); if (!Character.isJavaIdentifierPart(c)) break; --offset; } int start = offset; offset = anchor; int length = document.getLength(); while (offset < length) { c = document.getChar(offset); if (!Character.isJavaIdentifierPart(c)) break; ++offset; } int end = offset; if (start == end) return new Region(start, 0); else return new Region(start + 1, end - start - 1); } catch (BadLocationException x) { return null; } } IRegion getCurrentTextRegion(ISourceViewer viewer) { int offset = getCurrentTextOffset(viewer); if (offset == -1) return null; IJavaElement input = SelectionConverter.getInput(JavaEditor.this); if (input == null) return null; try { IJavaElement[] elements = null; synchronized (input) { elements = ((ICodeAssist) input).codeSelect(offset, 0); } if (elements == null || elements.length == 0) return null; return selectWord(viewer.getDocument(), offset); } catch (JavaModelException e) { return null; } } private int getCurrentTextOffset(ISourceViewer viewer) { try { StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return -1; Display display = text.getDisplay(); Point absolutePosition = display.getCursorLocation(); Point relativePosition = text.toControl(absolutePosition); int widgetOffset = text.getOffsetAtLocation(relativePosition); if (viewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) viewer; return extension.widgetOffset2ModelOffset(widgetOffset); } else { return widgetOffset + viewer.getVisibleRegion().getOffset(); } } catch (IllegalArgumentException e) { return -1; } } private void highlightRegion(ISourceViewer viewer, IRegion region) { if (region.equals(fActiveRegion)) return; repairRepresentation(); StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; // highlight region int offset = 0; int length = 0; if (viewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) viewer; IRegion widgetRange = extension.modelRange2WidgetRange(region); if (widgetRange == null) return; offset = widgetRange.getOffset(); length = widgetRange.getLength(); } else { offset = region.getOffset() - viewer.getVisibleRegion().getOffset(); length = region.getLength(); } fOldStyleRanges = text.getStyleRanges(offset, length); fOldStyleRangeRegion = new Region(offset, length); applyForgroundStyle(text, offset, length); text.redrawRange(offset, length, false); fActiveRegion = region; } private void applyForgroundStyle(StyledText fTextWidget, int offset, int length) { StyleRange[] styleRanges = fTextWidget.getStyleRanges(offset, length); ArrayList newStyleRanges = new ArrayList(styleRanges.length + 10); int rangeOffset = offset; for (int i = 0, max = styleRanges.length; i < max; i++) { StyleRange sr = styleRanges[i]; if (rangeOffset < sr.start) { // Unstyled range StyleRange usr = new StyleRange(rangeOffset, sr.start - rangeOffset, fColor, null); newStyleRanges.add(usr); } rangeOffset = sr.start + sr.length; // Important: Must create a new one sr = new StyleRange(sr.start, sr.length, fColor, sr.background, sr.fontStyle); newStyleRanges.add(sr); } int endOffset = offset + length; if (rangeOffset < endOffset) { // Last unstyled range StyleRange usr = new StyleRange(rangeOffset, endOffset - rangeOffset, fColor, null); newStyleRanges.add(usr); } styleRanges = (StyleRange[]) newStyleRanges.toArray(new StyleRange[newStyleRanges.size()]); fTextWidget.replaceStyleRanges(offset, length, styleRanges); } private void activateCursor(ISourceViewer viewer) { StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; Display display = text.getDisplay(); if (fCursor == null) fCursor = new Cursor(display, SWT.CURSOR_HAND); text.setCursor(fCursor); } private void resetCursor(ISourceViewer viewer) { StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) text.setCursor(null); if (fCursor != null) { fCursor.dispose(); fCursor = null; } } /* * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent) */ public void keyPressed(KeyEvent event) { if (fActive) { deactivate(); return; } if (event.keyCode != fKeyModifierMask) { deactivate(); return; } fActive = true; // removed for #25871 // // ISourceViewer viewer= getSourceViewer(); // if (viewer == null) // return; // // IRegion region= getCurrentTextRegion(viewer); // if (region == null) // return; // // highlightRegion(viewer, region); // activateCursor(viewer); } /* * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent) */ public void keyReleased(KeyEvent event) { if (!fActive) return; deactivate(); } /* * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) */ public void mouseDoubleClick(MouseEvent e) { } /* * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) */ public void mouseDown(MouseEvent event) { if (!fActive) return; if (event.stateMask != fKeyModifierMask) { deactivate(); return; } if (event.button != 1) { deactivate(); return; } } /* * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) */ public void mouseUp(MouseEvent e) { if (!fActive) return; if (e.button != 1) { deactivate(); return; } boolean wasActive = fCursor != null; deactivate(); if (wasActive) { IAction action = getAction("OpenEditor"); //$NON-NLS-1$ if (action != null) action.run(); } } /* * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) */ public void mouseMove(MouseEvent event) { if (event.widget instanceof Control && !((Control) event.widget).isFocusControl()) { deactivate(); return; } if (!fActive) { if (event.stateMask != fKeyModifierMask) return; // modifier was already pressed fActive = true; } ISourceViewer viewer = getSourceViewer(); if (viewer == null) { deactivate(); return; } StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) { deactivate(); return; } if ((event.stateMask & SWT.BUTTON1) != 0 && text.getSelectionCount() != 0) { deactivate(); return; } IRegion region = getCurrentTextRegion(viewer); if (region == null || region.getLength() == 0) { repairRepresentation(); return; } highlightRegion(viewer, region); activateCursor(viewer); } /* * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent) */ public void focusGained(FocusEvent e) { } /* * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent) */ public void focusLost(FocusEvent event) { deactivate(); } /* * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { if (fActive && fActiveRegion != null) { fRememberedPosition = new Position(fActiveRegion.getOffset(), fActiveRegion.getLength()); try { event.getDocument().addPosition(fRememberedPosition); } catch (BadLocationException x) { fRememberedPosition = null; } } } /* * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { if (fRememberedPosition != null) { if (!fRememberedPosition.isDeleted()) { event.getDocument().removePosition(fRememberedPosition); fActiveRegion = new Region(fRememberedPosition.getOffset(), fRememberedPosition.getLength()); fRememberedPosition = null; ISourceViewer viewer = getSourceViewer(); if (viewer != null) { StyledText widget = viewer.getTextWidget(); if (widget != null && !widget.isDisposed()) { widget.getDisplay().asyncExec(new Runnable() { public void run() { deactivate(); } }); } } } else { fActiveRegion = null; fRememberedPosition = null; deactivate(); } } } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput == null) return; deactivate(); oldInput.removeDocumentListener(this); } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput == null) return; newInput.addDocumentListener(this); } /* * @see PaintListener#paintControl(PaintEvent) */ public void paintControl(PaintEvent event) { if (fActiveRegion == null) return; ISourceViewer viewer = getSourceViewer(); if (viewer == null) return; StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; int offset = 0; int length = 0; if (viewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) viewer; IRegion widgetRange = extension.modelRange2WidgetRange(new Region(offset, length)); if (widgetRange == null) return; offset = widgetRange.getOffset(); length = widgetRange.getLength(); } else { IRegion region = viewer.getVisibleRegion(); if (!includes(region, fActiveRegion)) return; offset = fActiveRegion.getOffset() - region.getOffset(); length = fActiveRegion.getLength(); } // support for bidi Point minLocation = getMinimumLocation(text, offset, length); Point maxLocation = getMaximumLocation(text, offset, length); int x1 = minLocation.x; int x2 = minLocation.x + maxLocation.x - minLocation.x - 1; int y = minLocation.y + text.getLineHeight() - 1; GC gc = event.gc; if (fColor != null && !fColor.isDisposed()) gc.setForeground(fColor); gc.drawLine(x1, y, x2, y); } private boolean includes(IRegion region, IRegion position) { return position.getOffset() >= region.getOffset() && position.getOffset() + position.getLength() <= region.getOffset() + region.getLength(); } private Point getMinimumLocation(StyledText text, int offset, int length) { Point minLocation = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); for (int i = 0; i <= length; i++) { Point location = text.getLocationAtOffset(offset + i); if (location.x < minLocation.x) minLocation.x = location.x; if (location.y < minLocation.y) minLocation.y = location.y; } return minLocation; } private Point getMaximumLocation(StyledText text, int offset, int length) { Point maxLocation = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE); for (int i = 0; i <= length; i++) { Point location = text.getLocationAtOffset(offset + i); if (location.x > maxLocation.x) maxLocation.x = location.x; if (location.y > maxLocation.y) maxLocation.y = location.y; } return maxLocation; } } /** * This action dispatches into two behaviours: If there is no current text * hover, the javadoc is displayed using information presenter. If there is * a current text hover, it is converted into a information presenter in * order to make it sticky. */ class InformationDispatchAction extends TextEditorAction { /** The wrapped text operation action. */ private final TextOperationAction fTextOperationAction; /** * Creates a dispatch action. */ public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) { super(resourceBundle, prefix, JavaEditor.this); if (textOperationAction == null) throw new IllegalArgumentException(); fTextOperationAction = textOperationAction; } /* * @see org.eclipse.jface.action.IAction#run() */ public void run() { /** * Information provider used to present the information. * * @since 3.0 */ class InformationProvider implements IInformationProvider, IInformationProviderExtension2 { private IRegion fHoverRegion; private String fHoverInfo; private IInformationControlCreator fControlCreator; InformationProvider(IRegion hoverRegion, String hoverInfo, IInformationControlCreator controlCreator) { fHoverRegion = hoverRegion; fHoverInfo = hoverInfo; fControlCreator = controlCreator; } /* * @see org.eclipse.jface.text.information.IInformationProvider#getSubject(org.eclipse.jface.text.ITextViewer, int) */ public IRegion getSubject(ITextViewer textViewer, int invocationOffset) { return fHoverRegion; } /* * @see org.eclipse.jface.text.information.IInformationProvider#getInformation(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) */ public String getInformation(ITextViewer textViewer, IRegion subject) { return fHoverInfo; } /* * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator() * @since 3.0 */ public IInformationControlCreator getInformationPresenterControlCreator() { return fControlCreator; } } ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { fTextOperationAction.run(); return; } if (sourceViewer instanceof ITextViewerExtension4) { ITextViewerExtension4 extension4 = (ITextViewerExtension4) sourceViewer; if (extension4.moveFocusToWidgetToken()) return; } if (!(sourceViewer instanceof ITextViewerExtension2)) { fTextOperationAction.run(); return; } ITextViewerExtension2 textViewerExtension2 = (ITextViewerExtension2) sourceViewer; // does a text hover exist? ITextHover textHover = textViewerExtension2.getCurrentTextHover(); if (textHover == null) { fTextOperationAction.run(); return; } Point hoverEventLocation = textViewerExtension2.getHoverEventLocation(); int offset = computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y); if (offset == -1) { fTextOperationAction.run(); return; } try { // get the text hover content String contentType = TextUtilities.getContentType(sourceViewer.getDocument(), IJavaPartitions.JAVA_PARTITIONING, offset); IRegion hoverRegion = textHover.getHoverRegion(sourceViewer, offset); if (hoverRegion == null) return; String hoverInfo = textHover.getHoverInfo(sourceViewer, hoverRegion); IInformationControlCreator controlCreator = null; if (textHover instanceof IInformationProviderExtension2) controlCreator = ((IInformationProviderExtension2) textHover) .getInformationPresenterControlCreator(); IInformationProvider informationProvider = new InformationProvider(hoverRegion, hoverInfo, controlCreator); fInformationPresenter.setOffset(offset); fInformationPresenter.setInformationProvider(informationProvider, contentType); fInformationPresenter.showInformation(); } catch (BadLocationException e) { } } // modified version from TextViewer private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) { StyledText styledText = textViewer.getTextWidget(); IDocument document = textViewer.getDocument(); if (document == null) return -1; try { int widgetLocation = styledText.getOffsetAtLocation(new Point(x, y)); if (textViewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) textViewer; return extension.widgetOffset2ModelOffset(widgetLocation); } else { IRegion visibleRegion = textViewer.getVisibleRegion(); return widgetLocation + visibleRegion.getOffset(); } } catch (IllegalArgumentException e) { return -1; } } } static protected class AnnotationAccess extends DefaultMarkerAnnotationAccess { public AnnotationAccess(MarkerAnnotationPreferences markerAnnotationPreferences) { super(markerAnnotationPreferences); } /* * @see org.eclipse.jface.text.source.IAnnotationAccess#getType(org.eclipse.jface.text.source.Annotation) */ public Object getType(Annotation annotation) { if (annotation instanceof IJavaAnnotation) { IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation; if (javaAnnotation.isRelevant()) return javaAnnotation.getAnnotationType(); return null; } return super.getType(annotation); } /* * @see org.eclipse.jface.text.source.IAnnotationAccess#isMultiLine(org.eclipse.jface.text.source.Annotation) */ public boolean isMultiLine(Annotation annotation) { return true; } /* * @see org.eclipse.jface.text.source.IAnnotationAccess#isTemporary(org.eclipse.jface.text.source.Annotation) */ public boolean isTemporary(Annotation annotation) { if (annotation instanceof IJavaAnnotation) { IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation; if (javaAnnotation.isRelevant()) return javaAnnotation.isTemporary(); } return false; } } private class PropertyChangeListener implements org.eclipse.core.runtime.Preferences.IPropertyChangeListener { /* * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) { handlePreferencePropertyChanged(event); } } /** * This action implements smart home. * * Instead of going to the start of a line it does the following: * * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it, taking JavaDoc and multi-line comments into account. * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line * - if the caret is at the beginning of the line see first case. * * @since 3.0 */ protected class SmartLineStartAction extends LineStartAction { /** * Creates a new smart line start action * * @param textWidget the styled text widget * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected */ public SmartLineStartAction(final StyledText textWidget, final boolean doSelect) { super(textWidget, doSelect); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor.LineStartAction#getLineStartPosition(java.lang.String, int, java.lang.String) */ protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) { String type = IDocument.DEFAULT_CONTENT_TYPE; try { type = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset).getType(); } catch (BadLocationException exception) { // Should not happen } int index = super.getLineStartPosition(document, line, length, offset); if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) { if (index < length - 1 && line.charAt(index) == '*' && line.charAt(index + 1) != '/') { do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } else { if (index < length - 1 && line.charAt(index) == '/' && line.charAt(++index) == '/') { do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } return index; } } /** * Text navigation action to navigate to the next sub-word. * * @since 3.0 */ protected abstract class NextSubWordAction extends TextNavigationAction { /** Collator to determine the sub-word boundaries */ private final RuleBasedCollator fCollator = (RuleBasedCollator) Collator.getInstance(); /** * Creates a new next sub-word action. * * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. */ protected NextSubWordAction(int code) { super(getSourceViewer().getTextWidget(), code); // Only compare upper-/lower case fCollator.setStrength(Collator.TERTIARY); } /* * @see org.eclipse.jface.action.IAction#run() */ public void run() { try { final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); // Check whether we are in a java code partititon and the preference is enabled final IPreferenceStore store = getPreferenceStore(); final ITypedRegion region = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, position); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } // Check whether right hand character of caret is valid identifier start if (Character.isJavaIdentifierStart(document.getChar(position))) { int offset = 0; int order = CollationElementIterator.NULLORDER; short previous = Short.MAX_VALUE; short next = Short.MAX_VALUE; // Acquire collator for partition around caret final String buffer = document.get(position, region.getOffset() + region.getLength() - position); final CollationElementIterator iterator = fCollator.getCollationElementIterator(buffer); // Iterate to first upper-case character do { // Check whether we reached end of word offset = iterator.getOffset(); if (!Character.isJavaIdentifierPart(document.getChar(position + offset))) throw new BadLocationException(); // Test next characters order = iterator.next(); next = CollationElementIterator.tertiaryOrder(order); if (next <= previous) previous = next; else break; } while (order != CollationElementIterator.NULLORDER); // Check for leading underscores position += offset; if (Character.getType(document.getChar(position - 1)) != Character.CONNECTOR_PUNCTUATION) { setCaretPosition(position); getTextWidget().showSelection(); fireSelectionChanged(); return; } } } catch (BadLocationException exception) { // Use default behavior } super.run(); } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the next sub-word. * * @since 3.0 */ protected class NavigateNextSubWordAction extends NextSubWordAction { /** * Creates a new navigate next sub-word action. */ public NavigateNextSubWordAction() { super(ST.WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text operation action to delete the next sub-word. * * @since 3.0 */ protected class DeleteNextSubWordAction extends NextSubWordAction { /** * Creates a new delete next sub-word action. */ public DeleteNextSubWordAction() { super(ST.DELETE_WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final int caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); try { viewer.getDocument().replace(caret, position - caret, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } } /** * Text operation action to select the next sub-word. * * @since 3.0 */ protected class SelectNextSubWordAction extends NextSubWordAction { /** * Creates a new select next sub-word action. */ public SelectNextSubWordAction() { super(ST.SELECT_WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) text.setSelectionRange(selection.y, offset - selection.y); else text.setSelectionRange(selection.x, offset - selection.x); } } } /** * Text navigation action to navigate to the previous sub-word. * * @since 3.0 */ protected abstract class PreviousSubWordAction extends TextNavigationAction { /** Collator to determine the sub-word boundaries */ private final RuleBasedCollator fCollator = (RuleBasedCollator) Collator.getInstance(); /** * Creates a new previous sub-word action. * * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. */ protected PreviousSubWordAction(final int code) { super(getSourceViewer().getTextWidget(), code); // Only compare upper-/lower case fCollator.setStrength(Collator.TERTIARY); } /* * @see org.eclipse.jface.action.IAction#run() */ public void run() { try { final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()) - 1; // Check whether we are in a java code partititon and the preference is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } // Ignore trailing white spaces char character = document.getChar(position); while (position > 0 && Character.isWhitespace(character)) { --position; character = document.getChar(position); } // Check whether left hand character of caret is valid identifier part if (Character.isJavaIdentifierPart(character)) { int offset = 0; int order = CollationElementIterator.NULLORDER; short previous = Short.MAX_VALUE; short next = Short.MAX_VALUE; // Acquire collator for partition around caret final ITypedRegion region = TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, position); final String buffer = document.get(region.getOffset(), position - region.getOffset() + 1); final CollationElementIterator iterator = fCollator.getCollationElementIterator(buffer); // Iterate to first upper-case character iterator.setOffset(buffer.length() - 1); do { // Check whether we reached begin of word or single upper-case start offset = iterator.getOffset(); character = document.getChar(region.getOffset() + offset); if (!Character.isJavaIdentifierPart(character)) throw new BadLocationException(); else if (Character.isUpperCase(character)) { ++offset; break; } // Test next characters order = iterator.previous(); next = CollationElementIterator.tertiaryOrder(order); if (next <= previous) previous = next; else break; } while (order != CollationElementIterator.NULLORDER); // Check left character for multiple upper-case characters position = position - buffer.length() + offset - 1; character = document.getChar(position); while (position >= 0 && Character.isUpperCase(character)) character = document.getChar(--position); setCaretPosition(position + 1); getTextWidget().showSelection(); fireSelectionChanged(); return; } } catch (BadLocationException exception) { // Use default behavior } super.run(); } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the previous sub-word. * * @since 3.0 */ protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new navigate previous sub-word action. */ public NavigatePreviousSubWordAction() { super(ST.WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text operation action to delete the previous sub-word. * * @since 3.0 */ protected class DeletePreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new delete previous sub-word action. */ public DeletePreviousSubWordAction() { super(ST.DELETE_WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final int caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); try { viewer.getDocument().replace(position, caret - position, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } } /** * Text operation action to select the previous sub-word. * * @since 3.0 */ protected class SelectPreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new select previous sub-word action. */ public SelectPreviousSubWordAction() { super(ST.SELECT_WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) text.setSelectionRange(selection.y, offset - selection.y); else text.setSelectionRange(selection.x, offset - selection.x); } } } /** * Quick format action to format the enclosing java element. * <p> * The quick format action works as follows: * <ul> * <li>If there is no selection and the caret is positioned on a Java element, * only this element is formatted. If the element has some accompanying comment, * then the comment is formatted as well.</li> * <li>If the selection spans one or more partitions of the document, then all * partitions covered by the selection are entirely formatted.</li> * <p> * Partitions at the end of the selection are not completed, except for comments. * * @since 3.0 */ protected class QuickFormatAction extends Action { /* * @see org.eclipse.jface.action.IAction#run() */ public void run() { final JavaSourceViewer viewer = (JavaSourceViewer) getSourceViewer(); if (viewer.isEditable()) { final Point selection = viewer.rememberSelection(); try { viewer.setRedraw(false); final String type = TextUtilities.getContentType(viewer.getDocument(), IJavaPartitions.JAVA_PARTITIONING, selection.x); if (type.equals(IDocument.DEFAULT_CONTENT_TYPE) && selection.y == 0) { try { final IJavaElement element = getElementAt(selection.x, true); if (element != null && element.exists()) { final int kind = element.getElementType(); if (kind == IJavaElement.TYPE || kind == IJavaElement.METHOD || kind == IJavaElement.INITIALIZER) { final ISourceReference reference = (ISourceReference) element; final ISourceRange range = reference.getSourceRange(); if (range != null) { viewer.setSelectedRange(range.getOffset(), range.getLength()); viewer.doOperation(ISourceViewer.FORMAT); } } } } catch (JavaModelException exception) { // Should not happen } } else { viewer.setSelectedRange(selection.x, 1); viewer.doOperation(ISourceViewer.FORMAT); } } catch (BadLocationException exception) { // Can not happen } finally { viewer.setRedraw(true); viewer.restoreSelection(); } } } } /** Preference key for the link color */ protected final static String LINK_COLOR = PreferenceConstants.EDITOR_LINK_COLOR; /** Preference key for matching brackets */ protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS; /** Preference key for matching brackets color */ protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; /** Preference key for compiler task tags */ private final static String COMPILER_TASK_TAGS = JavaCore.COMPILER_TASK_TAGS; /** Preference key for browser like links */ private final static String BROWSER_LIKE_LINKS = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS; /** Preference key for key modifier of browser like links */ private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER; /** * Preference key for key modifier mask of browser like links. * The value is only used if the value of <code>EDITOR_BROWSER_LIKE_LINKS</code> * cannot be resolved to valid SWT modifier bits. * * @since 2.1.1 */ private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK; protected final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' }; /** The outline page */ protected JavaOutlinePage fOutlinePage; /** Outliner context menu Id */ protected String fOutlinerContextMenuId; /** * The editor selection changed listener. * * @since 3.0 */ private EditorSelectionChangedListener fEditorSelectionChangedListener; /** The selection changed listener */ protected AbstractSelectionChangedListener fOutlineSelectionChangedListener = new OutlineSelectionChangedListener(); /** The editor's bracket matcher */ protected JavaPairMatcher fBracketMatcher = new JavaPairMatcher(BRACKETS); /** This editor's encoding support */ private DefaultEncodingSupport fEncodingSupport; /** The mouse listener */ private MouseClickListener fMouseListener; /** The information presenter. */ private InformationPresenter fInformationPresenter; /** History for structure select action */ private SelectionHistory fSelectionHistory; /** The preference property change listener for java core. */ private org.eclipse.core.runtime.Preferences.IPropertyChangeListener fPropertyChangeListener = new PropertyChangeListener(); /** * Indicates whether this editor is about to update any annotation views. * @since 3.0 */ private boolean fIsUpdatingAnnotationViews = false; /** * The marker that served as last target for a goto marker request. * @since 3.0 */ private IMarker fLastMarkerTarget = null; protected CompositeActionGroup fActionGroups; private CompositeActionGroup fContextMenuGroup; /** * Holds the current occurrence annotations. * @since 3.0 */ private ArrayList fOccurrenceAnnotations = new ArrayList(); /** * Counts the number of background computation requests. * @since 3.0 */ private volatile int fComputeCount; private boolean fMarkOccurrenceAnnotations; /** * Returns the most narrow java element including the given offset. * * @param offset the offset inside of the requested element * @return the most narrow java element */ abstract protected IJavaElement getElementAt(int offset); /** * Returns the java element of this editor's input corresponding to the given IJavaElement */ abstract protected IJavaElement getCorrespondingElement(IJavaElement element); /** * Sets the input of the editor's outline page. */ abstract protected void setOutlinePageInput(JavaOutlinePage page, IEditorInput input); /** * Default constructor. */ public JavaEditor() { super(); JavaTextTools textTools = JavaPlugin.getDefault().getJavaTextTools(); setSourceViewerConfiguration( new JavaSourceViewerConfiguration(textTools, this, IJavaPartitions.JAVA_PARTITIONING)); setRangeIndicator(new DefaultRangeIndicator()); IPreferenceStore store = JavaPlugin.getDefault().getPreferenceStore(); setPreferenceStore(store); setKeyBindingScopes(new String[] { "org.eclipse.jdt.ui.javaEditorScope" }); //$NON-NLS-1$ fMarkOccurrenceAnnotations = store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); } /* * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int) */ protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRuler verticalRuler, int styles) { ISourceViewer viewer = createJavaSourceViewer(parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles); StyledText text = viewer.getTextWidget(); text.addBidiSegmentListener(new BidiSegmentListener() { public void lineGetSegments(BidiSegmentEvent event) { event.segments = getBidiLineSegments(event.lineOffset, event.lineText); } }); JavaUIHelp.setHelp(this, text, IJavaHelpContextIds.JAVA_EDITOR); // ensure source viewer decoration support has been created and configured getSourceViewerDecorationSupport(viewer); return viewer; } /* * @see org.eclipse.ui.texteditor.ExtendedTextEditor#createAnnotationAccess() */ protected IAnnotationAccess createAnnotationAccess() { return new AnnotationAccess(new MarkerAnnotationPreferences()); } public final ISourceViewer getViewer() { return getSourceViewer(); } /* * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int) */ protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles) { return new JavaSourceViewer(parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles); } /* * @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent) */ protected boolean affectsTextPresentation(PropertyChangeEvent event) { JavaTextTools textTools = JavaPlugin.getDefault().getJavaTextTools(); return textTools.affectsBehavior(event); } /** * Sets the outliner's context menu ID. */ protected void setOutlinerContextMenuId(String menuId) { fOutlinerContextMenuId = menuId; } /** * Returns the standard action group of this editor. */ protected ActionGroup getActionGroup() { return fActionGroups; } /* * @see AbstractTextEditor#editorContextMenuAboutToShow */ public void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, new Separator(IContextMenuConstants.GROUP_OPEN)); menu.insertAfter(IContextMenuConstants.GROUP_OPEN, new GroupMarker(IContextMenuConstants.GROUP_SHOW)); ActionContext context = new ActionContext(getSelectionProvider().getSelection()); fContextMenuGroup.setContext(context); fContextMenuGroup.fillContextMenu(menu); fContextMenuGroup.setContext(null); } /** * Creates the outline page used with this editor. */ protected JavaOutlinePage createOutlinePage() { JavaOutlinePage page = new JavaOutlinePage(fOutlinerContextMenuId, this); fOutlineSelectionChangedListener.install(page); setOutlinePageInput(page, getEditorInput()); return page; } /** * Informs the editor that its outliner has been closed. */ public void outlinePageClosed() { if (fOutlinePage != null) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage = null; resetHighlightRange(); } } /** * Synchronizes the outliner selection with the given element * position in the editor. * * @param element the java element to select */ protected void synchronizeOutlinePage(ISourceReference element) { synchronizeOutlinePage(element, true); } /** * Synchronizes the outliner selection with the given element * position in the editor. * * @param element the java element to select * @param checkIfOutlinePageActive <code>true</code> if check for active outline page needs to be done */ protected void synchronizeOutlinePage(ISourceReference element, boolean checkIfOutlinePageActive) { if (fOutlinePage != null && element != null && !(checkIfOutlinePageActive && isJavaOutlinePageActive())) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage.select(element); fOutlineSelectionChangedListener.install(fOutlinePage); } } /** * Synchronizes the outliner selection with the actual cursor * position in the editor. */ public void synchronizeOutlinePageSelection() { synchronizeOutlinePage(computeHighlightRangeSourceReference()); } /* * Get the desktop's StatusLineManager */ protected IStatusLineManager getStatusLineManager() { IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor(); if (contributor instanceof EditorActionBarContributor) { return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); } return null; } /* * @see AbstractTextEditor#getAdapter(Class) */ public Object getAdapter(Class required) { if (IContentOutlinePage.class.equals(required)) { if (fOutlinePage == null) fOutlinePage = createOutlinePage(); return fOutlinePage; } if (IEncodingSupport.class.equals(required)) return fEncodingSupport; if (required == IShowInTargetList.class) { return new IShowInTargetList() { public String[] getShowInTargetIds() { return new String[] { JavaUI.ID_PACKAGES, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV }; } }; } return super.getAdapter(required); } protected void setSelection(ISourceReference reference, boolean moveCursor) { ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof TextSelection) { TextSelection textSelection = (TextSelection) selection; // PR 39995: [navigation] Forward history cleared after going back in navigation history: // mark only in navigation history if the cursor is being moved (which it isn't if // this is called from a PostSelectionEvent that should only update the magnet) if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) markInNavigationHistory(); } if (reference != null) { StyledText textWidget = null; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) textWidget = sourceViewer.getTextWidget(); if (textWidget == null) return; try { ISourceRange range = reference.getSourceRange(); if (range == null) return; int offset = range.getOffset(); int length = range.getLength(); if (offset < 0 || length < 0) return; setHighlightRange(offset, length, moveCursor); if (!moveCursor) return; offset = -1; length = -1; if (reference instanceof IMember) { range = ((IMember) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } } else if (reference instanceof IImportDeclaration) { String name = ((IImportDeclaration) reference).getElementName(); if (name != null && name.length() > 0) { String content = reference.getSource(); if (content != null) { offset = range.getOffset() + content.indexOf(name); length = name.length(); } } } else if (reference instanceof IPackageDeclaration) { String name = ((IPackageDeclaration) reference).getElementName(); if (name != null && name.length() > 0) { String content = reference.getSource(); if (content != null) { offset = range.getOffset() + content.indexOf(name); length = name.length(); } } } if (offset > -1 && length > 0) { try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } markInNavigationHistory(); } } catch (JavaModelException x) { } catch (IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } public void setSelection(IJavaElement element) { if (element == null || element instanceof ICompilationUnit || element instanceof IClassFile) { /* * If the element is an ICompilationUnit this unit is either the input * of this editor or not being displayed. In both cases, nothing should * happened. (http://dev.eclipse.org/bugs/show_bug.cgi?id=5128) */ return; } IJavaElement corresponding = getCorrespondingElement(element); if (corresponding instanceof ISourceReference) { ISourceReference reference = (ISourceReference) corresponding; // set hightlight range setSelection(reference, true); // set outliner selection if (fOutlinePage != null) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage.select(reference); fOutlineSelectionChangedListener.install(fOutlinePage); } } } protected void doSelectionChanged(SelectionChangedEvent event) { ISourceReference reference = null; ISelection selection = event.getSelection(); Iterator iter = ((IStructuredSelection) selection).iterator(); while (iter.hasNext()) { Object o = iter.next(); if (o instanceof ISourceReference) { reference = (ISourceReference) o; break; } } if (!isActivePart() && JavaPlugin.getActivePage() != null) JavaPlugin.getActivePage().bringToTop(this); setSelection(reference, !isActivePart()); } /* * @see AbstractTextEditor#adjustHighlightRange(int, int) */ protected void adjustHighlightRange(int offset, int length) { try { IJavaElement element = getElementAt(offset); while (element instanceof ISourceReference) { ISourceRange range = ((ISourceReference) element).getSourceRange(); if (offset < range.getOffset() + range.getLength() && range.getOffset() < offset + length) { setHighlightRange(range.getOffset(), range.getLength(), true); if (fOutlinePage != null) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage.select((ISourceReference) element); fOutlineSelectionChangedListener.install(fOutlinePage); } return; } element = element.getParent(); } } catch (JavaModelException x) { JavaPlugin.log(x.getStatus()); } resetHighlightRange(); } protected boolean isActivePart() { IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } private boolean isJavaOutlinePageActive() { IWorkbenchPart part = getActivePart(); return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage; } private IWorkbenchPart getActivePart() { IWorkbenchWindow window = getSite().getWorkbenchWindow(); IPartService service = window.getPartService(); IWorkbenchPart part = service.getActivePart(); return part; } /* * @see StatusTextEditor#getStatusHeader(IStatus) */ protected String getStatusHeader(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusHeader(status); if (message != null) return message; } return super.getStatusHeader(status); } /* * @see StatusTextEditor#getStatusBanner(IStatus) */ protected String getStatusBanner(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusBanner(status); if (message != null) return message; } return super.getStatusBanner(status); } /* * @see StatusTextEditor#getStatusMessage(IStatus) */ protected String getStatusMessage(IStatus status) { if (fEncodingSupport != null) { String message = fEncodingSupport.getStatusMessage(status); if (message != null) return message; } return super.getStatusMessage(status); } /* * @see AbstractTextEditor#doSetInput */ protected void doSetInput(IEditorInput input) throws CoreException { super.doSetInput(input); if (fEncodingSupport != null) fEncodingSupport.reset(); setOutlinePageInput(fOutlinePage, input); } /* * @see IWorkbenchPart#dispose() */ public void dispose() { // cancel possible running computation fMarkOccurrenceAnnotations = false; fComputeCount++; if (isBrowserLikeLinks()) disableBrowserLikeLinks(); if (fEncodingSupport != null) { fEncodingSupport.dispose(); fEncodingSupport = null; } if (fPropertyChangeListener != null) { Preferences preferences = JavaCore.getPlugin().getPluginPreferences(); preferences.removePropertyChangeListener(fPropertyChangeListener); fPropertyChangeListener = null; } if (fBracketMatcher != null) { fBracketMatcher.dispose(); fBracketMatcher = null; } if (fSelectionHistory != null) { fSelectionHistory.dispose(); fSelectionHistory = null; } if (fEditorSelectionChangedListener != null) { fEditorSelectionChangedListener.uninstall(getSelectionProvider()); fEditorSelectionChangedListener = null; } super.dispose(); } protected void createActions() { super.createActions(); ResourceAction resAction = new AddTaskAction(JavaEditorMessages.getResourceBundle(), "AddTask.", this); //$NON-NLS-1$ resAction.setHelpContextId(IAbstractTextEditorHelpContextIds.ADD_TASK_ACTION); resAction.setActionDefinitionId(ITextEditorActionDefinitionIds.ADD_TASK); setAction(ITextEditorActionConstants.ADD_TASK, resAction); ActionGroup oeg, ovg, jsg, sg; fActionGroups = new CompositeActionGroup( new ActionGroup[] { oeg = new OpenEditorActionGroup(this), sg = new ShowActionGroup(this), ovg = new OpenViewActionGroup(this), jsg = new JavaSearchActionGroup(this) }); fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] { oeg, ovg, sg, jsg }); resAction = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "ShowJavaDoc.", this, //$NON-NLS-1$ ISourceViewer.INFORMATION, true); resAction = new InformationDispatchAction(JavaEditorMessages.getResourceBundle(), "ShowJavaDoc.", //$NON-NLS-1$ (TextOperationAction) resAction); resAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_JAVADOC); setAction("ShowJavaDoc", resAction); //$NON-NLS-1$ WorkbenchHelp.setHelp(resAction, IJavaHelpContextIds.SHOW_JAVADOC_ACTION); Action action = new GotoMatchingBracketAction(this); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "ShowOutline.", this, //$NON-NLS-1$ JavaSourceViewer.SHOW_OUTLINE, true); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SHOW_OUTLINE); setAction(IJavaEditorActionDefinitionIds.SHOW_OUTLINE, action); WorkbenchHelp.setHelp(action, IJavaHelpContextIds.SHOW_OUTLINE_ACTION); action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "OpenStructure.", this, //$NON-NLS-1$ JavaSourceViewer.OPEN_STRUCTURE, true); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_STRUCTURE); setAction(IJavaEditorActionDefinitionIds.OPEN_STRUCTURE, action); WorkbenchHelp.setHelp(action, IJavaHelpContextIds.OPEN_STRUCTURE_ACTION); action = new TextOperationAction(JavaEditorMessages.getResourceBundle(), "OpenHierarchy.", this, //$NON-NLS-1$ JavaSourceViewer.SHOW_HIERARCHY, true); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY); setAction(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY, action); WorkbenchHelp.setHelp(action, IJavaHelpContextIds.OPEN_HIERARCHY_ACTION); fEncodingSupport = new DefaultEncodingSupport(); fEncodingSupport.initialize(this); fSelectionHistory = new SelectionHistory(this); action = new StructureSelectEnclosingAction(this, fSelectionHistory); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_ENCLOSING); setAction(StructureSelectionAction.ENCLOSING, action); action = new StructureSelectNextAction(this, fSelectionHistory); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_NEXT); setAction(StructureSelectionAction.NEXT, action); action = new StructureSelectPreviousAction(this, fSelectionHistory); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_PREVIOUS); setAction(StructureSelectionAction.PREVIOUS, action); StructureSelectHistoryAction historyAction = new StructureSelectHistoryAction(this, fSelectionHistory); historyAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_LAST); setAction(StructureSelectionAction.HISTORY, historyAction); fSelectionHistory.setHistoryAction(historyAction); action = GoToNextPreviousMemberAction.newGoToNextMemberAction(this); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_NEXT_MEMBER); setAction(GoToNextPreviousMemberAction.NEXT_MEMBER, action); action = GoToNextPreviousMemberAction.newGoToPreviousMemberAction(this); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.GOTO_PREVIOUS_MEMBER); setAction(GoToNextPreviousMemberAction.PREVIOUS_MEMBER, action); action = new QuickFormatAction(); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.QUICK_FORMAT); setAction(IJavaEditorActionDefinitionIds.QUICK_FORMAT, action); action = new RemoveOccurrenceAnnotations(this); action.setActionDefinitionId(IJavaEditorActionDefinitionIds.REMOVE_OCCURRENCE_ANNOTATIONS); setAction("RemoveOccurrenceAnnotations", action); //$NON-NLS-1$ } public void updatedTitleImage(Image image) { setTitleImage(image); } /* * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent) */ protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { try { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; String property = event.getProperty(); if (PreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { Object value = event.getNewValue(); if (value instanceof Integer) { sourceViewer.getTextWidget().setTabs(((Integer) value).intValue()); } else if (value instanceof String) { sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value)); } return; } if (isJavaEditorHoverProperty(property)) updateHoverBehavior(); if (BROWSER_LIKE_LINKS.equals(property)) { if (isBrowserLikeLinks()) enableBrowserLikeLinks(); else disableBrowserLikeLinks(); return; } if (PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE.equals(property)) { if ((event.getNewValue() instanceof Boolean) && ((Boolean) event.getNewValue()).booleanValue()) { fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); fEditorSelectionChangedListener.selectionChanged(); } else { fEditorSelectionChangedListener.uninstall(getSelectionProvider()); fEditorSelectionChangedListener = null; } return; } if (PreferenceConstants.EDITOR_DISABLE_OVERWRITE_MODE.equals(property)) { if (event.getNewValue() instanceof Boolean) { Boolean disable = (Boolean) event.getNewValue(); configureInsertMode(OVERWRITE, !disable.booleanValue()); } } if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { if (event.getNewValue() instanceof Boolean) { fMarkOccurrenceAnnotations = ((Boolean) event.getNewValue()).booleanValue(); if (!fMarkOccurrenceAnnotations) { fComputeCount++; removeOccurrenceAnnotations(); } } } } finally { super.handlePreferenceStoreChanged(event); } } /** * Initializes the given viewer's colors. * * @param viewer the viewer to be initialized * @since 3.0 */ protected void initializeViewerColors(ISourceViewer viewer) { // is handled by JavaSourceViewer } private boolean isJavaEditorHoverProperty(String property) { return PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property); } /** * Return whether the browser like links should be enabled * according to the preference store settings. * @return <code>true</code> if the browser like links should be enabled */ private boolean isBrowserLikeLinks() { IPreferenceStore store = getPreferenceStore(); return store.getBoolean(BROWSER_LIKE_LINKS); } /** * Enables browser like links. */ private void enableBrowserLikeLinks() { if (fMouseListener == null) { fMouseListener = new MouseClickListener(); fMouseListener.install(); } } /** * Disables browser like links. */ private void disableBrowserLikeLinks() { if (fMouseListener != null) { fMouseListener.uninstall(); fMouseListener = null; } } /** * Handles a property change event describing a change * of the java core's preferences and updates the preference * related editor properties. * * @param event the property change event */ protected void handlePreferencePropertyChanged(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) { if (COMPILER_TASK_TAGS.equals(event.getProperty())) { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null && affectsTextPresentation(new PropertyChangeEvent(event.getSource(), event.getProperty(), event.getOldValue(), event.getNewValue()))) sourceViewer.invalidateTextPresentation(); } } /** * Returns a segmentation of the line of the given viewer's input document appropriate for * bidi rendering. The default implementation returns only the string literals of a java code * line as segments. * * @param viewer the text viewer * @param lineOffset the offset of the line * @return the line's bidi segmentation * @throws BadLocationException in case lineOffset is not valid in document */ public static int[] getBidiLineSegments(ITextViewer viewer, int lineOffset) throws BadLocationException { IDocument document = viewer.getDocument(); if (document == null) return null; IRegion line = document.getLineInformationOfOffset(lineOffset); ITypedRegion[] linePartitioning = TextUtilities.computePartitioning(document, IJavaPartitions.JAVA_PARTITIONING, lineOffset, line.getLength()); List segmentation = new ArrayList(); for (int i = 0; i < linePartitioning.length; i++) { if (IJavaPartitions.JAVA_STRING.equals(linePartitioning[i].getType())) segmentation.add(linePartitioning[i]); } if (segmentation.size() == 0) return null; int size = segmentation.size(); int[] segments = new int[size * 2 + 1]; int j = 0; for (int i = 0; i < size; i++) { ITypedRegion segment = (ITypedRegion) segmentation.get(i); if (i == 0) segments[j++] = 0; int offset = segment.getOffset() - lineOffset; if (offset > segments[j - 1]) segments[j++] = offset; if (offset + segment.getLength() >= line.getLength()) break; segments[j++] = offset + segment.getLength(); } if (j < segments.length) { int[] result = new int[j]; System.arraycopy(segments, 0, result, 0, j); segments = result; } return segments; } /** * Returns a segmentation of the given line appropriate for bidi rendering. The default * implementation returns only the string literals of a java code line as segments. * * @param lineOffset the offset of the line * @param line the content of the line * @return the line's bidi segmentation */ protected int[] getBidiLineSegments(int widgetLineOffset, String line) { if (line != null && line.length() > 0) { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { int lineOffset; if (sourceViewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer; lineOffset = extension.widgetOffset2ModelOffset(widgetLineOffset); } else { IRegion visible = sourceViewer.getVisibleRegion(); lineOffset = visible.getOffset() + widgetLineOffset; } try { return getBidiLineSegments(sourceViewer, lineOffset); } catch (BadLocationException x) { // don't segment line in this case } } } return null; } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#updatePropertyDependentActions() */ protected void updatePropertyDependentActions() { super.updatePropertyDependentActions(); if (fEncodingSupport != null) fEncodingSupport.reset(); } /* * Update the hovering behavior depending on the preferences. */ private void updateHoverBehavior() { SourceViewerConfiguration configuration = getSourceViewerConfiguration(); String[] types = configuration.getConfiguredContentTypes(getSourceViewer()); for (int i = 0; i < types.length; i++) { String t = types[i]; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension2) { // Remove existing hovers ((ITextViewerExtension2) sourceViewer).removeTextHovers(t); int[] stateMasks = configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t); if (stateMasks != null) { for (int j = 0; j < stateMasks.length; j++) { int stateMask = stateMasks[j]; ITextHover textHover = configuration.getTextHover(sourceViewer, t, stateMask); ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, stateMask); } } else { ITextHover textHover = configuration.getTextHover(sourceViewer, t); ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); } } else sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t); } } /* * @see org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput() */ public Object getViewPartInput() { return getEditorInput().getAdapter(IJavaElement.class); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetSelection(ISelection) */ protected void doSetSelection(ISelection selection) { super.doSetSelection(selection); synchronizeOutlinePageSelection(); } /* * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { super.createPartControl(parent); Preferences preferences = JavaCore.getPlugin().getPluginPreferences(); preferences.addPropertyChangeListener(fPropertyChangeListener); IInformationControlCreator informationControlCreator = new IInformationControlCreator() { public IInformationControl createInformationControl(Shell shell) { boolean cutDown = false; int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL); return new DefaultInformationControl(shell, SWT.RESIZE, style, new HTMLTextPresenter(cutDown)); } }; fInformationPresenter = new InformationPresenter(informationControlCreator); fInformationPresenter.setSizeConstraints(60, 10, true, true); fInformationPresenter.install(getSourceViewer()); if (PreferenceConstants.getPreferenceStore() .getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE)) { fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); } if (isBrowserLikeLinks()) enableBrowserLikeLinks(); if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_DISABLE_OVERWRITE_MODE)) configureInsertMode(OVERWRITE, false); } protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { support.setCharacterPairMatcher(fBracketMatcher); support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); super.configureSourceViewerDecorationSupport(support); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#gotoMarker(org.eclipse.core.resources.IMarker) */ public void gotoMarker(IMarker marker) { fLastMarkerTarget = marker; if (!fIsUpdatingAnnotationViews) super.gotoMarker(marker); } /** * Jumps to the next enabled annotation according to the given direction. * An annotation type is enabled if it is configured to be in the * Next/Previous tool bar drop down menu and if it is checked. * * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward */ public void gotoAnnotation(boolean forward) { ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection(); Position position = new Position(0, 0); if (false /* delayed - see bug 18316 */) { getNextAnnotation(selection.getOffset(), selection.getLength(), forward, position); selectAndReveal(position.getOffset(), position.getLength()); } else /* no delay - see bug 18316 */ { Annotation annotation = getNextAnnotation(selection.getOffset(), selection.getLength(), forward, position); setStatusLineErrorMessage(null); setStatusLineMessage(null); if (annotation != null) { updateAnnotationViews(annotation); selectAndReveal(position.getOffset(), position.getLength()); if (annotation instanceof IJavaAnnotation && ((IJavaAnnotation) annotation).isProblem()) setStatusLineMessage(((IJavaAnnotation) annotation).getMessage()); } } } /** * Updates the annotation views that show the given annotation. * * @param annotation the annotation */ private void updateAnnotationViews(Annotation annotation) { IMarker marker = null; if (annotation instanceof MarkerAnnotation) marker = ((MarkerAnnotation) annotation).getMarker(); else if (annotation instanceof IJavaAnnotation) { Iterator e = ((IJavaAnnotation) annotation).getOverlaidIterator(); if (e != null) { while (e.hasNext()) { Object o = e.next(); if (o instanceof MarkerAnnotation) { marker = ((MarkerAnnotation) o).getMarker(); break; } } } } if (marker != null && !marker.equals(fLastMarkerTarget)) { try { boolean isProblem = marker.isSubtypeOf(IMarker.PROBLEM); IWorkbenchPage page = getSite().getPage(); IViewPart view = page.findView(isProblem ? IPageLayout.ID_PROBLEM_VIEW : IPageLayout.ID_TASK_LIST); //$NON-NLS-1$ //$NON-NLS-2$ if (view != null) { Method method = view.getClass().getMethod("setSelection", //$NON-NLS-1$ new Class[] { IStructuredSelection.class, boolean.class }); method.invoke(view, new Object[] { new StructuredSelection(marker), Boolean.TRUE }); } } catch (CoreException x) { } catch (NoSuchMethodException x) { } catch (IllegalAccessException x) { } catch (InvocationTargetException x) { } // ignore exceptions, don't update any of the lists, just set statusline } } /** * Finds and marks occurrence annotations. * * @since 3.0 */ class OccurrencesFinder implements Runnable, IDocumentListener { private int fCount; private IDocument fDocument; private ITextSelection fSelection; private boolean fCancelled = false; public OccurrencesFinder(int count, IDocument document, ITextSelection selection) { fCount = count; fDocument = document; fSelection = selection; fDocument.addDocumentListener(this); } private boolean isCancelled() { return fCount != fComputeCount || fCancelled; } /* * @see java.lang.Runnable#run() */ public void run() { try { if (isCancelled()) return; // Find occurrences FindOccurrencesEngine engine = FindOccurrencesEngine.create(getInputJavaElement()); List matches = new ArrayList(); try { matches = engine.findOccurrences(fSelection.getOffset(), fSelection.getLength()); } catch (JavaModelException e) { JavaPlugin.log(e); return; } if (matches == null || matches.isEmpty()) return; if (isCancelled()) return; removeOccurrenceAnnotations(); if (isCancelled()) return; ITextViewer textViewer = getViewer(); if (textViewer == null) return; IDocument document = textViewer.getDocument(); if (document == null) return; IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) return; IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null) return; // Add occurrence annotations ArrayList annotations = new ArrayList(); ArrayList positions = new ArrayList(); for (Iterator each = matches.iterator(); each.hasNext();) { if (isCancelled()) return; ASTNode node = (ASTNode) each.next(); if (node == null) continue; String message; // Create & add annotation try { message = document.get(node.getStartPosition(), node.getLength()); } catch (BadLocationException ex) { // Skip this match continue; } annotations.add( new DefaultAnnotation(SearchUI.SEARCH_MARKER, IMarker.SEVERITY_INFO, true, message)); positions.add(new Position(node.getStartPosition(), node.getLength())); } if (isCancelled()) return; synchronized (annotationModel) { fOccurrenceAnnotations = annotations; for (int i = 0, size = annotations.size(); i < size; i++) annotationModel.addAnnotation((Annotation) annotations.get(i), (Position) positions.get(i)); } } finally { fDocument.removeDocumentListener(this); } } /* * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { fCancelled = true; } /* * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { } } /** * Updates the occurrences annotations based * on the current selection. * * @since 3.0 */ protected void updateOccurrences() { if (!fMarkOccurrenceAnnotations) return; IDocument document = getSourceViewer().getDocument(); if (document == null) return; OccurrencesFinder finder = new OccurrencesFinder(++fComputeCount, document, (ITextSelection) getSelectionProvider().getSelection()); Thread thread = new Thread(finder, "Occurrences Marker"); //$NON-NLS-1$ thread.setDaemon(true); thread.start(); } void removeOccurrenceAnnotations() { IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) return; IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null) return; synchronized (annotationModel) { for (int i = 0, size = fOccurrenceAnnotations.size(); i < size; i++) annotationModel.removeAnnotation((Annotation) fOccurrenceAnnotations.get(i)); fOccurrenceAnnotations.clear(); } } /** * Returns the Java element wrapped by this editors input. * * @return the Java element wrapped by this editors input. * @since 3.0 */ abstract protected IJavaElement getInputJavaElement(); protected void updateStatusLine() { ITextSelection selection = (ITextSelection) getSelectionProvider().getSelection(); Annotation annotation = getAnnotation(selection.getOffset(), selection.getLength()); setStatusLineErrorMessage(null); setStatusLineMessage(null); if (annotation != null) { try { fIsUpdatingAnnotationViews = true; updateAnnotationViews(annotation); } finally { fIsUpdatingAnnotationViews = false; } if (annotation instanceof IJavaAnnotation && ((IJavaAnnotation) annotation).isProblem()) setStatusLineMessage(((IJavaAnnotation) annotation).getMessage()); } } /** * Jumps to the matching bracket. */ public void gotoMatchingBracket() { ISourceViewer sourceViewer = getSourceViewer(); IDocument document = sourceViewer.getDocument(); if (document == null) return; IRegion selection = getSignedSelection(sourceViewer); int selectionLength = Math.abs(selection.getLength()); if (selectionLength > 1) { setStatusLineErrorMessage(JavaEditorMessages.getString("GotoMatchingBracket.error.invalidSelection")); //$NON-NLS-1$ sourceViewer.getTextWidget().getDisplay().beep(); return; } // #26314 int sourceCaretOffset = selection.getOffset() + selection.getLength(); if (isSurroundedByBrackets(document, sourceCaretOffset)) sourceCaretOffset -= selection.getLength(); IRegion region = fBracketMatcher.match(document, sourceCaretOffset); if (region == null) { setStatusLineErrorMessage(JavaEditorMessages.getString("GotoMatchingBracket.error.noMatchingBracket")); //$NON-NLS-1$ sourceViewer.getTextWidget().getDisplay().beep(); return; } int offset = region.getOffset(); int length = region.getLength(); if (length < 1) return; int anchor = fBracketMatcher.getAnchor(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 int targetOffset = (JavaPairMatcher.RIGHT == anchor) ? offset + 1 : offset + length; boolean visible = false; if (sourceViewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer; visible = (extension.modelOffset2WidgetOffset(targetOffset) > -1); } else { IRegion visibleRegion = sourceViewer.getVisibleRegion(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 visible = (targetOffset >= visibleRegion.getOffset() && targetOffset <= visibleRegion.getOffset() + visibleRegion.getLength()); } if (!visible) { setStatusLineErrorMessage( JavaEditorMessages.getString("GotoMatchingBracket.error.bracketOutsideSelectedElement")); //$NON-NLS-1$ sourceViewer.getTextWidget().getDisplay().beep(); return; } if (selection.getLength() < 0) targetOffset -= selection.getLength(); sourceViewer.setSelectedRange(targetOffset, selection.getLength()); sourceViewer.revealRange(targetOffset, selection.getLength()); } /** * Sets the given message as error message to this editor's status line. * * @param msg message to be set */ protected void setStatusLineErrorMessage(String msg) { IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class); if (statusLine != null) statusLine.setMessage(true, msg, null); } /** * Sets the given message as message to this editor's status line. * * @param msg message to be set * @since 3.0 */ protected void setStatusLineMessage(String msg) { IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class); if (statusLine != null) statusLine.setMessage(false, msg, null); } private static IRegion getSignedSelection(ITextViewer viewer) { StyledText text = viewer.getTextWidget(); int caretOffset = text.getCaretOffset(); Point selection = text.getSelection(); // caret left int offset, length; if (caretOffset == selection.x) { offset = selection.y; length = selection.x - selection.y; // caret right } else { offset = selection.x; length = selection.y - selection.x; } return new Region(offset, length); } private static boolean isBracket(char character) { for (int i = 0; i != BRACKETS.length; ++i) if (character == BRACKETS[i]) return true; return false; } private static boolean isSurroundedByBrackets(IDocument document, int offset) { if (offset == 0 || offset == document.getLength()) return false; try { return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset)); } catch (BadLocationException e) { return false; } } /** * Returns the annotation closest to the given range respecting the given * direction. If an annotation is found, the annotations current position * is copied into the provided annotation position. * * @param offset the region offset * @param length the region length * @param forward <code>true</code> for forwards, <code>false</code> for backward * @param annotationPosition the position of the found annotation * @return the found annotation */ private Annotation getNextAnnotation(int offset, int length, boolean forward, Position annotationPosition) { Annotation nextAnnotation = null; Position nextAnnotationPosition = null; Annotation containingAnnotation = null; Position containingAnnotationPosition = null; boolean currentAnnotation = false; IDocument document = getDocumentProvider().getDocument(getEditorInput()); int endOfDocument = document.getLength(); int distance = 0; IAnnotationAccess access = getAnnotationAccess(); IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); Iterator e = new JavaAnnotationIterator(model, true, true); while (e.hasNext()) { Annotation a = (Annotation) e.next(); Object type; if (a instanceof IJavaAnnotation) type = ((IJavaAnnotation) a).getAnnotationType(); else type = access.getType(a); boolean isNavigationTarget = isNavigationTargetType(type); if ((a instanceof IJavaAnnotation) && ((IJavaAnnotation) a).hasOverlay() || !isNavigationTarget) continue; Position p = model.getPosition(a); if (p == null) continue; if (!(p.includes(offset) || (p.getLength() == 0 && offset == p.offset))) { int currentDistance = 0; if (forward) { currentDistance = p.getOffset() - offset; if (currentDistance < 0) currentDistance = endOfDocument - offset + p.getOffset(); } else { currentDistance = offset - p.getOffset(); if (currentDistance < 0) currentDistance = offset + endOfDocument - p.getOffset(); } if (nextAnnotation == null || currentDistance < distance) { distance = currentDistance; nextAnnotation = a; nextAnnotationPosition = p; } } else { if (containingAnnotationPosition == null || containingAnnotationPosition.length > p.length) { containingAnnotation = a; containingAnnotationPosition = p; if (length == p.length) currentAnnotation = true; } } } if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) { annotationPosition.setOffset(containingAnnotationPosition.getOffset()); annotationPosition.setLength(containingAnnotationPosition.getLength()); return containingAnnotation; } if (nextAnnotationPosition != null) { annotationPosition.setOffset(nextAnnotationPosition.getOffset()); annotationPosition.setLength(nextAnnotationPosition.getLength()); } return nextAnnotation; } /** * Returns the annotation overlapping with the given range or <code>null</code>. * * @param offset the region offset * @param length the region length * @return the found annotation or <code>null</code> * @since 3.0 */ private Annotation getAnnotation(int offset, int length) { IAnnotationAccess access = getAnnotationAccess(); IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); Iterator e = new JavaAnnotationIterator(model, true, true); while (e.hasNext()) { Annotation a = (Annotation) e.next(); if (a instanceof IJavaAnnotation) { IJavaAnnotation annotation = (IJavaAnnotation) a; if (annotation.hasOverlay() || !isNavigationTargetType(annotation.getAnnotationType())) continue; } else if (!isNavigationTargetType(access.getType(a))) continue; Position p = model.getPosition(a); if (p != null && p.overlapsWith(offset, length)) return a; } return null; } /** * Returns whether the given annotation type is configured as a target type * for the "Go to Next/Previous Annotation" actions * * @param type the annotation type * @return <code>true</code> if this is a target type, <code>false</code> * otherwise * @since 3.0 */ private boolean isNavigationTargetType(Object type) { Preferences preferences = Platform.getPlugin("org.eclipse.ui.workbench.texteditor").getPluginPreferences(); //$NON-NLS-1$ Iterator i = getAnnotationPreferences().getAnnotationPreferences().iterator(); while (i.hasNext()) { AnnotationPreference annotationPref = (AnnotationPreference) i.next(); if (annotationPref.getAnnotationType().equals(type)) { // See bug 41689 // String key= forward ? annotationPref.getIsGoToNextNavigationTargetKey() : annotationPref.getIsGoToPreviousNavigationTargetKey(); String key = annotationPref.getIsGoToNextNavigationTargetKey(); if (key != null && preferences.getBoolean(key)) return true; } } return false; } /** * Computes and returns the source reference that includes the caret and * serves as provider for the outline page selection and the editor range * indication. * * @return the computed source reference * @since 3.0 */ protected ISourceReference computeHighlightRangeSourceReference() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return null; StyledText styledText = sourceViewer.getTextWidget(); if (styledText == null) return null; int caret = 0; if (sourceViewer instanceof ITextViewerExtension3) { ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer; caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); } else { int offset = sourceViewer.getVisibleRegion().getOffset(); caret = offset + styledText.getCaretOffset(); } IJavaElement element = getElementAt(caret, false); if (!(element instanceof ISourceReference)) return null; if (element.getElementType() == IJavaElement.IMPORT_DECLARATION) { IImportDeclaration declaration = (IImportDeclaration) element; IImportContainer container = (IImportContainer) declaration.getParent(); ISourceRange srcRange = null; try { srcRange = container.getSourceRange(); } catch (JavaModelException e) { } if (srcRange != null && srcRange.getOffset() == caret) return container; } return (ISourceReference) element; } /** * Returns the most narrow java element including the given offset. * * @param offset the offset inside of the requested element * @param reconcile <code>true</code> if editor input should be reconciled in advance * @return the most narrow java element * @since 3.0 */ protected IJavaElement getElementAt(int offset, boolean reconcile) { return getElementAt(offset); } /* * @see org.eclipse.ui.texteditor.ExtendedTextEditor#createChangeHover() */ protected LineChangeHover createChangeHover() { return new JavaChangeHover(IJavaPartitions.JAVA_PARTITIONING); } protected boolean isPrefQuickDiffAlwaysOn() { return false; // never show change ruler for the non-editable java editor. Overridden in subclasses like CompilationUnitEditor } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#createNavigationActions() */ protected void createNavigationActions() { super.createNavigationActions(); final StyledText textWidget = getSourceViewer().getTextWidget(); IAction action = new SmartLineStartAction(textWidget, false); action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START); setAction(ITextEditorActionDefinitionIds.LINE_START, action); action = new SmartLineStartAction(textWidget, true); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START); setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action); action = new NavigatePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL); action = new NavigateNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT); setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL); action = new DeletePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD); setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action); textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL); action = new DeleteNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD); setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action); textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL); action = new SelectPreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL); action = new SelectNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL); } }