1 /*******************************************************************************
2  * Copyright (c) 2003, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Jan-Ove Weichel <janove.weichel@vogella.com> - Bug 411578
14  *     Andrey Loskutov <loskutov@gmx.de> - Bug 485201, 496475
15  *     Mickael Istria (Red Hat Inc.) - Bug 90292 (default editor) and family
16  *******************************************************************************/
17 package org.eclipse.ui.ide;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URI;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 
26 import org.eclipse.core.filesystem.EFS;
27 import org.eclipse.core.filesystem.IFileStore;
28 import org.eclipse.core.resources.IFile;
29 import org.eclipse.core.resources.IFolder;
30 import org.eclipse.core.resources.IMarker;
31 import org.eclipse.core.resources.IProject;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IResourceDelta;
34 import org.eclipse.core.resources.IResourceStatus;
35 import org.eclipse.core.resources.IWorkspace;
36 import org.eclipse.core.resources.IWorkspaceRoot;
37 import org.eclipse.core.resources.ResourcesPlugin;
38 import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
39 import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
40 import org.eclipse.core.resources.mapping.ModelProvider;
41 import org.eclipse.core.resources.mapping.ModelStatus;
42 import org.eclipse.core.resources.mapping.ResourceChangeValidator;
43 import org.eclipse.core.runtime.Adapters;
44 import org.eclipse.core.runtime.CoreException;
45 import org.eclipse.core.runtime.IAdapterFactory;
46 import org.eclipse.core.runtime.IAdapterManager;
47 import org.eclipse.core.runtime.IStatus;
48 import org.eclipse.core.runtime.MultiStatus;
49 import org.eclipse.core.runtime.OperationCanceledException;
50 import org.eclipse.core.runtime.Platform;
51 import org.eclipse.core.runtime.QualifiedName;
52 import org.eclipse.core.runtime.SafeRunner;
53 import org.eclipse.core.runtime.content.IContentDescription;
54 import org.eclipse.core.runtime.content.IContentType;
55 import org.eclipse.core.runtime.content.IContentTypeMatcher;
56 import org.eclipse.jface.dialogs.ErrorDialog;
57 import org.eclipse.jface.dialogs.IDialogConstants;
58 import org.eclipse.jface.util.SafeRunnable;
59 import org.eclipse.jface.viewers.IStructuredSelection;
60 import org.eclipse.osgi.util.NLS;
61 import org.eclipse.swt.SWT;
62 import org.eclipse.swt.widgets.Composite;
63 import org.eclipse.swt.widgets.Shell;
64 import org.eclipse.ui.IEditorDescriptor;
65 import org.eclipse.ui.IEditorInput;
66 import org.eclipse.ui.IEditorPart;
67 import org.eclipse.ui.IEditorReference;
68 import org.eclipse.ui.IEditorRegistry;
69 import org.eclipse.ui.IMarkerHelpRegistry;
70 import org.eclipse.ui.IWorkbenchPage;
71 import org.eclipse.ui.IWorkbenchPartReference;
72 import org.eclipse.ui.IWorkbenchWindow;
73 import org.eclipse.ui.MultiPartInitException;
74 import org.eclipse.ui.PartInitException;
75 import org.eclipse.ui.PlatformUI;
76 import org.eclipse.ui.internal.ide.EditorAssociationOverrideDescriptor;
77 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
78 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
79 import org.eclipse.ui.internal.ide.model.StandardPropertiesAdapterFactory;
80 import org.eclipse.ui.internal.ide.model.WorkbenchAdapterFactory;
81 import org.eclipse.ui.internal.ide.registry.MarkerHelpRegistry;
82 import org.eclipse.ui.internal.ide.registry.MarkerHelpRegistryReader;
83 import org.eclipse.ui.internal.ide.registry.SystemEditorOrTextEditorStrategy;
84 import org.eclipse.ui.internal.ide.registry.UnassociatedEditorStrategyRegistry;
85 import org.eclipse.ui.internal.misc.UIStats;
86 import org.eclipse.ui.part.FileEditorInput;
87 
88 /**
89  * Collection of IDE-specific APIs factored out of existing workbench. This
90  * class cannot be instantiated; all functionality is provided by static methods
91  * and fields.
92  *
93  * @since 3.0
94  */
95 public final class IDE {
96 	/**
97 	 * The persistent property key used on IFile resources to contain the preferred
98 	 * editor ID to use.
99 	 * <p>
100 	 * Example of retrieving the persisted editor id:
101 	 * </p>
102 	 *
103 	 * <pre>
104 	 * <code>
105 	 *  IFile file = ...
106 	 *  IEditorDescriptor editorDesc = null;
107 	 *  try {
108 	 *  	String editorID = file.getPersistentProperty(EDITOR_KEY);
109 	 *  	if (editorID != null) {
110 	 *  		editorDesc = editorReg.findEditor(editorID);
111 	 *  	}
112 	 *  } catch (CoreException e) {
113 	 *  	// handle problem accessing persistent property here
114 	 *  }
115 	 * </code>
116 	 * </pre>
117 	 *
118 	 * <p>
119 	 * Example of persisting the editor id:
120 	 * </p>
121 	 *
122 	 * <pre>
123 	 * <code>
124 	 *  IFile file = ...
125 	 *  try {
126 	 *  	file.setPersistentProperty(EDITOR_KEY, editorDesc.getId());
127 	 *  } catch (CoreException e) {
128 	 *  	// handle problem setting persistent property here
129 	 *  }
130 	 * </code>
131 	 * </pre>
132 	 *
133 	 */
134 	public static final QualifiedName EDITOR_KEY = new QualifiedName(
135 			"org.eclipse.ui.internal.registry.ResourceEditorRegistry", "EditorProperty");//$NON-NLS-2$//$NON-NLS-1$
136 
137 	/**
138 	 * An optional attribute within a workspace marker (<code>IMarker</code>)
139 	 * which identifies the preferred editor type to be opened.
140 	 */
141 	public static final String EDITOR_ID_ATTR = "org.eclipse.ui.editorID"; //$NON-NLS-1$
142 
143 	/**
144 	 * The resource based perspective identifier.
145 	 */
146 	public static final String RESOURCE_PERSPECTIVE_ID = "org.eclipse.ui.resourcePerspective"; //$NON-NLS-1$
147 
148 	/**
149 	 * A preference key to decide which {@link IUnassociatedEditorStrategy} to use
150 	 * when trying to open files without associated editors.
151 	 *
152 	 * @since 3.12
153 	 */
154 	public static final String UNASSOCIATED_EDITOR_STRATEGY_PREFERENCE_KEY = "unassociatedEditorStrategy";//$NON-NLS-1$
155 
156 	/**
157 	 * Marker help registry mapping markers to help context ids and resolutions;
158 	 * lazily initialized on fist access.
159 	 */
160 	private static MarkerHelpRegistry markerHelpRegistry = null;
161 
162 	private static volatile IEditorAssociationOverride[] editorAssociationOverrides;
163 
164 
165 	/**
166 	 * Standard shared images defined by the IDE. These are over and above the
167 	 * standard workbench images declared in {@link org.eclipse.ui.ISharedImages
168 	 * ISharedImages}.
169 	 * <p>
170 	 * This interface is not intended to be implemented by clients.
171 	 * </p>
172 	 *
173 	 * @see org.eclipse.ui.ISharedImages
174 	 */
175 	public interface SharedImages {
176 		/**
177 		 * Identifies a project image.
178 		 */
179 		public static final String IMG_OBJ_PROJECT = "IMG_OBJ_PROJECT"; //$NON-NLS-1$
180 
181 		/**
182 		 * Identifies a closed project image.
183 		 */
184 		public static final String IMG_OBJ_PROJECT_CLOSED = "IMG_OBJ_PROJECT_CLOSED"; //$NON-NLS-1$
185 
186 		/**
187 		 * Identifies the image used for "open marker".
188 		 */
189 		public static final String IMG_OPEN_MARKER = "IMG_OPEN_MARKER"; //$NON-NLS-1$
190 
191 		/**
192 		 * Identifies the default image used to indicate a task.
193 		 */
194 		public static final String IMG_OBJS_TASK_TSK = "IMG_OBJS_TASK_TSK"; //$NON-NLS-1$
195 
196 		/**
197 		 * Identifies the default image used to indicate a bookmark.
198 		 */
199 		public static final String IMG_OBJS_BKMRK_TSK = "IMG_OBJS_BKMRK_TSK"; //$NON-NLS-1$
200 	}
201 
202 	/**
203 	 * Preferences defined by the IDE workbench.
204 	 * <p>
205 	 * This interface is not intended to be implemented by clients.
206 	 * </p>
207 	 * @noimplement This interface is not intended to be implemented by clients.
208 	 */
209 	public interface Preferences {
210 
211 
212 		/**
213 		 * A named preference for how a new perspective should be opened when a
214 		 * new project is created.
215 		 * <p>
216 		 * Value is of type <code>String</code>. The possible values are
217 		 * defined by the constants
218 		 * <code>OPEN_PERSPECTIVE_WINDOW, OPEN_PERSPECTIVE_PAGE,
219 		 * OPEN_PERSPECTIVE_REPLACE, and NO_NEW_PERSPECTIVE</code>.
220 		 * </p>
221 		 *
222 		 * @see org.eclipse.ui.IWorkbenchPreferenceConstants#OPEN_PERSPECTIVE_WINDOW
223 		 * @see org.eclipse.ui.IWorkbenchPreferenceConstants#OPEN_PERSPECTIVE_PAGE
224 		 * @see org.eclipse.ui.IWorkbenchPreferenceConstants#OPEN_PERSPECTIVE_REPLACE
225 		 * @see org.eclipse.ui.IWorkbenchPreferenceConstants#NO_NEW_PERSPECTIVE
226 		 */
227 		public static final String PROJECT_OPEN_NEW_PERSPECTIVE = "PROJECT_OPEN_NEW_PERSPECTIVE"; //$NON-NLS-1$
228 
229 		/**
230 		 * <p>
231 		 * Specifies whether or not the workspace selection dialog should be
232 		 * shown on startup.
233 		 * </p>
234 		 * <p>
235 		 * The default value for this preference is <code>true</code>.
236 		 * </p>
237 		 *
238 		 * @since 3.1
239 		 */
240 		public static final String SHOW_WORKSPACE_SELECTION_DIALOG = "SHOW_WORKSPACE_SELECTION_DIALOG"; //$NON-NLS-1$
241 
242 		/**
243 		 * Specifies whether the "Recent Workspaces" should be shown
244 		 *
245 		 * @since 3.12
246 		 */
247 		public static final String SHOW_RECENT_WORKSPACES = "SHOW_RECENT_WORKSPACES"; //$NON-NLS-1$
248 
249 		/**
250 		 * <p>
251 		 * Stores the maximum number of workspaces that should be displayed in
252 		 * the ChooseWorkspaceDialog.
253 		 * </p>
254 		 *
255 		 * @since 3.1
256 		 */
257 		public static final String MAX_RECENT_WORKSPACES = "MAX_RECENT_WORKSPACES"; //$NON-NLS-1$
258 
259 		/**
260 		 * <p>
261 		 * Stores a comma separated list of the recently used workspace paths.
262 		 * </p>
263 		 *
264 		 * @since 3.1
265 		 */
266 		public static final String RECENT_WORKSPACES = "RECENT_WORKSPACES"; //$NON-NLS-1$
267 
268 		/**
269 		 * <p>
270 		 * Stores the version of the protocol used to decode/encode the list of
271 		 * recent workspaces.
272 		 * </p>
273 		 *
274 		 * @since 3.1
275 		 */
276 		public static final String RECENT_WORKSPACES_PROTOCOL = "RECENT_WORKSPACES_PROTOCOL"; //$NON-NLS-1$
277 
278 		/**
279 		 * Workspace name, will be displayed in the window title. This
280 		 * preference must only be changed on the UI thread.
281 		 * @since 3.10
282 		 */
283 		public static final String WORKSPACE_NAME = "WORKSPACE_NAME"; //$NON-NLS-1$
284 	}
285 
286 	/**
287 	 * Block instantiation.
288 	 */
IDE()289 	private IDE() {
290 		// do nothing
291 	}
292 
293 	/**
294 	 * Returns the marker help registry for the workbench.
295 	 *
296 	 * @return the marker help registry
297 	 */
getMarkerHelpRegistry()298 	public static IMarkerHelpRegistry getMarkerHelpRegistry() {
299 		if (markerHelpRegistry == null) {
300 			markerHelpRegistry = new MarkerHelpRegistry();
301 			new MarkerHelpRegistryReader().addHelp(markerHelpRegistry);
302 		}
303 		return markerHelpRegistry;
304 	}
305 
306 	/**
307 	 * Sets the cursor and selection state for the given editor to reveal the
308 	 * position of the given marker. This is done on a best effort basis. If the
309 	 * editor does not provide an <code>IGotoMarker</code> interface (either
310 	 * directly or via <code>IAdaptable.getAdapter</code>), this has no
311 	 * effect.
312 	 *
313 	 * @param editor
314 	 *            the editor
315 	 * @param marker
316 	 *            the marker
317 	 */
gotoMarker(IEditorPart editor, IMarker marker)318 	public static void gotoMarker(IEditorPart editor, IMarker marker) {
319 		IGotoMarker gotoMarker = Adapters.adapt(editor, IGotoMarker.class);
320 		if (gotoMarker != null) {
321 			gotoMarker.gotoMarker(marker);
322 		}
323 	}
324 
325 	/**
326 	 * Opens an editor on the given object.
327 	 * <p>
328 	 * If the page already has an editor open on the target object then that
329 	 * editor is brought to front; otherwise, a new editor is opened.
330 	 * <p>
331 	 *
332 	 * @param page
333 	 *            the page in which the editor will be opened
334 	 * @param input
335 	 *            the editor input
336 	 * @param editorId
337 	 *            the id of the editor extension to use
338 	 * @return an open editor or <code>null</code> if an external editor was
339 	 *         opened
340 	 * @exception PartInitException
341 	 *                if the editor could not be initialized
342 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
343 	 */
openEditor(IWorkbenchPage page, IEditorInput input, String editorId)344 	public static IEditorPart openEditor(IWorkbenchPage page,
345 			IEditorInput input, String editorId) throws PartInitException {
346 		// sanity checks
347 		if (page == null) {
348 			throw new IllegalArgumentException();
349 		}
350 
351 		// open the editor on the file
352 		return page.openEditor(input, editorId);
353 	}
354 
355 	/**
356 	 * Opens an editor on the given IFileStore object.
357 	 * <p>
358 	 * Unlike the other <code>openEditor</code> methods, this one can be used
359 	 * to open files that reside outside the workspace resource set.
360 	 * </p>
361 	 * <p>
362 	 * If the page already has an editor open on the target object then that
363 	 * editor is brought to front; otherwise, a new editor is opened.
364 	 * </p>
365 	 *
366 	 * @param page
367 	 *            the page in which the editor will be opened
368 	 * @param uri
369 	 *            the URI of the file store representing the file to open
370 	 * @param editorId
371 	 *            the id of the editor extension to use
372 	 * @param activate
373 	 *            if <code>true</code> the editor will be activated opened
374 	 * @return an open editor or <code>null</code> if an external editor was
375 	 *         opened
376 	 * @exception PartInitException
377 	 *                if the editor could not be initialized
378 	 *
379 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
380 	 * @see EFS#getStore(URI)
381 	 *
382 	 * @since 3.3
383 	 */
openEditor(IWorkbenchPage page, URI uri, String editorId, boolean activate)384 	public static IEditorPart openEditor(IWorkbenchPage page, URI uri,
385 			String editorId, boolean activate) throws PartInitException {
386 		// sanity checks
387 		if (page == null) {
388 			throw new IllegalArgumentException();
389 		}
390 
391 		IFileStore fileStore;
392 		try {
393 			fileStore = EFS.getStore(uri);
394 		} catch (CoreException e) {
395 			throw new PartInitException(
396 					IDEWorkbenchMessages.IDE_coreExceptionFileStore, e);
397 		}
398 
399 		IEditorInput input = getEditorInput(fileStore);
400 
401 		// open the editor on the file
402 		return page.openEditor(input, editorId, activate);
403 	}
404 
405 	/**
406 	 * Create the Editor Input appropriate for the given <code>IFileStore</code>.
407 	 * The result is a normal file editor input if the file exists in the
408 	 * workspace and, if not, we create a wrapper capable of managing an
409 	 * 'external' file using its <code>IFileStore</code>.
410 	 *
411 	 * @param fileStore
412 	 *            The file store to provide the editor input for
413 	 * @return The editor input associated with the given file store
414 	 * @since 3.3
415 	 */
getEditorInput(IFileStore fileStore)416 	private static IEditorInput getEditorInput(IFileStore fileStore) {
417 		IFile workspaceFile = getWorkspaceFile(fileStore);
418 		if (workspaceFile != null)
419 			return new FileEditorInput(workspaceFile);
420 		return new FileStoreEditorInput(fileStore);
421 	}
422 
423 	/**
424 	 * Determine whether or not the <code>IFileStore</code> represents a file
425 	 * currently in the workspace.
426 	 *
427 	 * @param fileStore
428 	 *            The <code>IFileStore</code> to test
429 	 * @return The workspace's <code>IFile</code> if it exists or
430 	 *         <code>null</code> if not
431 	 */
getWorkspaceFile(IFileStore fileStore)432 	private static IFile getWorkspaceFile(IFileStore fileStore) {
433 		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
434 		IFile[] files = root.findFilesForLocationURI(fileStore.toURI());
435 		files = filterNonExistentFiles(files);
436 		if (files == null || files.length == 0)
437 			return null;
438 
439 		// for now only return the first file
440 		return files[0];
441 	}
442 
443 	/**
444 	 * Filter the incoming array of <code>IFile</code> elements by removing
445 	 * any that do not currently exist in the workspace.
446 	 *
447 	 * @param files
448 	 *            The array of <code>IFile</code> elements
449 	 * @return The filtered array
450 	 */
filterNonExistentFiles(IFile[] files)451 	private static IFile[] filterNonExistentFiles(IFile[] files) {
452 		if (files == null)
453 			return null;
454 
455 		int length = files.length;
456 		ArrayList<IFile> existentFiles = new ArrayList<>(length);
457 		for (int i = 0; i < length; i++) {
458 			if (files[i].exists())
459 				existentFiles.add(files[i]);
460 		}
461 		return existentFiles.toArray(new IFile[existentFiles.size()]);
462 	}
463 
464 	/**
465 	 * Opens an editor on the given object.
466 	 * <p>
467 	 * If the page already has an editor open on the target object then that
468 	 * editor is brought to front; otherwise, a new editor is opened. If
469 	 * <code>activate == true</code> the editor will be activated.
470 	 * <p>
471 	 *
472 	 * @param page
473 	 *            the page in which the editor will be opened
474 	 * @param input
475 	 *            the editor input
476 	 * @param editorId
477 	 *            the id of the editor extension to use
478 	 * @param activate
479 	 *            if <code>true</code> the editor will be activated
480 	 * @return an open editor or <code>null</code> if an external editor was
481 	 *         opened
482 	 * @exception PartInitException
483 	 *                if the editor could not be initialized
484 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String,
485 	 *      boolean)
486 	 */
openEditor(IWorkbenchPage page, IEditorInput input, String editorId, boolean activate)487 	public static IEditorPart openEditor(IWorkbenchPage page,
488 			IEditorInput input, String editorId, boolean activate)
489 			throws PartInitException {
490 		// sanity checks
491 		if (page == null) {
492 			throw new IllegalArgumentException();
493 		}
494 
495 		// open the editor on the file
496 		return page.openEditor(input, editorId, activate);
497 	}
498 
499 	/**
500 	 * Opens an editor on the given file resource. This method will attempt to
501 	 * resolve the editor based on content-type bindings as well as traditional
502 	 * name/extension bindings.
503 	 * <p>
504 	 * If the page already has an editor open on the target object then that
505 	 * editor is brought to front; otherwise, a new editor is opened. If
506 	 * <code>activate == true</code> the editor will be activated.
507 	 * <p>
508 	 *
509 	 * @param page
510 	 *            the page in which the editor will be opened
511 	 * @param input
512 	 *            the editor input
513 	 * @param activate
514 	 *            if <code>true</code> the editor will be activated
515 	 * @return an open editor or <code>null</code> if an external editor was
516 	 *         opened or if opening was canceled
517 	 * @exception PartInitException
518 	 *                if the editor could not be initialized
519 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(org.eclipse.ui.IEditorInput,
520 	 *      String, boolean)
521 	 */
openEditor(IWorkbenchPage page, IFile input, boolean activate)522 	public static IEditorPart openEditor(IWorkbenchPage page, IFile input,
523 			boolean activate) throws PartInitException {
524 		return openEditor(page, input, activate, true);
525 	}
526 
527 	/**
528 	 * Opens an editor on the given file resource. This method will attempt to
529 	 * resolve the editor based on content-type bindings as well as traditional
530 	 * name/extension bindings if <code>determineContentType</code> is
531 	 * <code>true</code>.
532 	 * <p>
533 	 * If the page already has an editor open on the target object then that
534 	 * editor is brought to front; otherwise, a new editor is opened. If
535 	 * <code>activate == true</code> the editor will be activated.
536 	 * <p>
537 	 *
538 	 * @param page
539 	 *            the page in which the editor will be opened
540 	 * @param input
541 	 *            the editor input
542 	 * @param activate
543 	 *            if <code>true</code> the editor will be activated
544 	 * @param determineContentType
545 	 *            attempt to resolve the content type for this file
546 	 * @return an open editor or <code>null</code> if an external editor was
547 	 *         opened or if opening was canceled
548 	 * @exception PartInitException
549 	 *                if the editor could not be initialized
550 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(org.eclipse.ui.IEditorInput,
551 	 *      String, boolean)
552 	 * @since 3.1
553 	 */
openEditor(IWorkbenchPage page, IFile input, boolean activate, boolean determineContentType)554 	public static IEditorPart openEditor(IWorkbenchPage page, IFile input,
555 			boolean activate, boolean determineContentType)
556 			throws PartInitException {
557 		// sanity checks
558 		if (page == null) {
559 			throw new IllegalArgumentException();
560 		}
561 
562 		// open the editor on the file
563 		IEditorDescriptor editorDesc;
564 		try {
565 			editorDesc = getEditorDescriptor(input, determineContentType, true);
566 		} catch (OperationCanceledException ex) {
567 			return null;
568 		}
569 		return page.openEditor(new FileEditorInput(input), editorDesc.getId(),
570 				activate);
571 	}
572 
573 	/**
574 	 * Opens an editor on the given file resource. This method will attempt to
575 	 * resolve the editor based on content-type bindings as well as traditional
576 	 * name/extension bindings.
577 	 * <p>
578 	 * If the page already has an editor open on the target object then that
579 	 * editor is brought to front; otherwise, a new editor is opened.
580 	 * <p>
581 	 *
582 	 * @param page
583 	 *            the page in which the editor will be opened
584 	 * @param input
585 	 *            the editor input
586 	 * @return an open editor or <code>null</code> if an external editor was
587 	 *         opened or if opening was canceled
588 	 * @exception PartInitException
589 	 *                if the editor could not be initialized
590 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
591 	 */
openEditor(IWorkbenchPage page, IFile input)592 	public static IEditorPart openEditor(IWorkbenchPage page, IFile input)
593 			throws PartInitException {
594 		// sanity checks
595 		if (page == null) {
596 			throw new IllegalArgumentException();
597 		}
598 
599 		// open the editor on the file
600 		IEditorDescriptor editorDesc;
601 		try {
602 			editorDesc = getEditorDescriptor(input, true, true);
603 		} catch (OperationCanceledException ex) {
604 			return null;
605 		}
606 		return page.openEditor(new FileEditorInput(input), editorDesc.getId());
607 	}
608 
609 	/**
610 	 * Opens an editor on the given file resource.
611 	 * <p>
612 	 * If the page already has an editor open on the target object then that
613 	 * editor is brought to front; otherwise, a new editor is opened.
614 	 * <p>
615 	 *
616 	 * @param page
617 	 *            the page in which the editor will be opened
618 	 * @param input
619 	 *            the editor input
620 	 * @param editorId
621 	 *            the id of the editor extension to use
622 	 * @return an open editor or <code>null</code> if an external editor was
623 	 *         opened
624 	 * @exception PartInitException
625 	 *                if the editor could not be initialized
626 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
627 	 */
openEditor(IWorkbenchPage page, IFile input, String editorId)628 	public static IEditorPart openEditor(IWorkbenchPage page, IFile input,
629 			String editorId) throws PartInitException {
630 		// sanity checks
631 		if (page == null) {
632 			throw new IllegalArgumentException();
633 		}
634 
635 		// open the editor on the file
636 		return page.openEditor(new FileEditorInput(input), editorId);
637 	}
638 
639 	/**
640 	 * Opens an editor on the given file resource.
641 	 * <p>
642 	 * If the page already has an editor open on the target object then that
643 	 * editor is brought to front; otherwise, a new editor is opened. If
644 	 * <code>activate == true</code> the editor will be activated.
645 	 * <p>
646 	 *
647 	 * @param page
648 	 *            the page in which the editor will be opened
649 	 * @param input
650 	 *            the editor input
651 	 * @param editorId
652 	 *            the id of the editor extension to use
653 	 * @param activate
654 	 *            if <code>true</code> the editor will be activated
655 	 * @return an open editor or <code>null</code> if an external editor was
656 	 *         opened
657 	 * @exception PartInitException
658 	 *                if the editor could not be initialized
659 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String,
660 	 *      boolean)
661 	 */
openEditor(IWorkbenchPage page, IFile input, String editorId, boolean activate)662 	public static IEditorPart openEditor(IWorkbenchPage page, IFile input,
663 			String editorId, boolean activate) throws PartInitException {
664 		// sanity checks
665 		if (page == null) {
666 			throw new IllegalArgumentException();
667 		}
668 
669 		// open the editor on the file
670 		return page.openEditor(new FileEditorInput(input), editorId, activate);
671 	}
672 
673 	/**
674 	 * Returns an editor descriptor appropriate for opening the given file
675 	 * resource.
676 	 * <p>
677 	 * The editor descriptor is determined using a multi-step process. This
678 	 * method will attempt to resolve the editor based on content-type bindings
679 	 * as well as traditional name/extension bindings.
680 	 * </p>
681 	 * <ol>
682 	 * <li>The <code>IResource</code> is consulted for a persistent property named
683 	 * <code>IDE.EDITOR_KEY</code> containing the preferred editor id to be
684 	 * used.</li>
685 	 * <li>The workbench editor registry is consulted to determine if an editor
686 	 * extension has been registered for the file type. If so, an instance of
687 	 * the editor extension is opened on the file. See
688 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
689 	 * <li>The operating system is consulted to determine if an in-place
690 	 * component editor is available (e.g. OLE editor on Win32 platforms).</li>
691 	 * <li>The operating system is consulted to determine if an external editor
692 	 * is available.</li>
693 	 * <li>The workbench editor registry is consulted to determine if the
694 	 * default text editor is available.</li>
695 	 * </ol>
696 	 *
697 	 * @param file
698 	 *            the file
699 	 * @return an editor descriptor, appropriate for opening the file
700 	 * @throws PartInitException
701 	 *             if no editor can be found
702 	 * @deprecated Since 3.12, use
703 	 *             {@link IDE#getEditorDescriptor(IFile, boolean, boolean)}
704 	 */
705 	@Deprecated
getEditorDescriptor(IFile file)706 	public static IEditorDescriptor getEditorDescriptor(IFile file) throws PartInitException {
707 		return getEditorDescriptor(file, true);
708 	}
709 
710 	/**
711 	 * Returns an editor descriptor appropriate for opening the given file
712 	 * resource.
713 	 * <p>
714 	 * The editor descriptor is determined using a multi-step process. This
715 	 * method will attempt to resolve the editor based on content-type bindings
716 	 * as well as traditional name/extension bindings if
717 	 * <code>determineContentType</code>is <code>true</code>.
718 	 * </p>
719 	 * <ol>
720 	 * <li>The <code>IResource</code> is consulted for a persistent property named
721 	 * <code>IDE.EDITOR_KEY</code> containing the preferred editor id to be
722 	 * used.</li>
723 	 * <li>The workbench editor registry is consulted to determine if an editor
724 	 * extension has been registered for the file type. If so, an instance of
725 	 * the editor extension is opened on the file. See
726 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
727 	 * <li>The operating system is consulted to determine if an in-place
728 	 * component editor is available (e.g. OLE editor on Win32 platforms).</li>
729 	 * <li>The operating system is consulted to determine if an external editor
730 	 * is available.</li>
731 	 * <li>The workbench editor registry is consulted to determine if the
732 	 * default text editor is available.</li>
733 	 * </ol>
734 	 *
735 	 * @param file
736 	 *            the file
737 	 * @param determineContentType
738 	 *            query the content type system for the content type of the file
739 	 * @return an editor descriptor, appropriate for opening the file
740 	 * @throws PartInitException
741 	 *             if no editor can be found
742 	 * @since 3.1
743 	 * @deprecated Since 3.12, use
744 	 *             {@link IDE#getEditorDescriptor(IFile, boolean, boolean)}
745 	 *             instead.
746 	 */
747 	@Deprecated
getEditorDescriptor(IFile file, boolean determineContentType)748 	public static IEditorDescriptor getEditorDescriptor(IFile file, boolean determineContentType) throws PartInitException {
749 
750 		if (file == null) {
751 			throw new IllegalArgumentException();
752 		}
753 
754 		return getEditorDescriptor(file.getName(), PlatformUI.getWorkbench()
755 				.getEditorRegistry(), getDefaultEditor(file,
756 				determineContentType));
757 	}
758 
759 	/**
760 	 * Returns an editor descriptor appropriate for opening the given file resource.
761 	 * <p>
762 	 * The editor descriptor is determined using a multi-step process. This method
763 	 * will attempt to resolve the editor based on content-type bindings as well as
764 	 * traditional name/extension bindings if <code>determineContentType</code>is
765 	 * <code>true</code>.
766 	 * </p>
767 	 * <ol>
768 	 * <li>The <code>IResource</code> is consulted for a persistent property named
769 	 * <code>IDE.EDITOR_KEY</code> containing the preferred editor id to be
770 	 * used.</li>
771 	 * <li>The workbench editor registry is consulted to determine if an editor
772 	 * extension has been registered for the file type. If so, an instance of the
773 	 * editor extension is opened on the file. See
774 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
775 	 * <li>The preferred {@link IUnassociatedEditorStrategy} is consulted.</li>
776 	 * <li>The {@link SystemEditorOrTextEditorStrategy} is consulted, whose behavior
777 	 * is
778 	 * <ol>
779 	 * <li>The operating system is consulted to determine if an in-place component
780 	 * editor is available (e.g. OLE editor on Win32 platforms).</li>
781 	 * <li>The operating system is consulted to determine if an external editor is
782 	 * available.</li>
783 	 * <li>The workbench editor registry is consulted to determine if the default
784 	 * text editor is available.</li>
785 	 * </ol>
786 	 * </li>
787 	 * </ol>
788 	 *
789 	 * @param file                 the file
790 	 * @param determineContentType query the content type system for the content
791 	 *                             type of the file
792 	 * @param allowInteractive     whether we allow user interactions
793 	 * @return an editor descriptor, appropriate for opening the file
794 	 * @throws PartInitException          if no editor can be found
795 	 * @throws OperationCanceledException in case descriptor lookup was canceled by
796 	 *                                    the user
797 	 * @since 3.12
798 	 */
getEditorDescriptor(IFile file, boolean determineContentType, boolean allowInteractive)799 	public static IEditorDescriptor getEditorDescriptor(IFile file, boolean determineContentType, boolean allowInteractive)
800 			throws PartInitException, OperationCanceledException {
801 
802 		if (file == null) {
803 			throw new IllegalArgumentException();
804 		}
805 
806 		return getEditorDescriptor(file.getName(), PlatformUI.getWorkbench().getEditorRegistry(),
807 				getDefaultEditor(file, determineContentType), allowInteractive);
808 	}
809 
810 	/**
811 	 * Returns an editor descriptor appropriate for opening the given file store.
812 	 * <p>
813 	 * The editor descriptor is determined using a multi-step process. This method
814 	 * will attempt to resolve the editor based on content-type bindings as well as
815 	 * traditional name/extension bindings.
816 	 * </p>
817 	 * <ol>
818 	 * <li>The workbench editor registry is consulted to determine if an editor
819 	 * extension has been registered for the file type. If so, an instance of the
820 	 * editor extension is opened on the file. See
821 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
822 	 * <li>The preferred {@link IUnassociatedEditorStrategy} is consulted.</li>
823 	 * <li>The {@link SystemEditorOrTextEditorStrategy} is consulted, whose behavior
824 	 * is
825 	 * <ol>
826 	 * <li>The operating system is consulted to determine if an in-place component
827 	 * editor is available (e.g. OLE editor on Win32 platforms).</li>
828 	 * <li>The operating system is consulted to determine if an external editor is
829 	 * available.</li>
830 	 * <li>The workbench editor registry is consulted to determine if the default
831 	 * text editor is available.</li>
832 	 * </ol>
833 	 * </li>
834 	 * </ol>
835 	 *
836 	 * @param fileStore        the file store
837 	 * @param allowInteractive Whether user interactions are allowed
838 	 * @return editor descriptor of an editor, appropriate for opening the file
839 	 * @throws PartInitException if no editor can be found
840 	 * @since 3.16
841 	 */
getEditorDescriptorForFileStore(IFileStore fileStore, boolean allowInteractive)842 	public static IEditorDescriptor getEditorDescriptorForFileStore(IFileStore fileStore, boolean allowInteractive)
843 			throws PartInitException {
844 		String name = fileStore.fetchInfo().getName();
845 		if (name == null) {
846 			throw new IllegalArgumentException();
847 		}
848 
849 		IContentType contentType = null;
850 		try (InputStream is = fileStore.openInputStream(EFS.NONE, null)) {
851 			contentType = Platform.getContentTypeManager().findContentTypeFor(is, name);
852 		} catch (CoreException | IOException ex) {
853 			// continue without content type
854 		}
855 
856 		IEditorRegistry editorReg = PlatformUI.getWorkbench().getEditorRegistry();
857 
858 		IEditorDescriptor defaultEditor = editorReg.getDefaultEditor(name, contentType);
859 		defaultEditor = overrideDefaultEditorAssociation(new FileStoreEditorInput(fileStore), contentType,
860 				defaultEditor);
861 		return getEditorDescriptor(name, editorReg, defaultEditor, allowInteractive);
862 	}
863 
864 	/**
865 	 * Applies the <code>org.eclipse.ui.ide.editorAssociationOverride</code> extensions to the given
866 	 * input.
867 	 * <p>
868 	 * <strong>Note:</strong> It is recommended to get the descriptor for the default editor by
869 	 * calling {@link #getDefaultEditor(IFile, boolean)}. This method here should only be used if
870 	 * this is not possible for whatever reason.
871 	 * </p>
872 	 *
873 	 * @param editorInput the editor input for the editor
874 	 * @param contentType the content type of the input or <code>null</code> if not available
875 	 * @param editorDescriptor the current association for the given input or <code>null</code> if
876 	 *            none
877 	 * @return the editor descriptor to be used for the given input or <code>null</code> if none.
878 	 *         Can be <code>editorDescriptor</code>.
879 	 * @see IEditorAssociationOverride#overrideDefaultEditor(IEditorInput, IContentType,
880 	 *      IEditorDescriptor)
881 	 * @since 3.8
882 	 */
overrideDefaultEditorAssociation(IEditorInput editorInput, IContentType contentType, IEditorDescriptor editorDescriptor)883 	public static IEditorDescriptor overrideDefaultEditorAssociation(IEditorInput editorInput, IContentType contentType, IEditorDescriptor editorDescriptor) {
884 		for (IEditorAssociationOverride override : getEditorAssociationOverrides()) {
885 			editorDescriptor = override.overrideDefaultEditor(editorInput, contentType, editorDescriptor);
886 		}
887 		return editorDescriptor;
888 	}
889 
890 	/**
891 	 * Applies the <code>org.eclipse.ui.ide.editorAssociationOverride</code> extensions to the given
892 	 * input.
893 	 *
894 	 * @param fileName the name of the file for which to choose the editor
895 	 * @param contentType the content type of the input or <code>null</code> if not available
896 	 * @param editorDescriptor the current association for the given input or <code>null</code> if
897 	 *            none
898 	 * @return the editor descriptor to be used for the given input or <code>null</code> if none.
899 	 *         Can be <code>editorDescriptor</code>.
900 	 * @see IEditorAssociationOverride#overrideDefaultEditor(String, IContentType,
901 	 *      IEditorDescriptor)
902 	 * @since 3.8
903 	 */
overrideDefaultEditorAssociation(String fileName, IContentType contentType, IEditorDescriptor editorDescriptor)904 	private static IEditorDescriptor overrideDefaultEditorAssociation(String fileName, IContentType contentType, IEditorDescriptor editorDescriptor) {
905 		for (IEditorAssociationOverride override : getEditorAssociationOverrides()) {
906 			editorDescriptor = override.overrideDefaultEditor(fileName, contentType, editorDescriptor);
907 		}
908 		return editorDescriptor;
909 	}
910 
911 	/**
912 	 * Applies the <code>org.eclipse.ui.ide.editorAssociationOverride</code> extensions to the given
913 	 * input.
914 	 *
915 	 * @param editorInput the editor input for the editor
916 	 * @param contentType the content type of the input or <code>null</code> if not available
917 	 * @param editorDescriptors the current association for the given input
918 	 * @return the editor descriptors to be used for the given input - can be
919 	 *         <code>editorDescriptors</code>. The order is not relevant.
920 	 * @see IEditorAssociationOverride#overrideEditors(IEditorInput, IContentType,
921 	 *      IEditorDescriptor[])
922 	 * @since 3.8
923 	 */
overrideEditorAssociations(IEditorInput editorInput, IContentType contentType, IEditorDescriptor[] editorDescriptors)924 	public static IEditorDescriptor[] overrideEditorAssociations(IEditorInput editorInput, IContentType contentType, IEditorDescriptor[] editorDescriptors) {
925 		for (IEditorAssociationOverride override : getEditorAssociationOverrides()) {
926 			editorDescriptors = override.overrideEditors(editorInput, contentType, editorDescriptors);
927 		}
928 		return removeNullEntries(editorDescriptors);
929 	}
930 
931 	/**
932 	 * Applies the <code>org.eclipse.ui.ide.editorAssociationOverride</code> extensions to the given
933 	 * input.
934 	 *
935 	 * @param fileName the name of the file for which to choose the editor
936 	 * @param contentType the content type of the input or <code>null</code> if not available
937 	 * @param editorDescriptors the current association for the given input
938 	 * @return the editor descriptors to be used for the given input - can be
939 	 *         <code>editorDescriptors</code>. The order is not relevant.
940 	 * @see IEditorAssociationOverride#overrideEditors(IEditorInput, IContentType,
941 	 *      IEditorDescriptor[])
942 	 * @since 3.8
943 	 */
overrideEditorAssociations(String fileName, IContentType contentType, IEditorDescriptor[] editorDescriptors)944 	public static IEditorDescriptor[] overrideEditorAssociations(String fileName, IContentType contentType, IEditorDescriptor[] editorDescriptors) {
945 		for (IEditorAssociationOverride override : getEditorAssociationOverrides()) {
946 			editorDescriptors = override.overrideEditors(fileName, contentType, editorDescriptors);
947 		}
948 		return removeNullEntries(editorDescriptors);
949 	}
950 
removeNullEntries(IEditorDescriptor[] editorDescriptors)951 	private static IEditorDescriptor[] removeNullEntries(IEditorDescriptor[] editorDescriptors) {
952 		boolean nullDescriptorFound = false;
953 		for (IEditorDescriptor editorDesc : editorDescriptors) {
954 			if (editorDesc == null) {
955 				nullDescriptorFound = true;
956 				break;
957 			}
958 		}
959 		if (!nullDescriptorFound) {
960 			return editorDescriptors;
961 		}
962 		List<IEditorDescriptor> nonNullDescriptors = new ArrayList<>(editorDescriptors.length);
963 		for (IEditorDescriptor editorDesc : editorDescriptors) {
964 			if (editorDesc != null) {
965 				nonNullDescriptors.add(editorDesc);
966 			}
967 		}
968 		return nonNullDescriptors.toArray(new IEditorDescriptor[nonNullDescriptors.size()]);
969 	}
970 
971 	/**
972 	 * Returns an editor descriptor appropriate for opening a file resource with
973 	 * the given name.
974 	 * <p>
975 	 * The editor descriptor is determined using a multi-step process. This
976 	 * method will attempt to infer content type from the file name.
977 	 * </p>
978 	 * <ol>
979 	 * <li>The workbench editor registry is consulted to determine if an editor
980 	 * extension has been registered for the file type. If so, an instance of
981 	 * the editor extension is opened on the file. See
982 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
983 	 * <li>The operating system is consulted to determine if an in-place
984 	 * component editor is available (e.g. OLE editor on Win32 platforms).</li>
985 	 * <li>The operating system is consulted to determine if an external editor
986 	 * is available.</li>
987 	 * <li>The workbench editor registry is consulted to determine if the
988 	 * default text editor is available.</li>
989 	 * </ol>
990 	 *
991 	 * @param name
992 	 *            the file name
993 	 * @return an editor descriptor, appropriate for opening the file
994 	 * @throws PartInitException
995 	 *             if no editor can be found
996 	 * @since 3.1
997 	 * @deprecated Since 3.12, use
998 	 *             {@link IDE#getEditorDescriptor(String, boolean, boolean)}
999 	 *             instead.
1000 	 */
1001 	@Deprecated
getEditorDescriptor(String name)1002 	public static IEditorDescriptor getEditorDescriptor(String name)
1003 			throws PartInitException {
1004 		return getEditorDescriptor(name, true);
1005 	}
1006 
1007 	/**
1008 	 * Returns an editor descriptor appropriate for opening a file resource with
1009 	 * the given name.
1010 	 * <p>
1011 	 * The editor descriptor is determined using a multi-step process. This
1012 	 * method will attempt to infer the content type of the file if
1013 	 * <code>inferContentType</code> is <code>true</code>.
1014 	 * </p>
1015 	 * <ol>
1016 	 * <li>The workbench editor registry is consulted to determine if an editor
1017 	 * extension has been registered for the file type. If so, an instance of
1018 	 * the editor extension is opened on the file. See
1019 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
1020 	 * <li>The operating system is consulted to determine if an in-place
1021 	 * component editor is available (e.g. OLE editor on Win32 platforms).</li>
1022 	 * <li>The operating system is consulted to determine if an external editor
1023 	 * is available.</li>
1024 	 * <li>The workbench editor registry is consulted to determine if the
1025 	 * default text editor is available.</li>
1026 	 * </ol>
1027 	 *
1028 	 * @param name
1029 	 *            the file name
1030 	 * @param inferContentType
1031 	 *            attempt to infer the content type from the file name if this
1032 	 *            is <code>true</code>
1033 	 * @return an editor descriptor, appropriate for opening the file
1034 	 * @throws PartInitException
1035 	 *             if no editor can be found
1036 	 * @since 3.1
1037 	 * @deprecated Since 3.12, use
1038 	 *             {@link IDE#getEditorDescriptor(String, boolean, boolean)}
1039 	 *             instead.
1040 	 */
1041 	@Deprecated
getEditorDescriptor(String name, boolean inferContentType)1042 	public static IEditorDescriptor getEditorDescriptor(String name, boolean inferContentType)
1043 			throws PartInitException {
1044 
1045 		if (name == null) {
1046 			throw new IllegalArgumentException();
1047 		}
1048 
1049 		IContentType contentType = inferContentType ? Platform
1050 				.getContentTypeManager().findContentTypeFor(name) : null;
1051 		IEditorRegistry editorReg = PlatformUI.getWorkbench()
1052 				.getEditorRegistry();
1053 
1054 		IEditorDescriptor defaultEditor = editorReg.getDefaultEditor(name, contentType);
1055 		defaultEditor = getEditorDescriptor(name, editorReg, defaultEditor);
1056 		return overrideDefaultEditorAssociation(name, contentType, defaultEditor);
1057 	}
1058 
1059 	/**
1060 	 * Returns an editor descriptor appropriate for opening a file resource with the
1061 	 * given name.
1062 	 * <p>
1063 	 * The editor descriptor is determined using a multi-step process. This method
1064 	 * will attempt to infer the content type of the file if
1065 	 * <code>inferContentType</code> is <code>true</code>.
1066 	 * </p>
1067 	 * <ol>
1068 	 * <li>The workbench editor registry is consulted to determine if an editor
1069 	 * extension has been registered for the file type. If so, an instance of the
1070 	 * editor extension is opened on the file. See
1071 	 * <code>IEditorRegistry.getDefaultEditor(String)</code>.</li>
1072 	 * <li>The preferred {@link IUnassociatedEditorStrategy} is consulted.</li>
1073 	 * <li>The {@link SystemEditorOrTextEditorStrategy} is consulted, whose behavior
1074 	 * is
1075 	 * <ol>
1076 	 * <li>The operating system is consulted to determine if an in-place component
1077 	 * editor is available (e.g. OLE editor on Win32 platforms).</li>
1078 	 * <li>The operating system is consulted to determine if an external editor is
1079 	 * available.</li>
1080 	 * <li>The workbench editor registry is consulted to determine if the default
1081 	 * text editor is available.</li>
1082 	 * </ol>
1083 	 * </li>
1084 	 * </ol>
1085 	 *
1086 	 * @param name             the file name
1087 	 * @param inferContentType attempt to infer the content type from the file name
1088 	 *                         if this is <code>true</code>
1089 	 * @param allowInteractive whether we allow user interactions.
1090 	 * @return an editor descriptor, appropriate for opening the file
1091 	 * @throws PartInitException          if no editor can be found
1092 	 * @throws OperationCanceledException in case descriptor lookup was canceled by
1093 	 *                                    the user
1094 	 * @since 3.12
1095 	 */
getEditorDescriptor(String name, boolean inferContentType, boolean allowInteractive)1096 	public static IEditorDescriptor getEditorDescriptor(String name, boolean inferContentType, boolean allowInteractive)
1097 			throws PartInitException, OperationCanceledException {
1098 
1099 		if (name == null) {
1100 			throw new IllegalArgumentException();
1101 		}
1102 
1103 		IContentType contentType = inferContentType ? Platform
1104 				.getContentTypeManager().findContentTypeFor(name) : null;
1105 		IEditorRegistry editorReg = PlatformUI.getWorkbench()
1106 				.getEditorRegistry();
1107 
1108 		IEditorDescriptor defaultEditor = editorReg.getDefaultEditor(name, contentType);
1109 		defaultEditor = getEditorDescriptor(name, editorReg, defaultEditor, allowInteractive);
1110 		return overrideDefaultEditorAssociation(name, contentType, defaultEditor);
1111 	}
1112 
1113 	/**
1114 	 * Get the editor descriptor for a given name using the editorDescriptor
1115 	 * passed in as a default as a starting point. It may delegate computation
1116 	 * to the active {@link IUnassociatedEditorStrategy}.
1117 	 *
1118 	 * @param name
1119 	 *            The name of the element to open.
1120 	 * @param editorReg
1121 	 *            The editor registry to do the lookups from.
1122 	 * @param defaultDescriptor
1123 	 *            IEditorDescriptor or <code>null</code>
1124 	 * @param allowInteractive
1125 	 *            Whether we ask selected {@link IUnassociatedEditorStrategy}, that
1126 	 *            can be interactive.
1127 	 * @return IEditorDescriptor
1128 	 * @throws PartInitException
1129 	 *             if no valid editor can be found
1130 	 * @throws OperationCanceledException
1131 	 *             in case descriptor lookup was canceled by the user
1132 	 *
1133 	 * @since 3.12
1134 	 */
getEditorDescriptor(String name, IEditorRegistry editorReg, IEditorDescriptor defaultDescriptor, boolean allowInteractive)1135 	private static IEditorDescriptor getEditorDescriptor(String name, IEditorRegistry editorReg,
1136 			IEditorDescriptor defaultDescriptor, boolean allowInteractive)
1137 					throws PartInitException, OperationCanceledException {
1138 
1139 		if (defaultDescriptor != null) {
1140 			return defaultDescriptor;
1141 		}
1142 
1143 		IUnassociatedEditorStrategy strategy = getUnassociatedEditorStrategy(allowInteractive);
1144 		IEditorDescriptor editorDesc;
1145 		try {
1146 			editorDesc = strategy.getEditorDescriptor(name, editorReg);
1147 		} catch (CoreException e) {
1148 			throw new PartInitException(IDEWorkbenchMessages.IDE_noFileEditorFound, e);
1149 		}
1150 
1151 		// if no valid editor found, bail out
1152 		if (editorDesc == null) {
1153 			throw new PartInitException(
1154 					IDEWorkbenchMessages.IDE_noFileEditorFound);
1155 		}
1156 
1157 		return editorDesc;
1158 	}
1159 
1160 	/**
1161 	 * Get the editor descriptor for a given name using the editorDescriptor
1162 	 * passed in as a default as a starting point.
1163 	 *
1164 	 * @param name
1165 	 *            The name of the element to open.
1166 	 * @param editorReg
1167 	 *            The editor registry to do the lookups from.
1168 	 * @param defaultDescriptor
1169 	 *            IEditorDescriptor or <code>null</code>
1170 	 * @return IEditorDescriptor
1171 	 * @throws PartInitException
1172 	 *             if no valid editor can be found
1173 	 *
1174 	 * @since 3.1
1175 	 * @deprecated Since 3.12, use {@link IDE#getEditorDescriptor(String, boolean, boolean)} instead
1176 	 */
1177 	@Deprecated
getEditorDescriptor(String name, IEditorRegistry editorReg, IEditorDescriptor defaultDescriptor)1178 	private static IEditorDescriptor getEditorDescriptor(String name,
1179 			IEditorRegistry editorReg, IEditorDescriptor defaultDescriptor)
1180 			throws PartInitException {
1181 
1182 		if (defaultDescriptor != null) {
1183 			return defaultDescriptor;
1184 		}
1185 
1186 		IEditorDescriptor editorDesc = defaultDescriptor;
1187 
1188 		// next check the OS for in-place editor (OLE on Win32)
1189 		if (editorReg.isSystemInPlaceEditorAvailable(name)) {
1190 			editorDesc = editorReg
1191 					.findEditor(IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID);
1192 		}
1193 
1194 		// next check with the OS for an external editor
1195 		if (editorDesc == null
1196 				&& editorReg.isSystemExternalEditorAvailable(name)) {
1197 			editorDesc = editorReg
1198 					.findEditor(IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID);
1199 		}
1200 
1201 		// next lookup the default text editor
1202 		if (editorDesc == null) {
1203 			editorDesc = editorReg
1204 					.findEditor(IDEWorkbenchPlugin.DEFAULT_TEXT_EDITOR_ID);
1205 		}
1206 
1207 		// if no valid editor found, bail out
1208 		if (editorDesc == null) {
1209 			throw new PartInitException(
1210 					IDEWorkbenchMessages.IDE_noFileEditorFound);
1211 		}
1212 
1213 		return editorDesc;
1214 	}
1215 
1216 	/**
1217 	 * @param allowInteractive
1218 	 *            Whether interactive strategies are considered
1219 	 * @return The strategy to use in order to open unknown file. Either as set
1220 	 *         by preference, or a {@link SystemEditorOrTextEditorStrategy} if
1221 	 *         none is explicitly configured. Never returns {@code null}.
1222 	 */
getUnassociatedEditorStrategy(boolean allowInteractive)1223 	private static IUnassociatedEditorStrategy getUnassociatedEditorStrategy(boolean allowInteractive) {
1224 		String preferedStrategy = IDEWorkbenchPlugin.getDefault().getPreferenceStore()
1225 				.getString(UNASSOCIATED_EDITOR_STRATEGY_PREFERENCE_KEY);
1226 		IUnassociatedEditorStrategy res = null;
1227 		UnassociatedEditorStrategyRegistry registry = IDEWorkbenchPlugin.getDefault()
1228 				.getUnassociatedEditorStrategyRegistry();
1229 		if (allowInteractive || !registry.isInteractive(preferedStrategy)) {
1230 			res = registry.getStrategy(preferedStrategy);
1231 		}
1232 		if (res == null) {
1233 			res = new SystemEditorOrTextEditorStrategy();
1234 		}
1235 		return res;
1236 	}
1237 
1238 	/**
1239 	 * Opens an editor on the file resource of the given marker.
1240 	 * <p>
1241 	 * If this page already has an editor open on the marker resource file that
1242 	 * editor is brought to front; otherwise, a new editor is opened.The cursor
1243 	 * and selection state of the editor are then updated from information
1244 	 * recorded in the marker.
1245 	 * </p>
1246 	 * <p>
1247 	 * If the marker contains an <code>EDITOR_ID_ATTR</code> attribute the
1248 	 * attribute value will be used to determine the editor type to be opened.
1249 	 * If not, the registered editor for the marker resource file will be used.
1250 	 * </p>
1251 	 *
1252 	 * @param page
1253 	 *            the workbench page to open the editor in
1254 	 * @param marker
1255 	 *            the marker to open
1256 	 * @return an open editor or <code>null</code> not possible
1257 	 * @exception PartInitException
1258 	 *                if the editor could not be initialized
1259 	 * @see #openEditor(org.eclipse.ui.IWorkbenchPage,
1260 	 *      org.eclipse.core.resources.IMarker, boolean)
1261 	 */
openEditor(IWorkbenchPage page, IMarker marker)1262 	public static IEditorPart openEditor(IWorkbenchPage page, IMarker marker)
1263 			throws PartInitException {
1264 		return openEditor(page, marker, true);
1265 	}
1266 
1267 	/**
1268 	 * Opens an editor on the file resource of the given marker.
1269 	 * <p>
1270 	 * If this page already has an editor open on the marker resource file that
1271 	 * editor is brought to front; otherwise, a new editor is opened. If
1272 	 * <code>activate == true</code> the editor will be activated. The cursor
1273 	 * and selection state of the editor are then updated from information
1274 	 * recorded in the marker.
1275 	 * </p>
1276 	 * <p>
1277 	 * If the marker contains an <code>EDITOR_ID_ATTR</code> attribute the
1278 	 * attribute value will be used to determine the editor type to be opened.
1279 	 * If not, the registered editor for the marker resource file will be used.
1280 	 * </p>
1281 	 *
1282 	 * @param page
1283 	 *            the workbench page to open the editor in
1284 	 * @param marker
1285 	 *            the marker to open
1286 	 * @param activate
1287 	 *            if <code>true</code> the editor will be activated
1288 	 * @return an open editor or <code>null</code> if not possible or if opening
1289 	 *         was canceled
1290 	 * @exception PartInitException
1291 	 *                if the editor could not be initialized
1292 	 */
openEditor(IWorkbenchPage page, IMarker marker, boolean activate)1293 	public static IEditorPart openEditor(IWorkbenchPage page, IMarker marker,
1294 			boolean activate) throws PartInitException {
1295 		// sanity checks
1296 		if (page == null || marker == null) {
1297 			throw new IllegalArgumentException();
1298 		}
1299 
1300 		// get the marker resource file
1301 		if (!(marker.getResource() instanceof IFile)) {
1302 			IDEWorkbenchPlugin
1303 					.log("Open editor on marker failed; marker resource not an IFile"); //$NON-NLS-1$
1304 			return null;
1305 		}
1306 		IFile file = (IFile) marker.getResource();
1307 
1308 		// get the preferred editor id from the marker
1309 		IEditorRegistry editorReg = PlatformUI.getWorkbench()
1310 				.getEditorRegistry();
1311 		IEditorDescriptor editorDesc = null;
1312 		try {
1313 			String editorID = (String) marker.getAttribute(EDITOR_ID_ATTR);
1314 			if (editorID != null) {
1315 				editorDesc = editorReg.findEditor(editorID);
1316 			}
1317 		} catch (CoreException e) {
1318 			// ignore this
1319 		}
1320 
1321 		// open the editor on the marker resource file
1322 		IEditorPart editor = null;
1323 		if (editorDesc == null) {
1324 			editor = openEditor(page, file, activate);
1325 		} else {
1326 			editor = page.openEditor(new FileEditorInput(file), editorDesc
1327 					.getId(), activate, IWorkbenchPage.MATCH_ID | IWorkbenchPage.MATCH_INPUT);
1328 		}
1329 
1330 		// get the editor to update its position based on the marker
1331 		if (editor != null) {
1332 			gotoMarker(editor, marker);
1333 		}
1334 
1335 		return editor;
1336 	}
1337 
1338 	/**
1339 	 * Opens an editor on the given IFileStore object.
1340 	 * <p>
1341 	 * Unlike the other <code>openEditor</code> methods, this one
1342 	 * can be used to open files that reside outside the workspace
1343 	 * resource set.
1344 	 * </p>
1345 	 * <p>
1346 	 * If the page already has an editor open on the target object then that
1347 	 * editor is brought to front; otherwise, a new editor is opened.
1348 	 * </p>
1349 	 *
1350 	 * @param page
1351 	 *            the page in which the editor will be opened
1352 	 * @param fileStore
1353 	 *            the IFileStore representing the file to open
1354 	 * @return an open editor or <code>null</code> if an external editor was
1355 	 *         opened or if opening was canceled
1356 	 * @exception PartInitException
1357 	 *                if the editor could not be initialized
1358 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
1359 	 * @since 3.3
1360 	 */
openEditorOnFileStore(IWorkbenchPage page, IFileStore fileStore)1361 	public static IEditorPart openEditorOnFileStore(IWorkbenchPage page, IFileStore fileStore) throws PartInitException {
1362 		//sanity checks
1363 		if (page == null) {
1364 			throw new IllegalArgumentException();
1365 		}
1366 
1367 		IEditorInput input = getEditorInput(fileStore);
1368 		String editorId;
1369 		try {
1370 			editorId = getEditorDescriptorForFileStore(fileStore, true).getId();
1371 		} catch (OperationCanceledException ex) {
1372 			return null;
1373 		}
1374 
1375 		// open the editor on the file
1376 		return page.openEditor(input, editorId);
1377 	}
1378 
1379 	/**
1380 	 * Opens an internal editor on the given IFileStore object.
1381 	 * <p>
1382 	 * Unlike the other <code>openEditor</code> methods, this one can be used to
1383 	 * open files that reside outside the workspace resource set.
1384 	 * </p>
1385 	 * <p>
1386 	 * If the page already has an editor open on the target object then that
1387 	 * editor is brought to front; otherwise, a new editor is opened.
1388 	 * </p>
1389 	 *
1390 	 * @param page
1391 	 *            the page in which the editor will be opened
1392 	 * @param fileStore
1393 	 *            the IFileStore representing the file to open
1394 	 * @return an open editor or <code>null</code> if an external editor was
1395 	 *         opened
1396 	 * @exception PartInitException
1397 	 *                if no internal editor can be found or if the editor could
1398 	 *                not be initialized
1399 	 * @see org.eclipse.ui.IWorkbenchPage#openEditor(IEditorInput, String)
1400 	 * @since 3.6
1401 	 */
openInternalEditorOnFileStore(IWorkbenchPage page, IFileStore fileStore)1402 	public static IEditorPart openInternalEditorOnFileStore(IWorkbenchPage page, IFileStore fileStore) throws PartInitException {
1403 		if (page == null)
1404 			throw new IllegalArgumentException();
1405 		if (fileStore == null)
1406 			throw new IllegalArgumentException();
1407 
1408 		IEditorInput input = getEditorInput(fileStore);
1409 		String name = fileStore.fetchInfo().getName();
1410 		if (name == null)
1411 			throw new IllegalArgumentException();
1412 
1413 		IContentType[] contentTypes = null;
1414 		InputStream is = null;
1415 		try {
1416 			is = fileStore.openInputStream(EFS.NONE, null);
1417 			contentTypes = Platform.getContentTypeManager().findContentTypesFor(is, name);
1418 		} catch (CoreException | IOException ex) {
1419 			// it's OK, ignore
1420 		} finally {
1421 			if (is != null) {
1422 				try {
1423 					is.close();
1424 				} catch (IOException e) {
1425 					// nothing good can be done here, ignore
1426 				}
1427 			}
1428 		}
1429 
1430 		IEditorRegistry editorReg = PlatformUI.getWorkbench().getEditorRegistry();
1431 		if (contentTypes != null) {
1432 			for (IContentType contentType : contentTypes) {
1433 				IEditorDescriptor editorDesc = editorReg.getDefaultEditor(name, contentType);
1434 				editorDesc = overrideDefaultEditorAssociation(input, contentType, editorDesc);
1435 				if ((editorDesc != null) && (editorDesc.isInternal()))
1436 					return page.openEditor(input, editorDesc.getId());
1437 			}
1438 		}
1439 
1440 		// no content types are available, use file name associations
1441 		IEditorDescriptor[] editors = editorReg.getEditors(name);
1442 		if (editors != null) {
1443 			editors = overrideEditorAssociations(input, null, editors);
1444 			for (IEditorDescriptor editor : editors) {
1445 				if ((editor != null) && (editor.isInternal()))
1446 					return page.openEditor(input, editor.getId());
1447 			}
1448 		}
1449 
1450 		// fallback to the default text editor
1451 		IEditorDescriptor textEditor = editorReg.findEditor(IDEWorkbenchPlugin.DEFAULT_TEXT_EDITOR_ID);
1452 		if (textEditor == null)
1453 			throw new PartInitException(IDEWorkbenchMessages.IDE_noFileEditorFound);
1454 		return page.openEditor(input, textEditor.getId());
1455 	}
1456 
1457 	/**
1458 	 * Save all dirty editors in the workbench whose editor input is a child
1459 	 * resource of one of the <code>IResource</code>'s provided. Opens a
1460 	 * dialog to prompt the user if <code>confirm</code> is true. Return true
1461 	 * if successful. Return false if the user has canceled the command.
1462 	 *
1463 	 * @since 3.0
1464 	 *
1465 	 * @param resourceRoots the resource roots under which editor input should
1466 	 *            be saved, other will be left dirty
1467 	 * @param confirm <code>true</code> to ask the user before saving unsaved
1468 	 *            changes (recommended), and <code>false</code> to save
1469 	 *            unsaved changes without asking
1470 	 * @return <code>true</code> if the command succeeded, and
1471 	 *         <code>false</code> if the operation was canceled by the user or
1472 	 *         an error occurred while saving
1473 	 */
saveAllEditors(final IResource[] resourceRoots, final boolean confirm)1474 	public static boolean saveAllEditors(final IResource[] resourceRoots,
1475 			final boolean confirm) {
1476 
1477 		if (resourceRoots.length == 0) {
1478 			return true;
1479 		}
1480 
1481 		final boolean[] result = new boolean[] { true };
1482 		SafeRunner.run(new SafeRunnable(IDEWorkbenchMessages.ErrorOnSaveAll) {
1483 			@Override
1484 			public void run() {
1485 				IWorkbenchWindow w = PlatformUI.getWorkbench()
1486 						.getActiveWorkbenchWindow();
1487 				if (w == null) {
1488 					IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
1489 							.getWorkbenchWindows();
1490 					if (windows.length > 0)
1491 						w = windows[0];
1492 				}
1493 				if (w != null) {
1494 					result[0] = PlatformUI.getWorkbench().saveAll(w, w,
1495 							new ResourceSaveableFilter(resourceRoots), confirm);
1496 				}
1497 			}
1498 		});
1499 		return result[0];
1500 	}
1501 
1502 	/**
1503 	 * Sets the default editor id for a given file. This value will be used to
1504 	 * determine the default editor descriptor for the file in future calls to
1505 	 * <code>getDefaultEditor(IFile)</code>.
1506 	 *
1507 	 * @param file
1508 	 *            the file
1509 	 * @param editorID
1510 	 *            the editor id
1511 	 */
setDefaultEditor(IFile file, String editorID)1512 	public static void setDefaultEditor(IFile file, String editorID) {
1513 		try {
1514 			file.setPersistentProperty(EDITOR_KEY, editorID);
1515 		} catch (CoreException e) {
1516 			// do nothing
1517 		}
1518 	}
1519 
1520 	/**
1521 	 * Returns the default editor for a given file. This method will attempt to
1522 	 * resolve the editor based on content-type bindings as well as traditional
1523 	 * name/extension bindings.
1524 	 * <p>
1525 	 * A default editor id may be registered for a specific file using
1526 	 * <code>setDefaultEditor</code>. If the given file has a registered
1527 	 * default editor id the default editor will derived from it. If not, the
1528 	 * default editor is determined by taking the file name for the file and
1529 	 * obtaining the default editor for that name.
1530 	 * </p>
1531 	 *
1532 	 * @param file
1533 	 *            the file
1534 	 * @return the descriptor of the default editor, or <code>null</code> if
1535 	 *         not found
1536 	 */
getDefaultEditor(IFile file)1537 	public static IEditorDescriptor getDefaultEditor(IFile file) {
1538 		return getDefaultEditor(file, true);
1539 	}
1540 
1541 	/**
1542 	 * Returns the default editor for a given file. This method will attempt to
1543 	 * resolve the editor based on content-type bindings as well as traditional
1544 	 * name/extension bindings if <code>determineContentType</code> is
1545 	 * <code>true</code>.
1546 	 * <p>
1547 	 * A default editor id may be registered for a specific file using
1548 	 * <code>setDefaultEditor</code>. If the given file has a registered
1549 	 * default editor id the default editor will derived from it. If not, the
1550 	 * default editor is determined by taking the file name for the file and
1551 	 * obtaining the default editor for that name.
1552 	 * </p>
1553 	 *
1554 	 * @param file
1555 	 *            the file
1556 	 * @param determineContentType
1557 	 *            determine the content type for the given file
1558 	 * @return the descriptor of the default editor, or <code>null</code> if
1559 	 *         not found
1560 	 * @since 3.1
1561 	 */
getDefaultEditor(IFile file, boolean determineContentType)1562 	public static IEditorDescriptor getDefaultEditor(IFile file,
1563 			boolean determineContentType) {
1564 		// Try file specific editor.
1565 		IEditorRegistry editorReg = PlatformUI.getWorkbench()
1566 				.getEditorRegistry();
1567 
1568 		IContentType contentType = null;
1569 		if (determineContentType) {
1570 			contentType = getContentType(file);
1571 		}
1572 
1573 		try {
1574 			String editorID = file.getPersistentProperty(EDITOR_KEY);
1575 			if (editorID != null) {
1576 				IEditorDescriptor desc = editorReg.findEditor(editorID);
1577 				if (desc != null) {
1578 					return overrideDefaultEditorAssociation(new FileEditorInput(file), contentType, desc);
1579 				}
1580 			}
1581 		} catch (CoreException e) {
1582 			// do nothing
1583 		}
1584 
1585 		// Try lookup with filename
1586 		IEditorDescriptor desc = editorReg.getDefaultEditor(file.getName(), contentType);
1587 		return overrideDefaultEditorAssociation(new FileEditorInput(file), contentType, desc);
1588 	}
1589 
1590 	/**
1591 	 * Extracts and returns the <code>IResource</code>s in the given
1592 	 * selection or the resource objects they adapts to.
1593 	 *
1594 	 * @param originalSelection
1595 	 *            the original selection, possibly empty
1596 	 * @return list of resources (element type: <code>IResource</code>),
1597 	 *         possibly empty
1598 	 */
computeSelectedResources(IStructuredSelection originalSelection)1599 	public static List<IResource> computeSelectedResources(IStructuredSelection originalSelection) {
1600 		List<IResource> resources = null;
1601 		for (Object next : originalSelection) {
1602 			IResource resource = Adapters.adapt(next, IResource.class);
1603 			if (resource != null) {
1604 				if (resources == null) {
1605 					// lazy init to avoid creating empty lists
1606 					// assume selection contains mostly resources most times
1607 					resources = new ArrayList<>(originalSelection.size());
1608 				}
1609 				resources.add(resource);
1610 			}
1611 		}
1612 		if (resources == null) {
1613 			return Collections.emptyList();
1614 		}
1615 		return resources;
1616 
1617 	}
1618 
1619 	/**
1620 	 * Return the content type for the given file.
1621 	 *
1622 	 * @param file
1623 	 *            the file to test
1624 	 * @return the content type, or <code>null</code> if it cannot be
1625 	 *         determined.
1626 	 * @since 3.1
1627 	 */
getContentType(IFile file)1628 	public static IContentType getContentType(IFile file) {
1629 		try {
1630 			UIStats.start(UIStats.CONTENT_TYPE_LOOKUP, file.getName());
1631 			IContentDescription contentDescription = file
1632 					.getContentDescription();
1633 			if (contentDescription == null) {
1634 				return null;
1635 			}
1636 			return contentDescription.getContentType();
1637 		} catch (CoreException e) {
1638 			if (e.getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
1639 				// Determine the content type from the file name.
1640 				return Platform.getContentTypeManager()
1641 							.findContentTypeFor(file.getName());
1642 			}
1643 			return null;
1644 		} finally {
1645 			UIStats.end(UIStats.CONTENT_TYPE_LOOKUP, file, file.getName());
1646 		}
1647 	}
1648 
1649 	/**
1650 	 * Guess at the content type of the given file based on the filename.
1651 	 *
1652 	 * @param file
1653 	 *            the file to test
1654 	 * @return the content type, or <code>null</code> if it cannot be
1655 	 *         determined.
1656 	 * @since 3.2
1657 	 */
guessContentType(IFile file)1658 	public static IContentType guessContentType(IFile file) {
1659 		String fileName = file.getName();
1660 		try {
1661 			UIStats.start(UIStats.CONTENT_TYPE_LOOKUP, fileName);
1662 			IContentTypeMatcher matcher = file.getProject()
1663 					.getContentTypeMatcher();
1664 			return matcher.findContentTypeFor(fileName);
1665 		} catch (CoreException e) {
1666 			return null;
1667 		} finally {
1668 			UIStats.end(UIStats.CONTENT_TYPE_LOOKUP, file, fileName);
1669 		}
1670 	}
1671 
1672 	/**
1673 	 * Prompt the user to inform them of the possible side effects of an
1674 	 * operation on resources. Do not prompt for side effects from ignored model
1675 	 * providers. A model provider can be ignored if it is the client calling
1676 	 * this API. Any message from the provided model provider id or any model
1677 	 * providers it extends will be ignored.
1678 	 *
1679 	 * @param shell
1680 	 *            the shell to parent the prompt dialog
1681 	 * @param title
1682 	 *            the title of the dialog
1683 	 * @param message
1684 	 *            the message for the dialog
1685 	 * @param delta
1686 	 *            a delta built using an
1687 	 *            {@link IResourceChangeDescriptionFactory}
1688 	 * @param ignoreModelProviderIds
1689 	 *            model providers to be ignored
1690 	 * @param syncExec
1691 	 *            prompt in a sync exec (required when called from a non-UI
1692 	 *            thread)
1693 	 * @return whether the user chose to continue
1694 	 * @since 3.2
1695 	 */
promptToConfirm(final Shell shell, final String title, String message, IResourceDelta delta, String[] ignoreModelProviderIds, boolean syncExec)1696 	public static boolean promptToConfirm(final Shell shell,
1697 			final String title, String message, IResourceDelta delta,
1698 			String[] ignoreModelProviderIds, boolean syncExec) {
1699 		IStatus status = ResourceChangeValidator.getValidator().validateChange(
1700 				delta, null);
1701 		if (status.isOK()) {
1702 			return true;
1703 		}
1704 		final IStatus displayStatus;
1705 		if (status.isMultiStatus()) {
1706 			List<IStatus> result = new ArrayList<>();
1707 			IStatus[] children = status.getChildren();
1708 			for (IStatus child : children) {
1709 				if (!isIgnoredStatus(child, ignoreModelProviderIds)) {
1710 					result.add(child);
1711 				}
1712 			}
1713 			if (result.isEmpty()) {
1714 				return true;
1715 			}
1716 			if (result.size() == 1) {
1717 				displayStatus = result.get(0);
1718 			} else {
1719 				displayStatus = new MultiStatus(status.getPlugin(), status.getCode(),
1720 						result.toArray(new IStatus[result.size()]), status.getMessage(), status.getException());
1721 			}
1722 		} else {
1723 			if (isIgnoredStatus(status, ignoreModelProviderIds)) {
1724 				return true;
1725 			}
1726 			displayStatus = status;
1727 		}
1728 
1729 		if (message == null) {
1730 			message = IDEWorkbenchMessages.IDE_sideEffectWarning;
1731 		}
1732 		final String dialogMessage = NLS.bind(
1733 				IDEWorkbenchMessages.IDE_areYouSure, message);
1734 
1735 		final boolean[] result = new boolean[] { false };
1736 		Runnable runnable = () -> {
1737 			ErrorDialog dialog = new ErrorDialog(shell, title,
1738 					dialogMessage, displayStatus, IStatus.ERROR
1739 							| IStatus.WARNING | IStatus.INFO) {
1740 				@Override
1741 				protected void createButtonsForButtonBar(Composite parent) {
1742 					createButton(parent, IDialogConstants.YES_ID,
1743 							IDialogConstants.YES_LABEL, false);
1744 					createButton(parent, IDialogConstants.NO_ID,
1745 							IDialogConstants.NO_LABEL, true);
1746 					createDetailsButton(parent);
1747 				}
1748 
1749 				@Override
1750 				protected void buttonPressed(int id) {
1751 					if (id == IDialogConstants.YES_ID) {
1752 						super.buttonPressed(IDialogConstants.OK_ID);
1753 					} else if (id == IDialogConstants.NO_ID) {
1754 						super.buttonPressed(IDialogConstants.CANCEL_ID);
1755 					}
1756 					super.buttonPressed(id);
1757 				}
1758 				@Override
1759 				protected int getShellStyle() {
1760 					return super.getShellStyle() | SWT.SHEET;
1761 				}
1762 			};
1763 			int code = dialog.open();
1764 			result[0] = code == 0;
1765 		};
1766 		if (syncExec) {
1767 			shell.getDisplay().syncExec(runnable);
1768 		} else {
1769 			runnable.run();
1770 		}
1771 		return result[0];
1772 	}
1773 
1774 	/**
1775 	 * Register workbench adapters programmatically. This is necessary to enable
1776 	 * certain types of content in the explorers.
1777 	 * <p>
1778 	 * <b>Note:</b> this method should only be called once, in your
1779 	 * application's WorkbenchAdvisor#initialize(IWorkbenchConfigurer) method.
1780 	 * </p>
1781 	 *
1782 	 * @since 3.5
1783 	 */
registerAdapters()1784 	public static void registerAdapters() {
1785 		IAdapterManager manager = Platform.getAdapterManager();
1786 		IAdapterFactory factory = new WorkbenchAdapterFactory();
1787 		manager.registerAdapters(factory, IWorkspace.class);
1788 		manager.registerAdapters(factory, IWorkspaceRoot.class);
1789 		manager.registerAdapters(factory, IProject.class);
1790 		manager.registerAdapters(factory, IFolder.class);
1791 		manager.registerAdapters(factory, IFile.class);
1792 		manager.registerAdapters(factory, IMarker.class);
1793 
1794 		// properties adapters
1795 		IAdapterFactory paFactory = new StandardPropertiesAdapterFactory();
1796 		manager.registerAdapters(paFactory, IWorkspace.class);
1797 		manager.registerAdapters(paFactory, IWorkspaceRoot.class);
1798 		manager.registerAdapters(paFactory, IProject.class);
1799 		manager.registerAdapters(paFactory, IFolder.class);
1800 		manager.registerAdapters(paFactory, IFile.class);
1801 		manager.registerAdapters(paFactory, IMarker.class);
1802 		manager.registerAdapters(paFactory, IEditorPart.class);
1803 	}
1804 
isIgnoredStatus(IStatus status, String[] ignoreModelProviderIds)1805 	private static boolean isIgnoredStatus(IStatus status,
1806 			String[] ignoreModelProviderIds) {
1807 		if (ignoreModelProviderIds == null) {
1808 			return false;
1809 		}
1810 		if (status instanceof ModelStatus) {
1811 			ModelStatus ms = (ModelStatus) status;
1812 			for (String id : ignoreModelProviderIds) {
1813 				if (ms.getModelProviderId().equals(id)) {
1814 					return true;
1815 				}
1816 				IModelProviderDescriptor desc = ModelProvider
1817 						.getModelProviderDescriptor(id);
1818 				String[] extended = desc.getExtendedModels();
1819 				if (isIgnoredStatus(status, extended)) {
1820 					return true;
1821 				}
1822 			}
1823 		}
1824 		return false;
1825 	}
1826 
1827 	/**
1828 	 * Opens editors on given file resources.
1829 	 * <p>
1830 	 * If the page already has an editor open on the target object then that
1831 	 * editor is brought to front; otherwise, a new editor is opened. The editor created
1832 	 * for the first input will be activated.
1833 	 * </p>
1834 	 * @param page the page in which the editor will be opened
1835 	 * @param inputs the inputs for the editors
1836 	 * @return references to the editors opened; the corresponding editors might not be materialized
1837 	 * @exception MultiPartInitException if at least one of the editors could not be initialized
1838 	 * @since 3.5
1839 	 */
openEditors(IWorkbenchPage page, IFile[] inputs)1840 	public static IEditorReference[] openEditors(IWorkbenchPage page, IFile[] inputs) throws MultiPartInitException {
1841 		if ((page == null) || (inputs == null))
1842 			throw new IllegalArgumentException();
1843 
1844 		String[] editorDescriptions = new String[inputs.length];
1845 		IEditorInput[] editorInputs = new IEditorInput[inputs.length];
1846 		for(int i = 0 ; i < inputs.length; i++) {
1847 			editorInputs[i] = new FileEditorInput(inputs[i]);
1848 			try {
1849 				editorDescriptions[i] = getEditorDescriptor(inputs[i], true, true).getId();
1850 			} catch (PartInitException e) {
1851 				PartInitException[] exceptions = new PartInitException[inputs.length];
1852 				exceptions[i] = e;
1853 				throw new MultiPartInitException(new IWorkbenchPartReference[inputs.length], exceptions);
1854 			}
1855 		}
1856 		return page.openEditors(editorInputs, editorDescriptions, IWorkbenchPage.MATCH_INPUT);
1857 	}
1858 
getEditorAssociationOverrides()1859 	private static IEditorAssociationOverride[] getEditorAssociationOverrides() {
1860 		if (editorAssociationOverrides == null) {
1861 			EditorAssociationOverrideDescriptor[] descriptors = EditorAssociationOverrideDescriptor.getContributedEditorAssociationOverrides();
1862 			List<IEditorAssociationOverride> overrides = new ArrayList<>(descriptors.length);
1863 			for (EditorAssociationOverrideDescriptor descriptor : descriptors) {
1864 				try {
1865 					IEditorAssociationOverride override = descriptor.createOverride();
1866 					overrides.add(override);
1867 				} catch (CoreException e) {
1868 					IDEWorkbenchPlugin
1869 							.log("Error while creating IEditorAssociationOverride from: " + descriptor.getId(), e); //$NON-NLS-1$
1870 				}
1871 			}
1872 			editorAssociationOverrides = overrides.toArray(new IEditorAssociationOverride[overrides.size()]);
1873 		}
1874 		return editorAssociationOverrides;
1875 	}
1876 }
1877