1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 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  *******************************************************************************/
14 package org.eclipse.team.internal.ui;
15 
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.MissingResourceException;
27 import java.util.ResourceBundle;
28 import java.util.Set;
29 
30 import org.eclipse.compare.CompareConfiguration;
31 import org.eclipse.compare.CompareEditorInput;
32 import org.eclipse.compare.structuremergeviewer.IDiffContainer;
33 import org.eclipse.compare.structuremergeviewer.IDiffElement;
34 import org.eclipse.core.resources.IFile;
35 import org.eclipse.core.resources.IResource;
36 import org.eclipse.core.resources.IStorage;
37 import org.eclipse.core.resources.mapping.ModelProvider;
38 import org.eclipse.core.resources.mapping.ResourceMapping;
39 import org.eclipse.core.resources.mapping.ResourceMappingContext;
40 import org.eclipse.core.resources.mapping.ResourceTraversal;
41 import org.eclipse.core.runtime.Adapters;
42 import org.eclipse.core.runtime.CoreException;
43 import org.eclipse.core.runtime.IAdaptable;
44 import org.eclipse.core.runtime.IProgressMonitor;
45 import org.eclipse.core.runtime.IStatus;
46 import org.eclipse.core.runtime.NullProgressMonitor;
47 import org.eclipse.core.runtime.Platform;
48 import org.eclipse.core.runtime.Status;
49 import org.eclipse.core.runtime.content.IContentType;
50 import org.eclipse.core.runtime.jobs.Job;
51 import org.eclipse.jface.action.IAction;
52 import org.eclipse.jface.dialogs.ErrorDialog;
53 import org.eclipse.jface.dialogs.MessageDialog;
54 import org.eclipse.jface.operation.IRunnableWithProgress;
55 import org.eclipse.jface.preference.IPreferenceStore;
56 import org.eclipse.jface.resource.ImageDescriptor;
57 import org.eclipse.jface.util.OpenStrategy;
58 import org.eclipse.jface.viewers.IStructuredSelection;
59 import org.eclipse.jface.viewers.StructuredViewer;
60 import org.eclipse.osgi.util.NLS;
61 import org.eclipse.swt.custom.BusyIndicator;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.Display;
64 import org.eclipse.swt.widgets.Shell;
65 import org.eclipse.team.core.RepositoryProvider;
66 import org.eclipse.team.core.TeamException;
67 import org.eclipse.team.core.diff.IDiff;
68 import org.eclipse.team.core.diff.IThreeWayDiff;
69 import org.eclipse.team.core.history.IFileHistoryProvider;
70 import org.eclipse.team.core.history.IFileRevision;
71 import org.eclipse.team.core.mapping.IResourceDiff;
72 import org.eclipse.team.core.mapping.ISynchronizationScope;
73 import org.eclipse.team.core.synchronize.FastSyncInfoFilter;
74 import org.eclipse.team.core.synchronize.SyncInfo;
75 import org.eclipse.team.core.variants.IResourceVariant;
76 import org.eclipse.team.internal.core.mapping.CompoundResourceTraversal;
77 import org.eclipse.team.internal.ui.history.FileRevisionEditorInput;
78 import org.eclipse.team.internal.ui.synchronize.SyncInfoModelElement;
79 import org.eclipse.team.ui.TeamImages;
80 import org.eclipse.team.ui.TeamUI;
81 import org.eclipse.team.ui.mapping.ISynchronizationCompareAdapter;
82 import org.eclipse.team.ui.synchronize.ISynchronizeManager;
83 import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
84 import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
85 import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
86 import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput;
87 import org.eclipse.ui.IContributorResourceAdapter;
88 import org.eclipse.ui.IEditorDescriptor;
89 import org.eclipse.ui.IEditorPart;
90 import org.eclipse.ui.IEditorReference;
91 import org.eclipse.ui.IEditorRegistry;
92 import org.eclipse.ui.IReusableEditor;
93 import org.eclipse.ui.IWorkbench;
94 import org.eclipse.ui.IWorkbenchPage;
95 import org.eclipse.ui.IWorkbenchSite;
96 import org.eclipse.ui.IWorkbenchWindow;
97 import org.eclipse.ui.PartInitException;
98 import org.eclipse.ui.PlatformUI;
99 import org.eclipse.ui.ide.IContributorResourceAdapter2;
100 import org.eclipse.ui.ide.IDE;
101 import org.eclipse.ui.internal.ErrorEditorPart;
102 import org.eclipse.ui.internal.registry.EditorDescriptor;
103 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
104 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
105 import org.osgi.framework.Bundle;
106 
107 public class Utils {
108 
109 	/**
110 	 * Constant used to indicate that tests are being run. This field
111 	 * should be the same as the corresponding field on
112 	 * org.eclipse.compare.internal.Utilities
113 	 */
114 	public static boolean RUNNING_TESTS = false;
115 
116 	/**
117 	 * Constant used while testing the indicate that changes should be flushed
118 	 * when the compare input changes and a viewer is dirty. This field
119 	 * should be the same as the corresponding field on
120 	 * org.eclipse.compare.internal.Utilities
121 	 */
122 	public static boolean TESTING_FLUSH_ON_COMPARE_INPUT_CHANGE = false;
123 
124 	/**
125 	 * The SortOperation takes a collection of objects and returns a sorted
126 	 * collection of these objects. Concrete instances of this class provide
127 	 * the criteria for the sorting of the objects based on the type of the
128 	 * objects.
129 	 */
130 	static public abstract class Sorter {
131 
132 		/**
133 		 * Returns true is elementTwo is 'greater than' elementOne This is the
134 		 * 'ordering' method of the sort operation. Each subclass overrides this
135 		 * method with the particular implementation of the 'greater than'
136 		 * concept for the objects being sorted.
137 		 * @param elementOne element 1
138 		 * @param elementTwo element 2
139 		 * @return whether element 2 is greater that element 1
140 		 */
compare(Object elementOne, Object elementTwo)141 		public abstract boolean compare(Object elementOne, Object elementTwo);
142 
143 		/**
144 		 * Sort the objects in sorted collection and return that collection.
145 		 */
quickSort(Object[] sortedCollection, int left, int right)146 		private Object[] quickSort(Object[] sortedCollection, int left, int right) {
147 			int originalLeft = left;
148 			int originalRight = right;
149 			Object mid = sortedCollection[(left + right) >>> 1];
150 			do {
151 				while (compare(sortedCollection[left], mid))
152 					left++;
153 				while (compare(mid, sortedCollection[right]))
154 					right--;
155 				if (left <= right) {
156 					Object tmp = sortedCollection[left];
157 					sortedCollection[left] = sortedCollection[right];
158 					sortedCollection[right] = tmp;
159 					left++;
160 					right--;
161 				}
162 			} while (left <= right);
163 			if (originalLeft < right)
164 				sortedCollection = quickSort(sortedCollection, originalLeft, right);
165 			if (left < originalRight)
166 				sortedCollection = quickSort(sortedCollection, left, originalRight);
167 			return sortedCollection;
168 		}
169 
170 		/**
171 		 * Return a new sorted collection from this unsorted collection. Sort
172 		 * using quick sort.
173 		 * @param unSortedCollection the original collection
174 		 * @return the sorted collection
175 		 */
sort(Object[] unSortedCollection)176 		public Object[] sort(Object[] unSortedCollection) {
177 			int size = unSortedCollection.length;
178 			Object[] sortedCollection = new Object[size];
179 			//copy the array so can return a new sorted collection
180 			System.arraycopy(unSortedCollection, 0, sortedCollection, 0, size);
181 			if (size > 1)
182 				quickSort(sortedCollection, 0, size - 1);
183 			return sortedCollection;
184 		}
185 	}
186 
187 	public static final Comparator<IResource> resourceComparator = new Comparator<IResource>() {
188 		@Override
189 		public boolean equals(Object obj) {
190 			return false;
191 		}
192 		@Override
193 		public int compare(IResource o1, IResource o2) {
194 				return o1.getFullPath().toString().compareTo(o2.getFullPath().toString());
195 		}
196 	};
197 
198 	/**
199 	 * Shows the given errors to the user.
200 	 * @param shell
201 	 *            the shell to open the error dialog in
202 	 * @param exception
203 	 *            the exception containing the error
204 	 * @param title
205 	 *            the title of the error dialog
206 	 * @param message
207 	 *            the message for the error dialog
208 	 */
handleError(Shell shell, Exception exception, String title, String message)209 	public static void handleError(Shell shell, Exception exception, String title, String message) {
210 		IStatus status = null;
211 		boolean log = false;
212 		boolean dialog = false;
213 		Throwable t = exception;
214 		if (exception instanceof TeamException) {
215 			status = ((TeamException) exception).getStatus();
216 			log = false;
217 			dialog = true;
218 		} else if (exception instanceof InvocationTargetException) {
219 			t = ((InvocationTargetException) exception).getTargetException();
220 			if (t instanceof TeamException) {
221 				status = ((TeamException) t).getStatus();
222 				log = false;
223 				dialog = true;
224 			} else if (t instanceof CoreException) {
225 				status = ((CoreException) t).getStatus();
226 				log = true;
227 				dialog = true;
228 			} else if (t instanceof InterruptedException) {
229 				return;
230 			} else {
231 				status = new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, TeamUIMessages.TeamAction_internal, t);
232 				log = true;
233 				dialog = true;
234 			}
235 		}
236 		if (status == null)
237 			return;
238 		if (!status.isOK()) {
239 			IStatus toShow = status;
240 			if (status.isMultiStatus()) {
241 				IStatus[] children = status.getChildren();
242 				if (children.length == 1) {
243 					toShow = children[0];
244 				}
245 			}
246 			if (title == null) {
247 				title = status.getMessage();
248 			}
249 			if (message == null) {
250 				message = status.getMessage();
251 			}
252 			if (dialog && shell != null) {
253 				ErrorDialog.openError(shell, title, message, toShow);
254 			}
255 			if (log || shell == null) {
256 				TeamUIPlugin.log(toShow.getSeverity(), message, t);
257 			}
258 		}
259 	}
260 
runWithProgress(Shell parent, boolean cancelable, final IRunnableWithProgress runnable)261 	public static void runWithProgress(Shell parent, boolean cancelable, final IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException {
262 		boolean createdShell = false;
263 		try {
264 			if (parent == null || parent.isDisposed()) {
265 				Display display = Display.getCurrent();
266 				if (display == null) {
267 					// cannot provide progress (not in UI thread)
268 					runnable.run(new NullProgressMonitor());
269 					return;
270 				}
271 				// get the active shell or a suitable top-level shell
272 				parent = display.getActiveShell();
273 				if (parent == null) {
274 					parent = new Shell(display);
275 					createdShell = true;
276 				}
277 			}
278 			// pop up progress dialog after a short delay
279 			final Exception[] holder = new Exception[1];
280 			BusyIndicator.showWhile(parent.getDisplay(), () -> {
281 				try {
282 					runnable.run(new NullProgressMonitor());
283 				} catch (InvocationTargetException e1) {
284 					holder[0] = e1;
285 				} catch (InterruptedException e2) {
286 					holder[0] = e2;
287 				}
288 			});
289 			if (holder[0] != null) {
290 				if (holder[0] instanceof InvocationTargetException) {
291 					throw (InvocationTargetException) holder[0];
292 				} else {
293 					throw (InterruptedException) holder[0];
294 				}
295 			}
296 			//new TimeoutProgressMonitorDialog(parent, TIMEOUT).run(true
297 			// /*fork*/, cancelable, runnable);
298 		} finally {
299 			if (createdShell)
300 				parent.dispose();
301 		}
302 	}
303 
getShell(IWorkbenchSite site)304 	public static Shell getShell(IWorkbenchSite site) {
305 		return getShell(site, false);
306 	}
307 
getShell(IWorkbenchSite site, boolean syncIfNecessary)308 	public static Shell getShell(IWorkbenchSite site, boolean syncIfNecessary) {
309 		if(site != null) {
310 			Shell shell = site.getShell();
311 			if (!shell.isDisposed())
312 				return shell;
313 		}
314 		IWorkbench workbench = PlatformUI.getWorkbench();
315 		if (workbench != null) {
316 			IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
317 			if (window != null) {
318 				return window.getShell();
319 			}
320 		}
321 		// Fallback to using the display
322 		Display display = Display.getCurrent();
323 		if (display == null) {
324 			display = Display.getDefault();
325 			if (display.isDisposed()) return null;
326 			if (syncIfNecessary) {
327 				final Shell[] result = new Shell[] { null };
328 				Runnable r = () -> result[0] = new Shell(Display.getDefault());
329 				display.syncExec(r);
330 				return result[0];
331 			}
332 		}
333 		if (display.isDisposed()) return null;
334 		return new Shell(display);
335 	}
336 	/*
337 	 * This method is only for use by the Target Management feature (see bug
338 	 * 16509). @param t
339 	 */
handle(final Throwable exception)340 	public static void handle(final Throwable exception) {
341 		TeamUIPlugin.getStandardDisplay().asyncExec(() -> {
342 			IStatus error = null;
343 			Throwable t = exception;
344 			if (t instanceof InvocationTargetException) {
345 				t = ((InvocationTargetException) t).getTargetException();
346 			}
347 			if (t instanceof CoreException) {
348 				error = ((CoreException) t).getStatus();
349 			} else if (t instanceof TeamException) {
350 				error = ((TeamException) t).getStatus();
351 			} else {
352 				error = new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, TeamUIMessages.simpleInternal, t);
353 			}
354 			Shell shell = new Shell(Display.getDefault());
355 			if (error.getSeverity() == IStatus.INFO) {
356 				MessageDialog.openInformation(shell, TeamUIMessages.information, error.getMessage());
357 			} else {
358 				ErrorDialog.openError(shell, TeamUIMessages.exception, null, error);
359 			}
360 			shell.dispose();
361 			// Let's log non-team exceptions
362 			if (!(t instanceof TeamException)) {
363 				TeamUIPlugin.log(error.getSeverity(), error.getMessage(), t);
364 			}
365 		});
366 	}
367 
findShell()368 	public static Shell findShell() {
369 		Display display = TeamUIPlugin.getStandardDisplay();
370 		Shell activeShell = display.getActiveShell();
371 		if (activeShell != null)
372 			return activeShell;
373 		// worst case, just create our own.
374 		return new Shell(display);
375 	}
376 
initAction(IAction a, String prefix)377 	public static void initAction(IAction a, String prefix) {
378 		Utils.initAction(a, prefix, Policy.getActionBundle());
379 	}
380 
initAction(IAction a, String prefix, ResourceBundle bundle)381 	public static void initAction(IAction a, String prefix, ResourceBundle bundle) {
382 		Utils.initAction(a, prefix, bundle, null);
383 	}
384 
updateLabels(SyncInfo sync, CompareConfiguration config, IProgressMonitor monitor)385 	public static void updateLabels(SyncInfo sync, CompareConfiguration config, IProgressMonitor monitor) {
386 		final IResourceVariant remote = sync.getRemote();
387 		final IResourceVariant base = sync.getBase();
388 		String baseAuthor = null;
389 		String remoteAuthor = null;
390 		String localAuthor = null;
391 		String localContentId = sync.getLocalContentIdentifier();
392 		String remoteContentId= remote != null ? remote.getContentIdentifier() : null;
393 		String baseContentId= base != null ? base.getContentIdentifier() : null;
394 		if (isShowAuthor()) {
395 			baseAuthor = getAuthor(base, monitor);
396 			if (baseContentId != null && baseContentId.equals(remoteContentId))
397 				remoteAuthor= baseAuthor;
398 			else
399 				remoteAuthor= getAuthor(remote, monitor);
400 
401 			if (localContentId != null) {
402 				if (localContentId.equals(baseContentId))
403 					localAuthor= baseAuthor;
404 				else if (localContentId.equals(remoteAuthor))
405 					localAuthor= remoteAuthor;
406 				else
407 					localAuthor= sync.getLocalAuthor(monitor);
408 			}
409 		}
410 		if (localContentId != null) {
411 			if (localAuthor != null) {
412 				config.setLeftLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_localLabelAuthorExists, new String[] { localContentId, localAuthor }));
413 			} else {
414 				config.setLeftLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_localLabelExists, new String[] { localContentId }));
415 			}
416 		} else {
417 			config.setLeftLabel(TeamUIMessages.SyncInfoCompareInput_localLabel);
418 		}
419 		if (remote != null) {
420 			if (remoteAuthor != null) {
421 				config.setRightLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_remoteLabelAuthorExists, new String[] { remoteContentId, remoteAuthor }));
422 			} else {
423 				config.setRightLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_remoteLabelExists, new String[] { remoteContentId }));
424 			}
425 		} else {
426 			config.setRightLabel(TeamUIMessages.SyncInfoCompareInput_remoteLabel);
427 		}
428 		if (base != null) {
429 			if (baseAuthor != null) {
430 				config.setAncestorLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_baseLabelAuthorExists, new String[] { baseContentId, baseAuthor }));
431 			} else {
432 				config.setAncestorLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_baseLabelExists, new String[] { baseContentId }));
433 			}
434 		} else {
435 			config.setAncestorLabel(TeamUIMessages.SyncInfoCompareInput_baseLabel);
436 		}
437 	}
438 
439 	/**
440 	 * DO NOT REMOVE, used in a product.
441 	 *
442 	 * @deprecated As of 3.5, replaced by
443 	 *             {@link #updateLabels(SyncInfo, CompareConfiguration, IProgressMonitor)}
444 	 */
445 	@SuppressWarnings("javadoc")
446 	@Deprecated
updateLabels(SyncInfo sync, CompareConfiguration config)447 	public static void updateLabels(SyncInfo sync, CompareConfiguration config) {
448 		updateLabels(sync, config, null);
449 	}
450 
isShowAuthor()451 	public static boolean isShowAuthor() {
452 		IPreferenceStore store = TeamUIPlugin.getPlugin().getPreferenceStore();
453 		return store.getBoolean(IPreferenceIds.SHOW_AUTHOR_IN_COMPARE_EDITOR);
454 	}
455 
getAuthor(IResourceVariant variant, IProgressMonitor monitor)456 	private static String getAuthor(IResourceVariant variant,
457 			IProgressMonitor monitor) {
458 		String author = null;
459 		if (variant instanceof IAdaptable) {
460 			IAdaptable adaptable = (IAdaptable) variant;
461 			IFileRevision revision = adaptable.getAdapter(IFileRevision.class);
462 			if (revision == null)
463 				return null;
464 			try {
465 				IFileRevision complete = revision.withAllProperties(monitor);
466 				if (complete != null) {
467 					author = complete.getAuthor();
468 				}
469 			} catch (CoreException e) {
470 				TeamUIPlugin.log(e);
471 			}
472 		}
473 		return author;
474 	}
475 
getLocalContentId(IDiff diff)476 	public static String getLocalContentId(IDiff diff) {
477 		if (diff instanceof IThreeWayDiff) {
478 			IThreeWayDiff twd = (IThreeWayDiff) diff;
479 			diff = twd.getLocalChange();
480 			if (diff == null)
481 				diff = twd.getRemoteChange();
482 		}
483 		if (diff instanceof IResourceDiff) {
484 			IResourceDiff rd = (IResourceDiff) diff;
485 			IResource resource = rd.getResource();
486 			IFileHistoryProvider provider = getHistoryProvider(resource);
487 			if (provider != null) {
488 				IFileRevision revision = provider.getWorkspaceFileRevision(resource);
489 				if (revision != null)
490 					return revision.getContentIdentifier();
491 			}
492 		}
493 		return null;
494 	}
495 
getHistoryProvider(IResource resource)496 	public static IFileHistoryProvider getHistoryProvider(IResource resource) {
497 		RepositoryProvider rp = RepositoryProvider.getProvider(resource.getProject());
498 		if (rp != null)
499 			return rp.getFileHistoryProvider();
500 		return null;
501 	}
502 
getBase(IDiff diff)503 	public static IFileRevision getBase(IDiff diff) {
504 		if (diff instanceof IThreeWayDiff) {
505 			IThreeWayDiff twd = (IThreeWayDiff) diff;
506 			IDiff remoteChange = twd.getRemoteChange();
507 			if (remoteChange instanceof IResourceDiff) {
508 				IResourceDiff rd = (IResourceDiff) remoteChange;
509 				return rd.getBeforeState();
510 			}
511 			IDiff localChange = twd.getLocalChange();
512 			if (localChange instanceof IResourceDiff) {
513 				IResourceDiff ld = (IResourceDiff) localChange;
514 				return ld.getBeforeState();
515 			}
516 		}
517 		return null;
518 	}
519 
getRemote(IDiff diff)520 	public static IFileRevision getRemote(IDiff diff) {
521 		if (diff instanceof IResourceDiff) {
522 			IResourceDiff rd = (IResourceDiff) diff;
523 			return rd.getAfterState();
524 		}
525 		if (diff instanceof IThreeWayDiff) {
526 			IThreeWayDiff twd = (IThreeWayDiff) diff;
527 			IDiff remoteChange = twd.getRemoteChange();
528 			if (remoteChange instanceof IResourceDiff) {
529 				IResourceDiff rd = (IResourceDiff) remoteChange;
530 				return rd.getAfterState();
531 			}
532 			IDiff localChange = twd.getLocalChange();
533 			if (localChange instanceof IResourceDiff) {
534 				IResourceDiff ld = (IResourceDiff) localChange;
535 				return ld.getBeforeState();
536 			}
537 		}
538 		return null;
539 	}
540 
541 	/**
542 	 * Initialize the given Action from a ResourceBundle.
543 	 * @param a the action
544 	 * @param prefix the bundle key prefix
545 	 * @param bundle the bundle
546 	 * @param bindings additional input to the action label
547 	 */
initAction(IAction a, String prefix, ResourceBundle bundle, String[] bindings)548 	public static void initAction(IAction a, String prefix, ResourceBundle bundle, String[] bindings) {
549 		String labelKey = "label"; //$NON-NLS-1$
550 		String tooltipKey = "tooltip"; //$NON-NLS-1$
551 		String imageKey = "image"; //$NON-NLS-1$
552 		String descriptionKey = "description"; //$NON-NLS-1$
553 		if (prefix != null && prefix.length() > 0) {
554 			labelKey = prefix + labelKey;
555 			tooltipKey = prefix + tooltipKey;
556 			imageKey = prefix + imageKey;
557 			descriptionKey = prefix + descriptionKey;
558 		}
559 		String s = null;
560 		if(bindings != null) {
561 			s = NLS.bind(getString(labelKey, bundle), bindings);
562 		} else {
563 			s = getString(labelKey, bundle);
564 		}
565 		if (s != null)
566 			a.setText(s);
567 		s = getString(tooltipKey, bundle);
568 		if (s != null)
569 			a.setToolTipText(s);
570 		s = getString(descriptionKey, bundle);
571 		if (s != null)
572 			a.setDescription(s);
573 		String relPath = getString(imageKey, bundle);
574 		if (relPath != null && !relPath.equals(imageKey) && relPath.trim().length() > 0) {
575 			String dPath;
576 			String ePath;
577 			if (relPath.contains("/")) { //$NON-NLS-1$
578 				String path = relPath.substring(1);
579 				dPath = 'd' + path;
580 				ePath = 'e' + path;
581 			} else {
582 				dPath = "dlcl16/" + relPath; //$NON-NLS-1$
583 				ePath = "elcl16/" + relPath; //$NON-NLS-1$
584 			}
585 			ImageDescriptor id = TeamImages.getImageDescriptor(dPath);
586 			if (id != null)
587 				a.setDisabledImageDescriptor(id);
588 			id = TeamUIPlugin.getImageDescriptor(ePath);
589 			if (id != null)
590 				a.setImageDescriptor(id);
591 		}
592 	}
593 
getString(String key, ResourceBundle b)594 	public static String getString(String key, ResourceBundle b) {
595 		try {
596 			return b.getString(key);
597 		} catch (MissingResourceException e) {
598 			return key;
599 		} catch (NullPointerException e) {
600 			return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
601 		}
602 	}
603 
modeToString(int mode)604 	public static String modeToString(int mode) {
605 		switch (mode) {
606 		case ISynchronizePageConfiguration.INCOMING_MODE :
607 			return TeamUIMessages.Utils_22;
608 		case ISynchronizePageConfiguration.OUTGOING_MODE :
609 			return TeamUIMessages.Utils_23;
610 		case ISynchronizePageConfiguration.BOTH_MODE :
611 			return TeamUIMessages.Utils_24;
612 		case ISynchronizePageConfiguration.CONFLICTING_MODE :
613 			return TeamUIMessages.Utils_25;
614 		default:
615 			return TeamUIMessages.Utils_26;
616 		}
617 	}
618 
619 	/**
620 	 * Returns the list of resources contained in the given elements.
621 	 * @param elements
622 	 * @return the list of resources contained in the given elements.
623 	 */
getResources(Object[] elements, List<Object> nonResources, boolean isContributed, boolean includeMappingResources)624 	private static IResource[] getResources(Object[] elements, List<Object> nonResources,
625 			boolean isContributed, boolean includeMappingResources) {
626 		List<IResource> resources = new ArrayList<>();
627 		for (Object element : elements) {
628 			boolean isResource = false;
629 			if (element instanceof IResource) {
630 				resources.add((IResource) element);
631 				isResource = true;
632 			} else if (element instanceof ISynchronizeModelElement){
633 				IResource resource = ((ISynchronizeModelElement) element).getResource();
634 				if (resource != null) {
635 					resources.add(resource);
636 					isResource = true;
637 				}
638 			} else if (element instanceof ResourceMapping) {
639 				if (includeMappingResources) {
640 					isResource = true;
641 					getResources((ResourceMapping)element, resources);
642 				}
643 			} else if (element != null) {
644 				Object adapted;
645 				if (isContributed) {
646 					adapted = getResource(element);
647 				} else {
648 					adapted = Adapters.adapt(element, IResource.class);
649 				}
650 				if (adapted instanceof IResource) {
651 					IResource resource = (IResource) adapted;
652 					isResource = true;
653 					if (resource.getType() != IResource.ROOT) {
654 						resources.add(resource);
655 					}
656 				} else {
657 					if (isContributed) {
658 						adapted = getResourceMapping(element);
659 					} else {
660 						adapted = Adapters.adapt(element, ResourceMapping.class);
661 					}
662 					if (adapted instanceof ResourceMapping && includeMappingResources) {
663 						isResource = true;
664 						getResources((ResourceMapping) adapted, resources);
665 					}
666 				}
667 			}
668 			if (!isResource) {
669 				if (nonResources != null)
670 					nonResources.add(element);
671 			}
672 		}
673 		return resources.toArray(new IResource[resources.size()]);
674 	}
675 
getResources(ResourceMapping element, List<IResource> resources)676 	private static void getResources(ResourceMapping element, List<IResource> resources) {
677 		try {
678 			ResourceTraversal[] traversals = element.getTraversals(ResourceMappingContext.LOCAL_CONTEXT, null);
679 			for (ResourceTraversal traversal : traversals) {
680 				IResource[] resourceArray = traversal.getResources();
681 				Collections.addAll(resources, resourceArray);
682 			}
683 		} catch (CoreException e) {
684 			TeamUIPlugin.log(new Status(IStatus.ERROR, TeamUIPlugin.ID, 0, "Error traversing resource mapping", e)); //$NON-NLS-1$
685 		}
686 	}
687 
getNonResources(Object[] elements)688 	public static Object[] getNonResources(Object[] elements) {
689 		List<Object> nonResources = new ArrayList<>();
690 		getResources(elements, nonResources, false, false);
691 		return nonResources.toArray();
692 	}
693 
getResources(Object[] element)694 	public static IResource[] getResources(Object[] element) {
695 		return getResources(element, null, false /* isContributed */, false /* includeMappingResources */);
696 	}
697 
getContributedResources(Object[] elements)698 	public static IResource[] getContributedResources(Object[] elements) {
699 		return getResources(elements, null, true /* isContributed */, true /* isIncudeMappings */);
700 	}
701 
702 	/**
703 	 * Return whether any sync nodes in the given selection or their
704 	 * descendants match the given filter.
705 	 * @param selection a selection
706 	 * @param filter a sync info filter
707 	 * @return whether any sync nodes in the given selection or their
708 	 * descendants match the given filter
709 	 */
hasMatchingDescendant(IStructuredSelection selection, FastSyncInfoFilter filter)710 	public static boolean hasMatchingDescendant(IStructuredSelection selection, FastSyncInfoFilter filter) {
711 		for (Iterator iter = selection.iterator(); iter.hasNext();) {
712 			Object o = iter.next();
713 			if (o instanceof ISynchronizeModelElement) {
714 				if (hasMatchingDescendant((ISynchronizeModelElement)o, filter)) {
715 					return true;
716 				}
717 			}
718 		}
719 		return false;
720 	}
721 
hasMatchingDescendant(ISynchronizeModelElement element, FastSyncInfoFilter filter)722 	private static boolean hasMatchingDescendant(ISynchronizeModelElement element, FastSyncInfoFilter filter) {
723 		if (element.getKind() != SyncInfo.IN_SYNC && element instanceof SyncInfoModelElement) {
724 			SyncInfo info = ((SyncInfoModelElement) element).getSyncInfo();
725 			if (info != null && filter.select(info)) {
726 				return true;
727 			}
728 		}
729 		IDiffElement[] children = element.getChildren();
730 		for (IDiffElement child : children) {
731 			if (child instanceof ISynchronizeModelElement) {
732 				if (hasMatchingDescendant((ISynchronizeModelElement)child, filter)) {
733 					return true;
734 				}
735 			}
736 		}
737 		return false;
738 	}
739 
740 	/**
741 	 * This method returns all out-of-sync SyncInfos that are in the current
742 	 * selection.
743 	 * @param selected the selected objects
744 	 *
745 	 * @return the list of selected sync infos
746 	 */
getDiffNodes(Object[] selected)747 	public static IDiffElement[] getDiffNodes(Object[] selected) {
748 		Set<IDiffElement> result = new HashSet<>();
749 		for (Object object : selected) {
750 			if(object instanceof IDiffElement) {
751 				collectAllNodes((IDiffElement)object, result);
752 			}
753 		}
754 		return result.toArray(new IDiffElement[result.size()]);
755 	}
756 
collectAllNodes(IDiffElement element, Set<IDiffElement> nodes)757 	private static void collectAllNodes(IDiffElement element, Set<IDiffElement> nodes) {
758 		if (element.getKind() != SyncInfo.IN_SYNC) {
759 			nodes.add(element);
760 		}
761 		if(element instanceof IDiffContainer) {
762 			IDiffElement[] children = ((IDiffContainer)element).getChildren();
763 			for (IDiffElement c : children) {
764 				collectAllNodes(c, nodes);
765 			}
766 		}
767 	}
768 
schedule(Job job, IWorkbenchSite site)769 	public static void schedule(Job job, IWorkbenchSite site) {
770 		if (site != null) {
771 			IWorkbenchSiteProgressService siteProgress = site.getAdapter(IWorkbenchSiteProgressService.class);
772 			if (siteProgress != null) {
773 				siteProgress.schedule(job, 0, true /* use half-busy cursor */);
774 				return;
775 			}
776 		}
777 		job.schedule();
778 	}
779 
readBytes(InputStream in)780 	public static byte[] readBytes(InputStream in) {
781 		ByteArrayOutputStream bos= new ByteArrayOutputStream();
782 		try {
783 			while (true) {
784 				int c= in.read();
785 				if (c == -1)
786 					break;
787 				bos.write(c);
788 			}
789 
790 		} catch (IOException ex) {
791 			return null;
792 
793 		} finally {
794 			if (in != null) {
795 				try {
796 					in.close();
797 				} catch (IOException x) {
798 					// silently ignored
799 				}
800 			}
801 			try {
802 				bos.close();
803 			} catch (IOException x) {
804 				// silently ignored
805 			}
806 		}
807 		return bos.toByteArray();
808 	}
809 
equalObject(Object o1, Object o2)810 	public static boolean equalObject(Object o1, Object o2) {
811 		if (o1 == null && o2 == null) return true;
812 		if (o1 == null || o2 == null) return false;
813 		return o1.equals(o2);
814 	}
815 
getKey(String id, String secondaryId)816 	public static String getKey(String id, String secondaryId) {
817 		return secondaryId == null ? id : id + '/' + secondaryId;
818 	}
819 
convertSelection(IResource[] resources)820 	public static String convertSelection(IResource[] resources) {
821 		StringBuilder  buffer = new StringBuilder();
822 		for (int i = 0; i < resources.length; i++) {
823 			IResource resource = resources[i];
824 			if(i > 0) buffer.append(", "); //$NON-NLS-1$
825 			buffer.append(resource.getFullPath());
826 		}
827 		return buffer.toString();
828 	}
829 
830 	/**
831 	 * Shorten the given text <code>t</code> so that its length
832 	 * doesn't exceed the given width. This implementation
833 	 * replaces characters in the center of the original string with an
834 	 * ellipsis ("...").
835 	 * @param maxWidth the maximum length for the text
836 	 * @param textValue the text to be shortened
837 	 * @return the shortened text
838 	 */
shortenText(int maxWidth, String textValue)839 	public static String shortenText(int maxWidth, String textValue) {
840 		int length = textValue.length();
841 		if (length < maxWidth)
842 			return textValue;
843 		String ellipsis = "..."; //$NON-NLS-1$
844 		int subStrLen = (maxWidth - ellipsis.length()) / 2;
845 		int addtl = (maxWidth - ellipsis.length()) % 2;
846 
847 		StringBuilder sb = new StringBuilder(maxWidth);
848 		sb.append(textValue.substring(0, subStrLen));
849 		sb.append(ellipsis);
850 		sb.append(textValue.substring(length - subStrLen - addtl));
851 		return sb.toString();
852 	}
853 
getTypeName(ISynchronizeParticipant participant)854 	public static String getTypeName(ISynchronizeParticipant participant) {
855 		ISynchronizeManager manager = TeamUI.getSynchronizeManager();
856 		return manager.getParticipantDescriptor(participant.getId()).getName();
857 	}
858 
859 	/**
860 	 * The viewer will only be updated if the viewer is not null, the control is not disposed, and
861 	 * this code is being run from the UI thread.
862 	 * @param viewer the viewer to be updated
863 	 * @return whether it is safe to update the viewer
864 	 */
canUpdateViewer(StructuredViewer viewer)865 	public static boolean canUpdateViewer(StructuredViewer viewer) {
866 		if(viewer == null || viewer.getControl().isDisposed()) return false;
867 		Display display = viewer.getControl().getDisplay();
868 		if (display == null) return false;
869 		if (display.getThread() != Thread.currentThread ()) return false;
870 		return true;
871 	}
872 
asyncExec(final Runnable r, StructuredViewer v)873 	public static void asyncExec(final Runnable r, StructuredViewer v) {
874 		if(v == null) return;
875 		final Control ctrl = v.getControl();
876 		if (ctrl != null && !ctrl.isDisposed()) {
877 			ctrl.getDisplay().asyncExec(() -> {
878 				if (!ctrl.isDisposed()) {
879 					BusyIndicator.showWhile(ctrl.getDisplay(), r);
880 				}
881 			});
882 		}
883 	}
884 
syncExec(final Runnable r, StructuredViewer v)885 	public static void syncExec(final Runnable r, StructuredViewer v) {
886 		if(v == null) return;
887 		final Control ctrl = v.getControl();
888 		syncExec(r, ctrl);
889 	}
890 
syncExec(final Runnable r, final Control ctrl)891 	public static void syncExec(final Runnable r, final Control ctrl) {
892 		if (ctrl != null && !ctrl.isDisposed()) {
893 			ctrl.getDisplay().syncExec(() -> {
894 				if (!ctrl.isDisposed()) {
895 					BusyIndicator.showWhile(ctrl.getDisplay(), r);
896 				}
897 			});
898 		}
899 	}
900 
asyncExec(final Runnable r, final Control ctrl)901 	public static void asyncExec(final Runnable r, final Control ctrl) {
902 		if (ctrl != null && !ctrl.isDisposed()) {
903 			ctrl.getDisplay().asyncExec(() -> {
904 				if (!ctrl.isDisposed()) {
905 					BusyIndicator.showWhile(ctrl.getDisplay(), r);
906 				}
907 			});
908 		}
909 	}
910 
getSyncInfo(ISynchronizeModelElement node)911 	public static SyncInfo getSyncInfo(ISynchronizeModelElement node) {
912 		if (node instanceof IAdaptable) {
913 			return ((IAdaptable) node).getAdapter(SyncInfo.class);
914 		}
915 		return null;
916 	}
917 
getCompareAdapter(Object element)918 	public static ISynchronizationCompareAdapter getCompareAdapter(Object element) {
919 		ModelProvider provider = getModelProvider(element);
920 		if (provider != null) {
921 			Object o = provider.getAdapter(ISynchronizationCompareAdapter.class);
922 			if (o instanceof ISynchronizationCompareAdapter) {
923 				return (ISynchronizationCompareAdapter) o;
924 			}
925 		}
926 		return null;
927 	}
928 
getModelProvider(Object o)929 	public static ModelProvider getModelProvider(Object o) {
930 		if (o instanceof ModelProvider) {
931 			return (ModelProvider) o;
932 		}
933 		ResourceMapping mapping = getResourceMapping(o);
934 		if (mapping != null)
935 			return mapping.getModelProvider();
936 		return null;
937 	}
938 
getResource(Object o)939 	public static IResource getResource(Object o) {
940 		IResource resource = null;
941 		if (o instanceof IResource) {
942 			resource = (IResource) o;
943 		} else if (o instanceof IAdaptable) {
944 			IAdaptable adaptable = (IAdaptable) o;
945 			resource = adaptable.getAdapter(IResource.class);
946 			if (resource == null) {
947 				IContributorResourceAdapter adapter = adaptable.getAdapter(IContributorResourceAdapter.class);
948 				if (adapter != null)
949 					resource = adapter.getAdaptedResource(adaptable);
950 			}
951 		}
952 		return resource;
953 	}
954 
955 
getResourceMapping(Object o)956 	public static ResourceMapping getResourceMapping(Object o) {
957 		if (o instanceof ResourceMapping) {
958 			return (ResourceMapping) o;
959 		}
960 		if (o instanceof IAdaptable) {
961 			IAdaptable adaptable = (IAdaptable) o;
962 			Object adapted = adaptable.getAdapter(ResourceMapping.class);
963 			if (adapted instanceof ResourceMapping) {
964 				return(ResourceMapping) adapted;
965 			}
966 			adapted = adaptable.getAdapter(IContributorResourceAdapter.class);
967 			if (adapted instanceof IContributorResourceAdapter2) {
968 				IContributorResourceAdapter2 cra = (IContributorResourceAdapter2) adapted;
969 				return cra.getAdaptedResourceMapping(adaptable);
970 			}
971 		} else {
972 			Object adapted = Platform.getAdapterManager().getAdapter(o, ResourceMapping.class);
973 			if (adapted instanceof ResourceMapping) {
974 				return(ResourceMapping) adapted;
975 			}
976 		}
977 		return null;
978 	}
979 
getResourceMappings(Object[] objects)980 	public static ResourceMapping[] getResourceMappings(Object[] objects) {
981 		List<ResourceMapping> result = new ArrayList<>();
982 		for (Object object : objects) {
983 			ResourceMapping mapping = getResourceMapping(object);
984 			if (mapping != null)
985 				result.add(mapping);
986 		}
987 		return result.toArray(new ResourceMapping[result.size()]);
988 	}
989 
getLabel(ResourceMapping mapping)990 	public static String getLabel(ResourceMapping mapping) {
991 		ModelProvider provider = mapping.getModelProvider();
992 		ISynchronizationCompareAdapter adapter = getCompareAdapter(provider);
993 		if (adapter == null)
994 			return ""; //$NON-NLS-1$
995 		String pathString = adapter.getPathString(mapping);
996 		if (pathString == null || pathString.length() == 0)
997 			return adapter.getName(mapping);
998 		return pathString;
999 	}
1000 
getLabel(ModelProvider provider)1001 	public static String getLabel(ModelProvider provider) {
1002 		ResourceMapping mapping = Utils.getResourceMapping(provider);
1003 		if (mapping != null) {
1004 			String base = Utils.getLabel(mapping);
1005 			if (base != null && base.length() > 0)
1006 				return base;
1007 		}
1008 		return provider.getDescriptor().getLabel();
1009 	}
1010 
getScopeDescription(ISynchronizationScope scope)1011 	public static String getScopeDescription(ISynchronizationScope scope) {
1012 		ResourceMapping[] mappings = scope.getInputMappings();
1013 		if (mappings.length == 1) {
1014 			String label = getLabel(mappings[0]);
1015 			if (label == null)
1016 				return TeamUIMessages.Utils_19;
1017 			else
1018 				return label;
1019 		}
1020 		String desc = convertSelection(mappings);
1021 		if (desc.length() > 0)
1022 			return shortenText(30, desc);
1023 		return NLS.bind(TeamUIMessages.Utils_18, Integer.valueOf(mappings.length));
1024 	}
1025 
convertSelection(ResourceMapping[] mappings)1026 	public static String convertSelection(ResourceMapping[] mappings) {
1027 		StringBuilder  buffer = new StringBuilder();
1028 		boolean hadOne = false;
1029 		for (ResourceMapping resourceMapping : mappings) {
1030 			String label = getLabel(resourceMapping);
1031 			if (label != null) {
1032 				if(hadOne) buffer.append(", "); //$NON-NLS-1$
1033 				hadOne = true;
1034 				buffer.append(label);
1035 			}
1036 		}
1037 		return buffer.toString();
1038 	}
1039 
getTraversals(Object[] elements)1040 	public static ResourceTraversal[] getTraversals(Object[] elements) throws CoreException {
1041 		CompoundResourceTraversal traversal = new CompoundResourceTraversal();
1042 		for (Object object : elements) {
1043 			ResourceMapping mapping = getResourceMapping(object);
1044 			if (mapping != null) {
1045 				traversal.addTraversals(mapping.getTraversals(ResourceMappingContext.LOCAL_CONTEXT, null));
1046 			}
1047 		}
1048 		return traversal.asTraversals();
1049 	}
1050 
1051 	/**
1052 	 * Return whether the editor associated with a descriptor is a text editor
1053 	 * (i.e. an instance of AbstractDecoratedTextEditor).
1054 	 * See bug 99568 for a request to move the createEditor method to IEditorDescriptor.
1055 	 * @param descriptor
1056 	 * @return whether the editor associated with a descriptor is a text editor
1057 	 * @throws CoreException
1058 	 */
isTextEditor(IEditorDescriptor descriptor)1059 	public static boolean isTextEditor(IEditorDescriptor descriptor) throws CoreException {
1060 		if (!(descriptor instanceof EditorDescriptor))
1061 			return false;
1062 
1063 		EditorDescriptor desc = (EditorDescriptor) descriptor;
1064 		String className = desc.getClassName();
1065 		String contributor = desc.getPluginId();
1066 
1067 		if (className == null || contributor == null)
1068 			return false;
1069 
1070 		try {
1071 			Bundle bundle= Platform.getBundle(contributor);
1072 			if (bundle != null) {
1073 				Class clazz= bundle.loadClass(className);
1074 				return AbstractDecoratedTextEditor.class.isAssignableFrom(clazz);
1075 			}
1076 		} catch (ClassNotFoundException e) {
1077 			// fallback and create editor
1078 		}
1079 
1080 		IEditorPart editor= desc.createEditor();
1081 		editor.dispose();
1082 		return editor instanceof AbstractDecoratedTextEditor;
1083 	}
1084 
openEditor(IWorkbenchPage page, IFileRevision revision, IProgressMonitor monitor)1085 	public static IEditorPart openEditor(IWorkbenchPage page, IFileRevision revision, IProgressMonitor monitor) throws CoreException {
1086 		IStorage file = revision.getStorage(monitor);
1087 		if (file instanceof IFile) {
1088 			//if this is the current workspace file, open it
1089 			return IDE.openEditor(page, (IFile)file, OpenStrategy.activateOnOpen());
1090 		} else {
1091 			FileRevisionEditorInput fileRevEditorInput = FileRevisionEditorInput.createEditorInputFor(revision, monitor);
1092 			IEditorPart	part = openEditor(page, fileRevEditorInput);
1093 			return part;
1094 		}
1095 	}
1096 
openEditor(IWorkbenchPage page, FileRevisionEditorInput editorInput)1097 	public static IEditorPart openEditor(IWorkbenchPage page,
1098 			FileRevisionEditorInput editorInput) throws PartInitException {
1099 		String id = getEditorId(editorInput);
1100 		return openEditor(page, editorInput, id);
1101 	}
1102 
openEditor(IWorkbenchPage page, FileRevisionEditorInput editorInput, String editorId)1103 	public static IEditorPart openEditor(IWorkbenchPage page,
1104 			FileRevisionEditorInput editorInput, String editorId)
1105 			throws PartInitException {
1106 		try {
1107 			IEditorPart part = page.openEditor(editorInput, editorId,
1108 					OpenStrategy.activateOnOpen());
1109 			// See bug 90582 for the reasons behind this discouraged access
1110 			if (part instanceof ErrorEditorPart) {
1111 				page.closeEditor(part, false);
1112 				part = null;
1113 			}
1114 			if (part == null) {
1115 				throw new PartInitException(NLS.bind(TeamUIMessages.Utils_17,
1116 						editorId));
1117 			}
1118 			return part;
1119 		} catch (PartInitException e) {
1120 			if (editorId.equals("org.eclipse.ui.DefaultTextEditor")) { //$NON-NLS-1$
1121 				throw e;
1122 			} else {
1123 				return page.openEditor(editorInput,
1124 						"org.eclipse.ui.DefaultTextEditor"); //$NON-NLS-1$
1125 			}
1126 		}
1127 	}
1128 
getEditors(IFileRevision revision)1129 	public static IEditorDescriptor[] getEditors(IFileRevision revision) {
1130 		String name= revision.getName();
1131 		IEditorRegistry registry = PlatformUI.getWorkbench()
1132 				.getEditorRegistry();
1133 		// so far only the revision name is used to find editors
1134 		IEditorDescriptor[] editorDescs= registry.getEditors(name/* , getContentType(revision) */);
1135 		return IDE.overrideEditorAssociations(name, null, editorDescs);
1136 	}
1137 
getDefaultEditor(IFileRevision revision)1138 	public static IEditorDescriptor getDefaultEditor(IFileRevision revision) {
1139 		String name= revision.getName();
1140 		// so far only the revision name is used to find the default editor
1141 		try {
1142 			return IDE.getEditorDescriptor(name);
1143 		} catch (PartInitException e) {
1144 			// Fallback to old way of getting the editor
1145 			IEditorRegistry registry= PlatformUI.getWorkbench().getEditorRegistry();
1146 			return registry.getDefaultEditor(name);
1147 		}
1148 	}
1149 
getEditorId(FileRevisionEditorInput editorInput)1150 	private static String getEditorId(FileRevisionEditorInput editorInput) {
1151 		String id= getEditorId(editorInput, getContentType(editorInput));
1152 		return id;
1153 	}
1154 
getContentType(FileRevisionEditorInput editorInput)1155 	private static IContentType getContentType(FileRevisionEditorInput editorInput) {
1156 		IContentType type = null;
1157 		try {
1158 			InputStream contents = editorInput.getStorage().getContents();
1159 			try {
1160 				type = getContentType(editorInput.getFileRevision().getName(), contents);
1161 			} finally {
1162 				try {
1163 					contents.close();
1164 				} catch (IOException e) {
1165 					// ignore
1166 				}
1167 			}
1168 		} catch (CoreException e) {
1169 			TeamUIPlugin.log(IStatus.ERROR, NLS.bind("An error occurred reading the contents of file {0}", new String[] { editorInput.getName() }), e); //$NON-NLS-1$
1170 		}
1171 		return type;
1172 	}
1173 
getContentType(String fileName, InputStream contents)1174 	private static IContentType getContentType(String fileName, InputStream contents) {
1175 		IContentType type = null;
1176 		if (contents != null) {
1177 			try {
1178 				type = Platform.getContentTypeManager().findContentTypeFor(contents, fileName);
1179 			} catch (IOException e) {
1180 				TeamUIPlugin.log(IStatus.ERROR, NLS.bind("An error occurred reading the contents of file {0}", fileName), e); //$NON-NLS-1$
1181 			}
1182 		}
1183 		if (type == null) {
1184 			type = Platform.getContentTypeManager().findContentTypeFor(fileName);
1185 		}
1186 		return type;
1187 	}
1188 
getEditorId(FileRevisionEditorInput editorInput, IContentType type)1189 	private static String getEditorId(FileRevisionEditorInput editorInput, IContentType type) {
1190 		String fileName= editorInput.getFileRevision().getName();
1191 		IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
1192 		IEditorDescriptor descriptor = registry.getDefaultEditor(fileName, type);
1193 		IDE.overrideDefaultEditorAssociation(editorInput, type, descriptor);
1194 		String id;
1195 		if (descriptor == null || descriptor.isOpenExternal()) {
1196 			id = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$
1197 		} else {
1198 			id = descriptor.getId();
1199 		}
1200 		return id;
1201 	}
1202 
1203 	/**
1204 	 * Returns an editor that can be re-used. An open compare editor that has
1205 	 * un-saved changes cannot be re-used.
1206 	 *
1207 	 * @param input
1208 	 *            the input being opened
1209 	 * @param page
1210 	 * @param editorInputClasses
1211 	 * @return an EditorPart or <code>null</code> if none can be found
1212 	 */
findReusableCompareEditor( CompareEditorInput input, IWorkbenchPage page, Class[] editorInputClasses)1213 	public static IEditorPart findReusableCompareEditor(
1214 			CompareEditorInput input, IWorkbenchPage page,
1215 			Class[] editorInputClasses) {
1216 		IEditorReference[] editorRefs = page.getEditorReferences();
1217 		// first loop looking for an editor with the same input
1218 		for (IEditorReference editorRef : editorRefs) {
1219 			IEditorPart part = editorRef.getEditor(false);
1220 			if (part != null && part instanceof IReusableEditor) {
1221 				for (Class editorInputClasse : editorInputClasses) {
1222 					// check if the editor input type
1223 					// complies with the types given by the caller
1224 					if (editorInputClasse.isInstance(part.getEditorInput()) && part.getEditorInput().equals(input)) {
1225 						return part;
1226 					}
1227 				}
1228 			}
1229 		}
1230 		// if none found and "Reuse open compare editors" preference is on use
1231 		// a non-dirty editor
1232 		if (TeamUIPlugin.getPlugin().getPreferenceStore()
1233 				.getBoolean(IPreferenceIds.REUSE_OPEN_COMPARE_EDITOR)) {
1234 			for (IEditorReference editorRef : editorRefs) {
1235 				IEditorPart part = editorRef.getEditor(false);
1236 				if (part != null
1237 						&& (part.getEditorInput() instanceof SaveableCompareEditorInput)
1238 						&& part instanceof IReusableEditor && !part.isDirty()) {
1239 					return part;
1240 				}
1241 			}
1242 		}
1243 
1244 		// no re-usable editor found
1245 		return null;
1246 	}
1247 
1248 }
1249