| /******************************************************************************* |
| * Copyright (c) 2000, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.javaeditor; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.osgi.framework.Bundle; |
| |
| import org.eclipse.swt.SWT; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.content.IContentDescription; |
| import org.eclipse.core.runtime.content.IContentType; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IStorage; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextSelection; |
| |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.ide.IGotoMarker; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.part.MultiEditorInput; |
| |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| import org.eclipse.ui.texteditor.TextEditorAction; |
| |
| import org.eclipse.ui.editors.text.TextFileDocumentProvider; |
| |
| import org.eclipse.compare.rangedifferencer.IRangeComparator; |
| import org.eclipse.compare.rangedifferencer.RangeDifference; |
| import org.eclipse.compare.rangedifferencer.RangeDifferencer; |
| |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.ISourceReference; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| |
| import org.eclipse.jdt.internal.ui.IJavaStatusConstants; |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.compare.JavaCompareUtilities; |
| import org.eclipse.jdt.internal.ui.text.LineComparator; |
| |
| |
| /** |
| * A number of routines for working with JavaElements in editors. |
| * |
| * Use 'isOpenInEditor' to test if an element is already open in a editor |
| * Use 'openInEditor' to force opening an element in a editor |
| * With 'getWorkingCopy' you get the working copy (element in the editor) of an element |
| */ |
| public class EditorUtility { |
| |
| /** |
| * Tests if a CU is currently shown in an editor |
| * |
| * @param inputElement the input element |
| * @return the IEditorPart if shown, null if element is not open in an editor |
| */ |
| public static IEditorPart isOpenInEditor(Object inputElement) { |
| IEditorPart editor= findEditor(inputElement, false); |
| if (editor != null) |
| return editor; |
| |
| IEditorInput input= getEditorInput(inputElement); |
| |
| if (input != null) { |
| IWorkbenchPage p= JavaPlugin.getActivePage(); |
| if (p != null) { |
| return p.findEditor(input); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Opens a Java editor for an element such as <code>IJavaElement</code>, <code>IFile</code>, or <code>IStorage</code>. |
| * The editor is activated by default. |
| * |
| * @param inputElement the input element |
| * @return an open editor or <code>null</code> if an external editor was opened |
| * @throws PartInitException if the editor could not be opened or the input element is not valid. |
| * Status code {@link IJavaStatusConstants#EDITOR_NO_EDITOR_INPUT} if opening the editor failed as |
| * no editor input could be created for the given element. |
| */ |
| public static IEditorPart openInEditor(Object inputElement) throws PartInitException { |
| return openInEditor(inputElement, true); |
| } |
| |
| /** |
| * Opens the editor currently associated with the given element (IJavaElement, IFile, IStorage...) |
| * |
| * @param inputElement the input element |
| * @param activate <code>true</code> if the editor should be activated |
| * @return an open editor or <code>null</code> if an external editor was opened |
| * @throws PartInitException if the editor could not be opened or the input element is not valid |
| * Status code {@link IJavaStatusConstants#EDITOR_NO_EDITOR_INPUT} if opening the editor failed as |
| * no editor input could be created for the given element. |
| */ |
| public static IEditorPart openInEditor(Object inputElement, boolean activate) throws PartInitException { |
| |
| if (inputElement instanceof IFile) { |
| IFile file= (IFile) inputElement; |
| if (!isClassFile(file)) |
| return openInEditor(file, activate); |
| inputElement= JavaCore.createClassFileFrom(file); |
| } |
| |
| IEditorPart editor= findEditor(inputElement, activate); |
| if (editor != null) |
| return editor; |
| |
| IEditorInput input= getEditorInput(inputElement); |
| if (input == null) |
| throwPartInitException(JavaEditorMessages.EditorUtility_no_editorInput, IJavaStatusConstants.EDITOR_NO_EDITOR_INPUT); |
| |
| return openInEditor(input, getEditorID(input), activate); |
| } |
| |
| /** |
| * Tries to find the editor for the given input element. |
| * |
| * @param inputElement the input element |
| * @param activate <code>true</code> if the found editor should be activated |
| * @return the editor or <code>null</code> |
| * @since 3.5 |
| */ |
| private static IEditorPart findEditor(Object inputElement, boolean activate) { |
| /* |
| * Support to navigate inside non-primary working copy. |
| * For now we only support to navigate inside the currently |
| * active editor. |
| * |
| * XXX: once we have FileStoreEditorInput as API, |
| * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=111887 |
| * we can fix this code by creating the correct editor input |
| * in getEditorInput(Object) |
| */ |
| if (inputElement instanceof IJavaElement) { |
| ICompilationUnit cu= (ICompilationUnit)((IJavaElement)inputElement).getAncestor(IJavaElement.COMPILATION_UNIT); |
| if (cu != null) { |
| IWorkbenchPage page= JavaPlugin.getActivePage(); |
| IEditorPart editor= page != null ? editor= page.getActiveEditor() : null; |
| if (editor != null) { |
| boolean isCompareEditorInput= isCompareEditorInput(editor.getEditorInput()); |
| if (isCompareEditorInput || !JavaModelUtil.isPrimary(cu)) { |
| IEditorInput editorInput; |
| if (isCompareEditorInput) |
| editorInput= editor.getAdapter(IEditorInput.class); |
| else |
| editorInput= editor.getEditorInput(); |
| IJavaElement editorCU= getEditorInputJavaElement(editorInput, false); |
| if (cu.equals(editorCU)) { |
| if (activate && page.getActivePart() != editor) |
| page.activate(editor); |
| return editor; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * Avoid unnecessary loading of Compare plug-in |
| */ |
| private static boolean isComparePlugInActivated() { |
| return Platform.getBundle("org.eclipse.compare").getState() == Bundle.ACTIVE; //$NON-NLS-1$ |
| } |
| |
| /* |
| * Avoid unnecessary loading of Compare plug-in |
| */ |
| public static boolean isCompareEditorInput(IEditorInput input) { |
| return isComparePlugInActivated() && JavaCompareUtilities.isCompareEditorInput(input); |
| } |
| |
| /** |
| * Selects a Java Element in an editor. |
| * |
| * @param part the editor part |
| * @param element the Java element to reveal |
| */ |
| public static void revealInEditor(IEditorPart part, IJavaElement element) { |
| if (element == null) |
| return; |
| |
| if (part instanceof JavaEditor) { |
| ((JavaEditor) part).setSelection(element); |
| return; |
| } |
| |
| // Support for non-Java editor |
| try { |
| ISourceRange range= null; |
| if (element instanceof ICompilationUnit) |
| return; |
| else if (element instanceof IClassFile) |
| return; |
| |
| if (element instanceof ISourceReference) |
| range= ((ISourceReference)element).getNameRange(); |
| |
| if (range != null) |
| revealInEditor(part, range.getOffset(), range.getLength()); |
| } catch (JavaModelException e) { |
| // don't reveal |
| } |
| } |
| |
| /** |
| * Selects and reveals the given region in the given editor part. |
| * |
| * @param part the editor part |
| * @param region the region |
| */ |
| public static void revealInEditor(IEditorPart part, IRegion region) { |
| if (part != null && region != null) |
| revealInEditor(part, region.getOffset(), region.getLength()); |
| } |
| |
| /** |
| * Selects and reveals the given offset and length in the given editor part. |
| * @param editor the editor part |
| * @param offset the offset |
| * @param length the length |
| */ |
| public static void revealInEditor(IEditorPart editor, final int offset, final int length) { |
| if (editor instanceof ITextEditor) { |
| ((ITextEditor)editor).selectAndReveal(offset, length); |
| return; |
| } |
| |
| // Support for non-text editor - try IGotoMarker interface |
| final IGotoMarker gotoMarkerTarget; |
| if (editor instanceof IGotoMarker) |
| gotoMarkerTarget= (IGotoMarker)editor; |
| else |
| gotoMarkerTarget= editor != null ? (IGotoMarker)editor.getAdapter(IGotoMarker.class) : null; |
| if (gotoMarkerTarget != null) { |
| final IEditorInput input= editor.getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| WorkspaceModifyOperation op = new WorkspaceModifyOperation() { |
| @Override |
| protected void execute(IProgressMonitor monitor) throws CoreException { |
| IMarker marker= null; |
| try { |
| marker= ((IFileEditorInput)input).getFile().createMarker(IMarker.TEXT); |
| marker.setAttribute(IMarker.CHAR_START, offset); |
| marker.setAttribute(IMarker.CHAR_END, offset + length); |
| |
| gotoMarkerTarget.gotoMarker(marker); |
| |
| } finally { |
| if (marker != null) |
| marker.delete(); |
| } |
| } |
| }; |
| |
| try { |
| op.run(null); |
| } catch (InvocationTargetException ex) { |
| // reveal failed |
| } catch (InterruptedException e) { |
| Assert.isTrue(false, "this operation can not be canceled"); //$NON-NLS-1$ |
| } |
| } |
| return; |
| } |
| |
| /* |
| * Workaround: send out a text selection |
| * XXX: Needs to be improved, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=32214 |
| */ |
| if (editor != null && editor.getEditorSite().getSelectionProvider() != null) { |
| IEditorSite site= editor.getEditorSite(); |
| if (site == null) |
| return; |
| |
| ISelectionProvider provider= editor.getEditorSite().getSelectionProvider(); |
| if (provider == null) |
| return; |
| |
| provider.setSelection(new TextSelection(offset, length)); |
| } |
| } |
| |
| private static IEditorPart openInEditor(IFile file, boolean activate) throws PartInitException { |
| if (file == null) |
| throwPartInitException(JavaEditorMessages.EditorUtility_file_must_not_be_null); |
| |
| IWorkbenchPage p= JavaPlugin.getActivePage(); |
| if (p == null) |
| throwPartInitException(JavaEditorMessages.EditorUtility_no_active_WorkbenchPage); |
| |
| IEditorPart editorPart= IDE.openEditor(p, file, activate); |
| initializeHighlightRange(editorPart); |
| return editorPart; |
| } |
| |
| private static IEditorPart openInEditor(IEditorInput input, String editorID, boolean activate) throws PartInitException { |
| Assert.isNotNull(input); |
| Assert.isNotNull(editorID); |
| |
| IWorkbenchPage p= JavaPlugin.getActivePage(); |
| if (p == null) |
| throwPartInitException(JavaEditorMessages.EditorUtility_no_active_WorkbenchPage); |
| |
| IEditorPart editorPart= p.openEditor(input, editorID, activate); |
| initializeHighlightRange(editorPart); |
| return editorPart; |
| } |
| |
| private static void throwPartInitException(String message, int code) throws PartInitException { |
| IStatus status= new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, code, message, null); |
| throw new PartInitException(status); |
| } |
| |
| private static void throwPartInitException(String message) throws PartInitException { |
| throwPartInitException(message, IStatus.OK); |
| } |
| |
| private static void initializeHighlightRange(IEditorPart editorPart) { |
| if (editorPart instanceof ITextEditor) { |
| IAction toggleAction= editorPart.getEditorSite().getActionBars().getGlobalActionHandler(ITextEditorActionDefinitionIds.TOGGLE_SHOW_SELECTED_ELEMENT_ONLY); |
| boolean enable= toggleAction != null; |
| if (enable && editorPart instanceof JavaEditor) |
| enable= JavaPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS); |
| else |
| enable= enable && toggleAction.isEnabled() && toggleAction.isChecked(); |
| if (enable) { |
| if (toggleAction instanceof TextEditorAction) { |
| // Reset the action |
| ((TextEditorAction)toggleAction).setEditor(null); |
| // Restore the action |
| ((TextEditorAction)toggleAction).setEditor((ITextEditor)editorPart); |
| } else { |
| // Uncheck |
| toggleAction.run(); |
| // Check |
| toggleAction.run(); |
| } |
| } |
| } |
| } |
| |
| public static String getEditorID(IEditorInput input) throws PartInitException { |
| Assert.isNotNull(input); |
| if (input instanceof IFileEditorInput) |
| return IDE.getEditorDescriptor(((IFileEditorInput)input).getFile()).getId(); |
| |
| String name= input.getName(); |
| |
| if (input instanceof IClassFileEditorInput) { |
| boolean hasSource; |
| try { |
| hasSource= ((IClassFileEditorInput) input).getClassFile().getSourceRange() != null; |
| } catch (JavaModelException e) { |
| hasSource= false; |
| } |
| |
| if (!hasSource) { |
| if (JavaModelUtil.MODULE_INFO_CLASS.equals(name)) { |
| name= "module-info.class without source"; //$NON-NLS-1$ |
| } else { |
| name= "*.class without source"; //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| return IDE.getEditorDescriptor(name).getId(); |
| } |
| |
| /** |
| * Returns the given editor's input as Java element. |
| * |
| * @param editor the editor |
| * @param primaryOnly if <code>true</code> only primary working copies will be returned |
| * @return the given editor's input as <code>ITypeRoot</code> or <code>null</code> if none |
| * @since 3.2 |
| */ |
| public static ITypeRoot getEditorInputJavaElement(IEditorPart editor, boolean primaryOnly) { |
| Assert.isNotNull(editor); |
| return getEditorInputJavaElement(editor.getEditorInput(), primaryOnly); |
| } |
| |
| private static ITypeRoot getEditorInputJavaElement(IEditorInput editorInput, boolean primaryOnly) { |
| if (editorInput == null) |
| return null; |
| |
| ICompilationUnit cu= JavaPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editorInput, primaryOnly); |
| if (cu != null) |
| return cu; |
| |
| IJavaElement je= editorInput.getAdapter(IJavaElement.class); |
| if (je instanceof ITypeRoot) |
| return (ITypeRoot)je; |
| |
| return null; |
| } |
| |
| private static IEditorInput getEditorInput(IJavaElement element) { |
| while (element != null) { |
| if (element instanceof ICompilationUnit) { |
| ICompilationUnit unit= ((ICompilationUnit) element).getPrimary(); |
| IResource resource= unit.getResource(); |
| if (resource instanceof IFile) |
| return new FileEditorInput((IFile) resource); |
| } |
| |
| if (element instanceof IClassFile) |
| return new InternalClassFileEditorInput((IClassFile) element); |
| |
| element= element.getParent(); |
| } |
| |
| return null; |
| } |
| |
| public static IEditorInput getEditorInput(Object input) { |
| if (input instanceof IJavaElement) |
| return getEditorInput((IJavaElement) input); |
| |
| if (input instanceof IFile) |
| return new FileEditorInput((IFile) input); |
| |
| if (JavaModelUtil.isOpenableStorage(input)) |
| return new JarEntryEditorInput((IStorage)input); |
| |
| return null; |
| } |
| |
| /** |
| * Returns the Java element edited in the current active editor. |
| * |
| * @return the Java element or <code>null</code> if the active editor doesn't edit a Java element |
| */ |
| public static IJavaElement getActiveEditorJavaInput() { |
| IWorkbenchPage page= JavaPlugin.getActivePage(); |
| if (page != null) { |
| IEditorPart part= page.getActiveEditor(); |
| if (part != null) { |
| IEditorInput editorInput= part.getEditorInput(); |
| if (editorInput != null) { |
| return JavaUI.getEditorInputJavaElement(editorInput); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Maps the localized modifier name to a code in the same |
| * manner as #findModifier. |
| * |
| * @param modifierName the modifier name |
| * @return the SWT modifier bit, or <code>0</code> if no match was found |
| * @since 2.1.1 |
| */ |
| public static int findLocalizedModifier(String modifierName) { |
| if (modifierName == null) |
| return 0; |
| |
| if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.CTRL))) |
| return SWT.CTRL; |
| if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.SHIFT))) |
| return SWT.SHIFT; |
| if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.ALT))) |
| return SWT.ALT; |
| if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.COMMAND))) |
| return SWT.COMMAND; |
| |
| return 0; |
| } |
| |
| /** |
| * Returns the modifier string for the given SWT modifier |
| * modifier bits. |
| * |
| * @param stateMask the SWT modifier bits |
| * @return the modifier string |
| * @since 2.1.1 |
| */ |
| public static String getModifierString(int stateMask) { |
| String modifierString= ""; //$NON-NLS-1$ |
| if ((stateMask & SWT.CTRL) == SWT.CTRL) |
| modifierString= appendModifierString(modifierString, SWT.CTRL); |
| if ((stateMask & SWT.ALT) == SWT.ALT) |
| modifierString= appendModifierString(modifierString, SWT.ALT); |
| if ((stateMask & SWT.SHIFT) == SWT.SHIFT) |
| modifierString= appendModifierString(modifierString, SWT.SHIFT); |
| if ((stateMask & SWT.COMMAND) == SWT.COMMAND) |
| modifierString= appendModifierString(modifierString, SWT.COMMAND); |
| |
| return modifierString; |
| } |
| |
| /** |
| * Appends to modifier string of the given SWT modifier bit |
| * to the given modifierString. |
| * |
| * @param modifierString the modifier string |
| * @param modifier an int with SWT modifier bit |
| * @return the concatenated modifier string |
| * @since 2.1.1 |
| */ |
| private static String appendModifierString(String modifierString, int modifier) { |
| if (modifierString == null) |
| modifierString= ""; //$NON-NLS-1$ |
| String newModifierString= Action.findModifierString(modifier); |
| if (modifierString.length() == 0) |
| return newModifierString; |
| return Messages.format(JavaEditorMessages.EditorUtility_concatModifierStrings, new String[] {modifierString, newModifierString}); |
| } |
| |
| /** |
| * Returns the Java project for a given editor input or <code>null</code> if no corresponding |
| * Java project exists. |
| * |
| * @param input the editor input |
| * @return the corresponding Java project |
| * |
| * @since 3.0 |
| */ |
| public static IJavaProject getJavaProject(IEditorInput input) { |
| IJavaProject jProject= null; |
| if (input instanceof IFileEditorInput) { |
| IProject project= ((IFileEditorInput)input).getFile().getProject(); |
| if (project != null) { |
| jProject= JavaCore.create(project); |
| if (!jProject.exists()) |
| jProject= null; |
| } |
| } else if (input instanceof IClassFileEditorInput) { |
| jProject= ((IClassFileEditorInput)input).getClassFile().getJavaProject(); |
| } |
| return jProject; |
| } |
| |
| /** |
| * Returns the Java project for a given text editor or <code>null</code> if no corresponding |
| * Java project exists. |
| * |
| * @param editor the text editor |
| * |
| * @return the corresponding Java project |
| * |
| * @since 3.19 |
| */ |
| public static IJavaProject getJavaProject(ITextEditor editor) { |
| if (editor == null) |
| return null; |
| |
| IJavaElement element= null; |
| IEditorInput input= editor.getEditorInput(); |
| IDocumentProvider provider= editor.getDocumentProvider(); |
| if (provider instanceof ICompilationUnitDocumentProvider) { |
| ICompilationUnitDocumentProvider cudp= (ICompilationUnitDocumentProvider) provider; |
| element= cudp.getWorkingCopy(input); |
| } else if (input instanceof IClassFileEditorInput) { |
| IClassFileEditorInput cfei= (IClassFileEditorInput) input; |
| element= cfei.getClassFile(); |
| } |
| |
| if (element == null) { |
| if (input != null) { |
| return EditorUtility.getJavaProject(input); |
| } |
| return null; |
| } |
| |
| return element.getJavaProject(); |
| } |
| |
| /** |
| * Returns an array of all editors that have an unsaved content. If the identical content is |
| * presented in more than one editor, only one of those editor parts is part of the result. |
| * @param skipNonResourceEditors if <code>true</code>, editors whose inputs do not adapt to {@link IResource} |
| * are not saved |
| * |
| * @return an array of dirty editor parts |
| * @since 3.4 |
| */ |
| public static IEditorPart[] getDirtyEditors(boolean skipNonResourceEditors) { |
| Set<IEditorInput> inputs= new HashSet<>(); |
| List<IEditorPart> result= new ArrayList<>(0); |
| IWorkbench workbench= PlatformUI.getWorkbench(); |
| IWorkbenchWindow[] windows= workbench.getWorkbenchWindows(); |
| for (int i= 0; i < windows.length; i++) { |
| IWorkbenchPage[] pages= windows[i].getPages(); |
| for (int x= 0; x < pages.length; x++) { |
| IEditorPart[] editors= pages[x].getDirtyEditors(); |
| for (int z= 0; z < editors.length; z++) { |
| IEditorPart ep= editors[z]; |
| IEditorInput input= ep.getEditorInput(); |
| if (inputs.add(input)) { |
| if (!skipNonResourceEditors || isResourceEditorInput(input)) { |
| result.add(ep); |
| } |
| } |
| } |
| } |
| } |
| return result.toArray(new IEditorPart[result.size()]); |
| } |
| |
| private static boolean isResourceEditorInput(IEditorInput input) { |
| if (input instanceof MultiEditorInput) { |
| IEditorInput[] inputs= ((MultiEditorInput) input).getInput(); |
| for (int i= 0; i < inputs.length; i++) { |
| if (inputs[i].getAdapter(IResource.class) != null) { |
| return true; |
| } |
| } |
| } else if (input.getAdapter(IResource.class) != null) { |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean isClassFile(IFile file) { |
| IContentDescription contentDescription; |
| try { |
| contentDescription= file.getContentDescription(); |
| } catch (CoreException e) { |
| contentDescription= null; |
| } |
| if (contentDescription == null) |
| return false; |
| |
| IContentType contentType= contentDescription.getContentType(); |
| if (contentType == null) |
| return false; |
| |
| return "org.eclipse.jdt.core.javaClass".equals(contentType.getId()); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns the editors to save before performing global Java-related |
| * operations. |
| * |
| * @param saveUnknownEditors <code>true</code> iff editors with unknown buffer management should also be saved |
| * @return the editors to save |
| * @since 3.3 |
| */ |
| public static IEditorPart[] getDirtyEditorsToSave(boolean saveUnknownEditors) { |
| Set<IEditorInput> inputs= new HashSet<>(); |
| List<IEditorPart> result= new ArrayList<>(0); |
| IWorkbench workbench= PlatformUI.getWorkbench(); |
| IWorkbenchWindow[] windows= workbench.getWorkbenchWindows(); |
| for (int i= 0; i < windows.length; i++) { |
| IWorkbenchPage[] pages= windows[i].getPages(); |
| for (int x= 0; x < pages.length; x++) { |
| IEditorPart[] editors= pages[x].getDirtyEditors(); |
| for (int z= 0; z < editors.length; z++) { |
| IEditorPart ep= editors[z]; |
| IEditorInput input= ep.getEditorInput(); |
| if (!mustSaveDirtyEditor(ep, input, saveUnknownEditors)) |
| continue; |
| |
| if (inputs.add(input)) |
| result.add(ep); |
| } |
| } |
| } |
| return result.toArray(new IEditorPart[result.size()]); |
| } |
| |
| /* |
| * @since 3.3 |
| */ |
| private static boolean mustSaveDirtyEditor(IEditorPart ep, IEditorInput input, boolean saveUnknownEditors) { |
| /* |
| * Goal: save all editors that could interfere with refactoring operations. |
| * |
| * Always save all editors for compilation units that are not working copies. |
| * (Leaving them dirty would cause problems, since the file buffer could have been |
| * modified but the Java model is not reconciled.) |
| * |
| * If <code>saveUnknownEditors</code> is <code>true</code>, save all editors |
| * whose implementation is probably not based on file buffers. |
| */ |
| IResource resource= input.getAdapter(IResource.class); |
| if (resource == null) |
| return saveUnknownEditors; |
| |
| IJavaElement javaElement= JavaCore.create(resource); |
| if (javaElement instanceof ICompilationUnit) { |
| ICompilationUnit cu= (ICompilationUnit) javaElement; |
| if (!cu.isWorkingCopy()) { |
| return true; |
| } |
| } |
| |
| if (! (ep instanceof ITextEditor)) |
| return saveUnknownEditors; |
| |
| ITextEditor textEditor= (ITextEditor) ep; |
| IDocumentProvider documentProvider= textEditor.getDocumentProvider(); |
| if (! (documentProvider instanceof TextFileDocumentProvider)) |
| return saveUnknownEditors; |
| |
| return false; |
| } |
| |
| /** |
| * Return the regions of all lines which have changed in the given buffer since the last save |
| * occurred. Each region in the result spans over the size of at least one line. If successive |
| * lines have changed a region spans over the size of all successive lines. The regions include |
| * line delimiters. |
| * |
| * @param buffer the buffer to compare contents from |
| * @param monitor to report progress to |
| * @return the regions of the changed lines |
| * @throws CoreException if something goes wrong |
| * @since 3.4 |
| */ |
| public static IRegion[] calculateChangedLineRegions(final ITextFileBuffer buffer, final IProgressMonitor monitor) throws CoreException { |
| final IRegion[][] result= new IRegion[1][]; |
| final IStatus[] errorStatus= new IStatus[] { Status.OK_STATUS }; |
| |
| try { |
| SafeRunner.run(new ISafeRunnable() { |
| |
| /* |
| * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) |
| */ |
| @Override |
| public void handleException(Throwable exception) { |
| JavaPlugin.log(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_CHANGED_REGION_CALCULATION, exception.getLocalizedMessage(), exception)); |
| String msg= JavaEditorMessages.CompilationUnitDocumentProvider_error_calculatingChangedRegions; |
| errorStatus[0]= new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.EDITOR_CHANGED_REGION_CALCULATION, msg, exception); |
| result[0]= null; |
| } |
| |
| /* |
| * @see org.eclipse.core.runtime.ISafeRunnable#run() |
| */ |
| @Override |
| public void run() throws Exception { |
| monitor.beginTask(JavaEditorMessages.CompilationUnitDocumentProvider_calculatingChangedRegions_message, 20); |
| IPath location= buffer.getLocation(); |
| |
| ITextFileBufferManager fileBufferManager= FileBuffers.createTextFileBufferManager(); |
| fileBufferManager.connect(location, LocationKind.NORMALIZE, getSubProgressMonitor(monitor, 15)); |
| try { |
| IDocument currentDocument= buffer.getDocument(); |
| IDocument oldDocument= fileBufferManager.getTextFileBuffer(location, LocationKind.NORMALIZE).getDocument(); |
| |
| result[0]= getChangedLineRegions(oldDocument, currentDocument); |
| } finally { |
| fileBufferManager.disconnect(location, LocationKind.NORMALIZE, getSubProgressMonitor(monitor, 5)); |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * Return regions of all lines which differ comparing <code>oldDocument</code>s |
| * content with <code>currentDocument</code>s content. Successive lines are merged |
| * into one region. |
| * |
| * @param oldDocument a document containing the old content |
| * @param currentDocument a document containing the current content |
| * @return the changed regions |
| * @throws BadLocationException if fetching the line information fails |
| */ |
| private IRegion[] getChangedLineRegions(IDocument oldDocument, IDocument currentDocument) throws BadLocationException { |
| /* |
| * Do not change the type of those local variables. We use Object |
| * here in order to prevent loading of the Compare plug-in at load |
| * time of this class. |
| */ |
| Object leftSide= new LineComparator(oldDocument); |
| Object rightSide= new LineComparator(currentDocument); |
| |
| RangeDifference[] differences= RangeDifferencer.findDifferences((IRangeComparator) leftSide, (IRangeComparator) rightSide); |
| |
| //It holds that: |
| //1. Ranges are sorted: |
| // forAll r1,r2 element differences: indexOf(r1)<indexOf(r2) -> r1.rightStart()<r2.rightStart(); |
| //2. Successive changed lines are merged into on RangeDifference |
| // forAll r1,r2 element differences: r1.rightStart()<r2.rightStart() -> r1.rightEnd()<r2.rightStart |
| |
| ArrayList<IRegion> regions= new ArrayList<>(); |
| for (int i= 0; i < differences.length; i++) { |
| RangeDifference curr= differences[i]; |
| if (curr.kind() == RangeDifference.CHANGE && curr.rightLength() > 0) { |
| int startLine= curr.rightStart(); |
| int endLine= curr.rightEnd() - 1; |
| |
| IRegion startLineRegion= currentDocument.getLineInformation(startLine); |
| if (startLine == endLine) { |
| regions.add(startLineRegion); |
| } else { |
| IRegion endLineRegion= currentDocument.getLineInformation(endLine); |
| int startOffset= startLineRegion.getOffset(); |
| int endOffset= endLineRegion.getOffset() + endLineRegion.getLength(); |
| regions.add(new Region(startOffset, endOffset - startOffset)); |
| } |
| } |
| } |
| |
| return regions.toArray(new IRegion[regions.size()]); |
| } |
| }); |
| } finally { |
| if (!errorStatus[0].isOK()) |
| throw new CoreException(errorStatus[0]); |
| } |
| |
| return result[0]; |
| } |
| |
| /** |
| * Creates and returns a new sub-progress monitor for the |
| * given parent monitor. |
| * |
| * @param monitor the parent progress monitor |
| * @param ticks the number of work ticks allocated from the parent monitor |
| * @return the new sub-progress monitor |
| * @since 3.4 |
| */ |
| private static IProgressMonitor getSubProgressMonitor(IProgressMonitor monitor, int ticks) { |
| if (monitor != null) |
| return new SubProgressMonitor(monitor, ticks, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); |
| |
| return new NullProgressMonitor(); |
| } |
| |
| } |