blob: 2019b9ea2a83ce82bf08f03422de5becef9387cb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ui;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.structuremergeviewer.IDiffContainer;
import org.eclipse.compare.structuremergeviewer.IDiffElement;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.mapping.ModelProvider;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.resources.mapping.ResourceMappingContext;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.diff.IThreeWayDiff;
import org.eclipse.team.core.history.IFileHistoryProvider;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.core.mapping.IResourceDiff;
import org.eclipse.team.core.mapping.ISynchronizationScope;
import org.eclipse.team.core.synchronize.FastSyncInfoFilter;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.core.mapping.CompoundResourceTraversal;
import org.eclipse.team.internal.ui.history.FileRevisionEditorInput;
import org.eclipse.team.internal.ui.synchronize.SyncInfoModelElement;
import org.eclipse.team.ui.TeamImages;
import org.eclipse.team.ui.TeamUI;
import org.eclipse.team.ui.mapping.ISynchronizationCompareAdapter;
import org.eclipse.team.ui.synchronize.ISynchronizeManager;
import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput;
import org.eclipse.ui.IContributorResourceAdapter;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IContributorResourceAdapter2;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.ErrorEditorPart;
import org.eclipse.ui.internal.registry.EditorDescriptor;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.osgi.framework.Bundle;
public class Utils {
/**
* Constant used to indicate that tests are being run. This field
* should be the same as the corresponding field on
* org.eclipse.compare.internal.Utilities
*/
public static boolean RUNNING_TESTS = false;
/**
* Constant used while testing the indicate that changes should be flushed
* when the compare input changes and a viewer is dirty. This field
* should be the same as the corresponding field on
* org.eclipse.compare.internal.Utilities
*/
public static boolean TESTING_FLUSH_ON_COMPARE_INPUT_CHANGE = false;
/**
* The SortOperation takes a collection of objects and returns a sorted
* collection of these objects. Concrete instances of this class provide
* the criteria for the sorting of the objects based on the type of the
* objects.
*/
static public abstract class Sorter {
/**
* Returns true is elementTwo is 'greater than' elementOne This is the
* 'ordering' method of the sort operation. Each subclass overrides this
* method with the particular implementation of the 'greater than'
* concept for the objects being sorted.
* @param elementOne element 1
* @param elementTwo element 2
* @return whether element 2 is greater that element 1
*/
public abstract boolean compare(Object elementOne, Object elementTwo);
/**
* Sort the objects in sorted collection and return that collection.
*/
private Object[] quickSort(Object[] sortedCollection, int left, int right) {
int originalLeft = left;
int originalRight = right;
Object mid = sortedCollection[(left + right) >>> 1];
do {
while (compare(sortedCollection[left], mid))
left++;
while (compare(mid, sortedCollection[right]))
right--;
if (left <= right) {
Object tmp = sortedCollection[left];
sortedCollection[left] = sortedCollection[right];
sortedCollection[right] = tmp;
left++;
right--;
}
} while (left <= right);
if (originalLeft < right)
sortedCollection = quickSort(sortedCollection, originalLeft, right);
if (left < originalRight)
sortedCollection = quickSort(sortedCollection, left, originalRight);
return sortedCollection;
}
/**
* Return a new sorted collection from this unsorted collection. Sort
* using quick sort.
* @param unSortedCollection the original collection
* @return the sorted collection
*/
public Object[] sort(Object[] unSortedCollection) {
int size = unSortedCollection.length;
Object[] sortedCollection = new Object[size];
//copy the array so can return a new sorted collection
System.arraycopy(unSortedCollection, 0, sortedCollection, 0, size);
if (size > 1)
quickSort(sortedCollection, 0, size - 1);
return sortedCollection;
}
}
public static final Comparator<IResource> resourceComparator = new Comparator<IResource>() {
@Override
public boolean equals(Object obj) {
return false;
}
@Override
public int compare(IResource o1, IResource o2) {
return o1.getFullPath().toString().compareTo(o2.getFullPath().toString());
}
};
/**
* Shows the given errors to the user.
* @param shell
* the shell to open the error dialog in
* @param exception
* the exception containing the error
* @param title
* the title of the error dialog
* @param message
* the message for the error dialog
*/
public static void handleError(Shell shell, Exception exception, String title, String message) {
IStatus status = null;
boolean log = false;
boolean dialog = false;
Throwable t = exception;
if (exception instanceof TeamException) {
status = ((TeamException) exception).getStatus();
log = false;
dialog = true;
} else if (exception instanceof InvocationTargetException) {
t = ((InvocationTargetException) exception).getTargetException();
if (t instanceof TeamException) {
status = ((TeamException) t).getStatus();
log = false;
dialog = true;
} else if (t instanceof CoreException) {
status = ((CoreException) t).getStatus();
log = true;
dialog = true;
} else if (t instanceof InterruptedException) {
return;
} else {
status = new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, TeamUIMessages.TeamAction_internal, t);
log = true;
dialog = true;
}
}
if (status == null)
return;
if (!status.isOK()) {
IStatus toShow = status;
if (status.isMultiStatus()) {
IStatus[] children = status.getChildren();
if (children.length == 1) {
toShow = children[0];
}
}
if (title == null) {
title = status.getMessage();
}
if (message == null) {
message = status.getMessage();
}
if (dialog && shell != null) {
ErrorDialog.openError(shell, title, message, toShow);
}
if (log || shell == null) {
TeamUIPlugin.log(toShow.getSeverity(), message, t);
}
}
}
public static void runWithProgress(Shell parent, boolean cancelable, final IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException {
boolean createdShell = false;
try {
if (parent == null || parent.isDisposed()) {
Display display = Display.getCurrent();
if (display == null) {
// cannot provide progress (not in UI thread)
runnable.run(new NullProgressMonitor());
return;
}
// get the active shell or a suitable top-level shell
parent = display.getActiveShell();
if (parent == null) {
parent = new Shell(display);
createdShell = true;
}
}
// pop up progress dialog after a short delay
final Exception[] holder = new Exception[1];
BusyIndicator.showWhile(parent.getDisplay(), () -> {
try {
runnable.run(new NullProgressMonitor());
} catch (InvocationTargetException e1) {
holder[0] = e1;
} catch (InterruptedException e2) {
holder[0] = e2;
}
});
if (holder[0] != null) {
if (holder[0] instanceof InvocationTargetException) {
throw (InvocationTargetException) holder[0];
} else {
throw (InterruptedException) holder[0];
}
}
//new TimeoutProgressMonitorDialog(parent, TIMEOUT).run(true
// /*fork*/, cancelable, runnable);
} finally {
if (createdShell)
parent.dispose();
}
}
public static Shell getShell(IWorkbenchSite site) {
return getShell(site, false);
}
public static Shell getShell(IWorkbenchSite site, boolean syncIfNecessary) {
if(site != null) {
Shell shell = site.getShell();
if (!shell.isDisposed())
return shell;
}
IWorkbench workbench = PlatformUI.getWorkbench();
if (workbench != null) {
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
if (window != null) {
return window.getShell();
}
}
// Fallback to using the display
Display display = Display.getCurrent();
if (display == null) {
display = Display.getDefault();
if (display.isDisposed()) return null;
if (syncIfNecessary) {
final Shell[] result = new Shell[] { null };
Runnable r = () -> result[0] = new Shell(Display.getDefault());
display.syncExec(r);
return result[0];
}
}
if (display.isDisposed()) return null;
return new Shell(display);
}
/*
* This method is only for use by the Target Management feature (see bug
* 16509). @param t
*/
public static void handle(final Throwable exception) {
TeamUIPlugin.getStandardDisplay().asyncExec(() -> {
IStatus error = null;
Throwable t = exception;
if (t instanceof InvocationTargetException) {
t = ((InvocationTargetException) t).getTargetException();
}
if (t instanceof CoreException) {
error = ((CoreException) t).getStatus();
} else if (t instanceof TeamException) {
error = ((TeamException) t).getStatus();
} else {
error = new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, TeamUIMessages.simpleInternal, t);
}
Shell shell = new Shell(Display.getDefault());
if (error.getSeverity() == IStatus.INFO) {
MessageDialog.openInformation(shell, TeamUIMessages.information, error.getMessage());
} else {
ErrorDialog.openError(shell, TeamUIMessages.exception, null, error);
}
shell.dispose();
// Let's log non-team exceptions
if (!(t instanceof TeamException)) {
TeamUIPlugin.log(error.getSeverity(), error.getMessage(), t);
}
});
}
public static Shell findShell() {
Display display = TeamUIPlugin.getStandardDisplay();
Shell activeShell = display.getActiveShell();
if (activeShell != null)
return activeShell;
// worst case, just create our own.
return new Shell(display);
}
public static void initAction(IAction a, String prefix) {
Utils.initAction(a, prefix, Policy.getActionBundle());
}
public static void initAction(IAction a, String prefix, ResourceBundle bundle) {
Utils.initAction(a, prefix, bundle, null);
}
public static void updateLabels(SyncInfo sync, CompareConfiguration config, IProgressMonitor monitor) {
final IResourceVariant remote = sync.getRemote();
final IResourceVariant base = sync.getBase();
String baseAuthor = null;
String remoteAuthor = null;
String localAuthor = null;
String localContentId = sync.getLocalContentIdentifier();
String remoteContentId= remote != null ? remote.getContentIdentifier() : null;
String baseContentId= base != null ? base.getContentIdentifier() : null;
if (isShowAuthor()) {
baseAuthor = getAuthor(base, monitor);
if (baseContentId != null && baseContentId.equals(remoteContentId))
remoteAuthor= baseAuthor;
else
remoteAuthor= getAuthor(remote, monitor);
if (localContentId != null) {
if (localContentId.equals(baseContentId))
localAuthor= baseAuthor;
else if (localContentId.equals(remoteAuthor))
localAuthor= remoteAuthor;
else
localAuthor= sync.getLocalAuthor(monitor);
}
}
if (localContentId != null) {
if (localAuthor != null) {
config.setLeftLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_localLabelAuthorExists, new String[] { localContentId, localAuthor }));
} else {
config.setLeftLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_localLabelExists, new String[] { localContentId }));
}
} else {
config.setLeftLabel(TeamUIMessages.SyncInfoCompareInput_localLabel);
}
if (remote != null) {
if (remoteAuthor != null) {
config.setRightLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_remoteLabelAuthorExists, new String[] { remoteContentId, remoteAuthor }));
} else {
config.setRightLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_remoteLabelExists, new String[] { remoteContentId }));
}
} else {
config.setRightLabel(TeamUIMessages.SyncInfoCompareInput_remoteLabel);
}
if (base != null) {
if (baseAuthor != null) {
config.setAncestorLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_baseLabelAuthorExists, new String[] { baseContentId, baseAuthor }));
} else {
config.setAncestorLabel(NLS.bind(TeamUIMessages.SyncInfoCompareInput_baseLabelExists, new String[] { baseContentId }));
}
} else {
config.setAncestorLabel(TeamUIMessages.SyncInfoCompareInput_baseLabel);
}
}
/**
* DO NOT REMOVE, used in a product.
*
* @deprecated As of 3.5, replaced by
* {@link #updateLabels(SyncInfo, CompareConfiguration, IProgressMonitor)}
*/
@SuppressWarnings("javadoc")
@Deprecated
public static void updateLabels(SyncInfo sync, CompareConfiguration config) {
updateLabels(sync, config, null);
}
public static boolean isShowAuthor() {
IPreferenceStore store = TeamUIPlugin.getPlugin().getPreferenceStore();
return store.getBoolean(IPreferenceIds.SHOW_AUTHOR_IN_COMPARE_EDITOR);
}
private static String getAuthor(IResourceVariant variant,
IProgressMonitor monitor) {
String author = null;
if (variant instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) variant;
IFileRevision revision = adaptable.getAdapter(IFileRevision.class);
if (revision == null)
return null;
try {
IFileRevision complete = revision.withAllProperties(monitor);
if (complete != null) {
author = complete.getAuthor();
}
} catch (CoreException e) {
TeamUIPlugin.log(e);
}
}
return author;
}
public static String getLocalContentId(IDiff diff) {
if (diff instanceof IThreeWayDiff) {
IThreeWayDiff twd = (IThreeWayDiff) diff;
diff = twd.getLocalChange();
if (diff == null)
diff = twd.getRemoteChange();
}
if (diff instanceof IResourceDiff) {
IResourceDiff rd = (IResourceDiff) diff;
IResource resource = rd.getResource();
IFileHistoryProvider provider = getHistoryProvider(resource);
if (provider != null) {
IFileRevision revision = provider.getWorkspaceFileRevision(resource);
if (revision != null)
return revision.getContentIdentifier();
}
}
return null;
}
public static IFileHistoryProvider getHistoryProvider(IResource resource) {
RepositoryProvider rp = RepositoryProvider.getProvider(resource.getProject());
if (rp != null)
return rp.getFileHistoryProvider();
return null;
}
public static IFileRevision getBase(IDiff diff) {
if (diff instanceof IThreeWayDiff) {
IThreeWayDiff twd = (IThreeWayDiff) diff;
IDiff remoteChange = twd.getRemoteChange();
if (remoteChange instanceof IResourceDiff) {
IResourceDiff rd = (IResourceDiff) remoteChange;
return rd.getBeforeState();
}
IDiff localChange = twd.getLocalChange();
if (localChange instanceof IResourceDiff) {
IResourceDiff ld = (IResourceDiff) localChange;
return ld.getBeforeState();
}
}
return null;
}
public static IFileRevision getRemote(IDiff diff) {
if (diff instanceof IResourceDiff) {
IResourceDiff rd = (IResourceDiff) diff;
return rd.getAfterState();
}
if (diff instanceof IThreeWayDiff) {
IThreeWayDiff twd = (IThreeWayDiff) diff;
IDiff remoteChange = twd.getRemoteChange();
if (remoteChange instanceof IResourceDiff) {
IResourceDiff rd = (IResourceDiff) remoteChange;
return rd.getAfterState();
}
IDiff localChange = twd.getLocalChange();
if (localChange instanceof IResourceDiff) {
IResourceDiff ld = (IResourceDiff) localChange;
return ld.getBeforeState();
}
}
return null;
}
/**
* Initialize the given Action from a ResourceBundle.
* @param a the action
* @param prefix the bundle key prefix
* @param bundle the bundle
* @param bindings additional input to the action label
*/
public static void initAction(IAction a, String prefix, ResourceBundle bundle, String[] bindings) {
String labelKey = "label"; //$NON-NLS-1$
String tooltipKey = "tooltip"; //$NON-NLS-1$
String imageKey = "image"; //$NON-NLS-1$
String descriptionKey = "description"; //$NON-NLS-1$
if (prefix != null && prefix.length() > 0) {
labelKey = prefix + labelKey;
tooltipKey = prefix + tooltipKey;
imageKey = prefix + imageKey;
descriptionKey = prefix + descriptionKey;
}
String s = null;
if(bindings != null) {
s = NLS.bind(getString(labelKey, bundle), bindings);
} else {
s = getString(labelKey, bundle);
}
if (s != null)
a.setText(s);
s = getString(tooltipKey, bundle);
if (s != null)
a.setToolTipText(s);
s = getString(descriptionKey, bundle);
if (s != null)
a.setDescription(s);
String relPath = getString(imageKey, bundle);
if (relPath != null && !relPath.equals(imageKey) && relPath.trim().length() > 0) {
String dPath;
String ePath;
if (relPath.contains("/")) { //$NON-NLS-1$
String path = relPath.substring(1);
dPath = 'd' + path;
ePath = 'e' + path;
} else {
dPath = "dlcl16/" + relPath; //$NON-NLS-1$
ePath = "elcl16/" + relPath; //$NON-NLS-1$
}
ImageDescriptor id = TeamImages.getImageDescriptor(dPath);
if (id != null)
a.setDisabledImageDescriptor(id);
id = TeamUIPlugin.getImageDescriptor(ePath);
if (id != null)
a.setImageDescriptor(id);
}
}
public static String getString(String key, ResourceBundle b) {
try {
return b.getString(key);
} catch (MissingResourceException e) {
return key;
} catch (NullPointerException e) {
return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
public static String modeToString(int mode) {
switch (mode) {
case ISynchronizePageConfiguration.INCOMING_MODE :
return TeamUIMessages.Utils_22;
case ISynchronizePageConfiguration.OUTGOING_MODE :
return TeamUIMessages.Utils_23;
case ISynchronizePageConfiguration.BOTH_MODE :
return TeamUIMessages.Utils_24;
case ISynchronizePageConfiguration.CONFLICTING_MODE :
return TeamUIMessages.Utils_25;
default:
return TeamUIMessages.Utils_26;
}
}
/**
* Returns the list of resources contained in the given elements.
* @param elements
* @return the list of resources contained in the given elements.
*/
private static IResource[] getResources(Object[] elements, List<Object> nonResources,
boolean isContributed, boolean includeMappingResources) {
List<IResource> resources = new ArrayList<>();
for (Object element : elements) {
boolean isResource = false;
if (element instanceof IResource) {
resources.add((IResource) element);
isResource = true;
} else if (element instanceof ISynchronizeModelElement){
IResource resource = ((ISynchronizeModelElement) element).getResource();
if (resource != null) {
resources.add(resource);
isResource = true;
}
} else if (element instanceof ResourceMapping) {
if (includeMappingResources) {
isResource = true;
getResources((ResourceMapping)element, resources);
}
} else if (element != null) {
Object adapted;
if (isContributed) {
adapted = getResource(element);
} else {
adapted = Adapters.adapt(element, IResource.class);
}
if (adapted instanceof IResource) {
IResource resource = (IResource) adapted;
isResource = true;
if (resource.getType() != IResource.ROOT) {
resources.add(resource);
}
} else {
if (isContributed) {
adapted = getResourceMapping(element);
} else {
adapted = Adapters.adapt(element, ResourceMapping.class);
}
if (adapted instanceof ResourceMapping && includeMappingResources) {
isResource = true;
getResources((ResourceMapping) adapted, resources);
}
}
}
if (!isResource) {
if (nonResources != null)
nonResources.add(element);
}
}
return resources.toArray(new IResource[resources.size()]);
}
private static void getResources(ResourceMapping element, List<IResource> resources) {
try {
ResourceTraversal[] traversals = element.getTraversals(ResourceMappingContext.LOCAL_CONTEXT, null);
for (ResourceTraversal traversal : traversals) {
IResource[] resourceArray = traversal.getResources();
Collections.addAll(resources, resourceArray);
}
} catch (CoreException e) {
TeamUIPlugin.log(new Status(IStatus.ERROR, TeamUIPlugin.ID, 0, "Error traversing resource mapping", e)); //$NON-NLS-1$
}
}
public static Object[] getNonResources(Object[] elements) {
List<Object> nonResources = new ArrayList<>();
getResources(elements, nonResources, false, false);
return nonResources.toArray();
}
public static IResource[] getResources(Object[] element) {
return getResources(element, null, false /* isContributed */, false /* includeMappingResources */);
}
public static IResource[] getContributedResources(Object[] elements) {
return getResources(elements, null, true /* isContributed */, true /* isIncudeMappings */);
}
/**
* Return whether any sync nodes in the given selection or their
* descendants match the given filter.
* @param selection a selection
* @param filter a sync info filter
* @return whether any sync nodes in the given selection or their
* descendants match the given filter
*/
public static boolean hasMatchingDescendant(IStructuredSelection selection, FastSyncInfoFilter filter) {
for (Iterator iter = selection.iterator(); iter.hasNext();) {
Object o = iter.next();
if (o instanceof ISynchronizeModelElement) {
if (hasMatchingDescendant((ISynchronizeModelElement)o, filter)) {
return true;
}
}
}
return false;
}
private static boolean hasMatchingDescendant(ISynchronizeModelElement element, FastSyncInfoFilter filter) {
if (element.getKind() != SyncInfo.IN_SYNC && element instanceof SyncInfoModelElement) {
SyncInfo info = ((SyncInfoModelElement) element).getSyncInfo();
if (info != null && filter.select(info)) {
return true;
}
}
IDiffElement[] children = element.getChildren();
for (IDiffElement child : children) {
if (child instanceof ISynchronizeModelElement) {
if (hasMatchingDescendant((ISynchronizeModelElement)child, filter)) {
return true;
}
}
}
return false;
}
/**
* This method returns all out-of-sync SyncInfos that are in the current
* selection.
* @param selected the selected objects
*
* @return the list of selected sync infos
*/
public static IDiffElement[] getDiffNodes(Object[] selected) {
Set<IDiffElement> result = new HashSet<>();
for (Object object : selected) {
if(object instanceof IDiffElement) {
collectAllNodes((IDiffElement)object, result);
}
}
return result.toArray(new IDiffElement[result.size()]);
}
private static void collectAllNodes(IDiffElement element, Set<IDiffElement> nodes) {
if (element.getKind() != SyncInfo.IN_SYNC) {
nodes.add(element);
}
if(element instanceof IDiffContainer) {
IDiffElement[] children = ((IDiffContainer)element).getChildren();
for (IDiffElement c : children) {
collectAllNodes(c, nodes);
}
}
}
public static void schedule(Job job, IWorkbenchSite site) {
if (site != null) {
IWorkbenchSiteProgressService siteProgress = site.getAdapter(IWorkbenchSiteProgressService.class);
if (siteProgress != null) {
siteProgress.schedule(job, 0, true /* use half-busy cursor */);
return;
}
}
job.schedule();
}
public static byte[] readBytes(InputStream in) {
ByteArrayOutputStream bos= new ByteArrayOutputStream();
try {
while (true) {
int c= in.read();
if (c == -1)
break;
bos.write(c);
}
} catch (IOException ex) {
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException x) {
// silently ignored
}
}
try {
bos.close();
} catch (IOException x) {
// silently ignored
}
}
return bos.toByteArray();
}
public static boolean equalObject(Object o1, Object o2) {
if (o1 == null && o2 == null) return true;
if (o1 == null || o2 == null) return false;
return o1.equals(o2);
}
public static String getKey(String id, String secondaryId) {
return secondaryId == null ? id : id + '/' + secondaryId;
}
public static String convertSelection(IResource[] resources) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
if(i > 0) buffer.append(", "); //$NON-NLS-1$
buffer.append(resource.getFullPath());
}
return buffer.toString();
}
/**
* Shorten the given text <code>t</code> so that its length
* doesn't exceed the given width. This implementation
* replaces characters in the center of the original string with an
* ellipsis ("...").
* @param maxWidth the maximum length for the text
* @param textValue the text to be shortened
* @return the shortened text
*/
public static String shortenText(int maxWidth, String textValue) {
int length = textValue.length();
if (length < maxWidth)
return textValue;
String ellipsis = "..."; //$NON-NLS-1$
int subStrLen = (maxWidth - ellipsis.length()) / 2;
int addtl = (maxWidth - ellipsis.length()) % 2;
StringBuilder sb = new StringBuilder(maxWidth);
sb.append(textValue.substring(0, subStrLen));
sb.append(ellipsis);
sb.append(textValue.substring(length - subStrLen - addtl));
return sb.toString();
}
public static String getTypeName(ISynchronizeParticipant participant) {
ISynchronizeManager manager = TeamUI.getSynchronizeManager();
return manager.getParticipantDescriptor(participant.getId()).getName();
}
/**
* The viewer will only be updated if the viewer is not null, the control is not disposed, and
* this code is being run from the UI thread.
* @param viewer the viewer to be updated
* @return whether it is safe to update the viewer
*/
public static boolean canUpdateViewer(StructuredViewer viewer) {
if(viewer == null || viewer.getControl().isDisposed()) return false;
Display display = viewer.getControl().getDisplay();
if (display == null) return false;
if (display.getThread() != Thread.currentThread ()) return false;
return true;
}
public static void asyncExec(final Runnable r, StructuredViewer v) {
if(v == null) return;
final Control ctrl = v.getControl();
if (ctrl != null && !ctrl.isDisposed()) {
ctrl.getDisplay().asyncExec(() -> {
if (!ctrl.isDisposed()) {
BusyIndicator.showWhile(ctrl.getDisplay(), r);
}
});
}
}
public static void syncExec(final Runnable r, StructuredViewer v) {
if(v == null) return;
final Control ctrl = v.getControl();
syncExec(r, ctrl);
}
public static void syncExec(final Runnable r, final Control ctrl) {
if (ctrl != null && !ctrl.isDisposed()) {
ctrl.getDisplay().syncExec(() -> {
if (!ctrl.isDisposed()) {
BusyIndicator.showWhile(ctrl.getDisplay(), r);
}
});
}
}
public static void asyncExec(final Runnable r, final Control ctrl) {
if (ctrl != null && !ctrl.isDisposed()) {
ctrl.getDisplay().asyncExec(() -> {
if (!ctrl.isDisposed()) {
BusyIndicator.showWhile(ctrl.getDisplay(), r);
}
});
}
}
public static SyncInfo getSyncInfo(ISynchronizeModelElement node) {
if (node instanceof IAdaptable) {
return ((IAdaptable) node).getAdapter(SyncInfo.class);
}
return null;
}
public static ISynchronizationCompareAdapter getCompareAdapter(Object element) {
ModelProvider provider = getModelProvider(element);
if (provider != null) {
Object o = provider.getAdapter(ISynchronizationCompareAdapter.class);
if (o instanceof ISynchronizationCompareAdapter) {
return (ISynchronizationCompareAdapter) o;
}
}
return null;
}
public static ModelProvider getModelProvider(Object o) {
if (o instanceof ModelProvider) {
return (ModelProvider) o;
}
ResourceMapping mapping = getResourceMapping(o);
if (mapping != null)
return mapping.getModelProvider();
return null;
}
public static IResource getResource(Object o) {
IResource resource = null;
if (o instanceof IResource) {
resource = (IResource) o;
} else if (o instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) o;
resource = adaptable.getAdapter(IResource.class);
if (resource == null) {
IContributorResourceAdapter adapter = adaptable.getAdapter(IContributorResourceAdapter.class);
if (adapter != null)
resource = adapter.getAdaptedResource(adaptable);
}
}
return resource;
}
public static ResourceMapping getResourceMapping(Object o) {
if (o instanceof ResourceMapping) {
return (ResourceMapping) o;
}
if (o instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) o;
Object adapted = adaptable.getAdapter(ResourceMapping.class);
if (adapted instanceof ResourceMapping) {
return(ResourceMapping) adapted;
}
adapted = adaptable.getAdapter(IContributorResourceAdapter.class);
if (adapted instanceof IContributorResourceAdapter2) {
IContributorResourceAdapter2 cra = (IContributorResourceAdapter2) adapted;
return cra.getAdaptedResourceMapping(adaptable);
}
} else {
Object adapted = Platform.getAdapterManager().getAdapter(o, ResourceMapping.class);
if (adapted instanceof ResourceMapping) {
return(ResourceMapping) adapted;
}
}
return null;
}
public static ResourceMapping[] getResourceMappings(Object[] objects) {
List<ResourceMapping> result = new ArrayList<>();
for (Object object : objects) {
ResourceMapping mapping = getResourceMapping(object);
if (mapping != null)
result.add(mapping);
}
return result.toArray(new ResourceMapping[result.size()]);
}
public static String getLabel(ResourceMapping mapping) {
ModelProvider provider = mapping.getModelProvider();
ISynchronizationCompareAdapter adapter = getCompareAdapter(provider);
if (adapter == null)
return ""; //$NON-NLS-1$
String pathString = adapter.getPathString(mapping);
if (pathString == null || pathString.length() == 0)
return adapter.getName(mapping);
return pathString;
}
public static String getLabel(ModelProvider provider) {
ResourceMapping mapping = Utils.getResourceMapping(provider);
if (mapping != null) {
String base = Utils.getLabel(mapping);
if (base != null && base.length() > 0)
return base;
}
return provider.getDescriptor().getLabel();
}
public static String getScopeDescription(ISynchronizationScope scope) {
ResourceMapping[] mappings = scope.getInputMappings();
if (mappings.length == 1) {
String label = getLabel(mappings[0]);
if (label == null)
return TeamUIMessages.Utils_19;
else
return label;
}
String desc = convertSelection(mappings);
if (desc.length() > 0)
return shortenText(30, desc);
return NLS.bind(TeamUIMessages.Utils_18, Integer.valueOf(mappings.length));
}
public static String convertSelection(ResourceMapping[] mappings) {
StringBuilder buffer = new StringBuilder();
boolean hadOne = false;
for (ResourceMapping resourceMapping : mappings) {
String label = getLabel(resourceMapping);
if (label != null) {
if(hadOne) buffer.append(", "); //$NON-NLS-1$
hadOne = true;
buffer.append(label);
}
}
return buffer.toString();
}
public static ResourceTraversal[] getTraversals(Object[] elements) throws CoreException {
CompoundResourceTraversal traversal = new CompoundResourceTraversal();
for (Object object : elements) {
ResourceMapping mapping = getResourceMapping(object);
if (mapping != null) {
traversal.addTraversals(mapping.getTraversals(ResourceMappingContext.LOCAL_CONTEXT, null));
}
}
return traversal.asTraversals();
}
/**
* Return whether the editor associated with a descriptor is a text editor
* (i.e. an instance of AbstractDecoratedTextEditor).
* See bug 99568 for a request to move the createEditor method to IEditorDescriptor.
* @param descriptor
* @return whether the editor associated with a descriptor is a text editor
* @throws CoreException
*/
public static boolean isTextEditor(IEditorDescriptor descriptor) throws CoreException {
if (!(descriptor instanceof EditorDescriptor))
return false;
EditorDescriptor desc = (EditorDescriptor) descriptor;
String className = desc.getClassName();
String contributor = desc.getPluginId();
if (className == null || contributor == null)
return false;
try {
Bundle bundle= Platform.getBundle(contributor);
if (bundle != null) {
Class clazz= bundle.loadClass(className);
return AbstractDecoratedTextEditor.class.isAssignableFrom(clazz);
}
} catch (ClassNotFoundException e) {
// fallback and create editor
}
IEditorPart editor= desc.createEditor();
editor.dispose();
return editor instanceof AbstractDecoratedTextEditor;
}
public static IEditorPart openEditor(IWorkbenchPage page, IFileRevision revision, IProgressMonitor monitor) throws CoreException {
IStorage file = revision.getStorage(monitor);
if (file instanceof IFile) {
//if this is the current workspace file, open it
return IDE.openEditor(page, (IFile)file, OpenStrategy.activateOnOpen());
} else {
FileRevisionEditorInput fileRevEditorInput = FileRevisionEditorInput.createEditorInputFor(revision, monitor);
IEditorPart part = openEditor(page, fileRevEditorInput);
return part;
}
}
public static IEditorPart openEditor(IWorkbenchPage page,
FileRevisionEditorInput editorInput) throws PartInitException {
String id = getEditorId(editorInput);
return openEditor(page, editorInput, id);
}
public static IEditorPart openEditor(IWorkbenchPage page,
FileRevisionEditorInput editorInput, String editorId)
throws PartInitException {
try {
IEditorPart part = page.openEditor(editorInput, editorId,
OpenStrategy.activateOnOpen());
// See bug 90582 for the reasons behind this discouraged access
if (part instanceof ErrorEditorPart) {
page.closeEditor(part, false);
part = null;
}
if (part == null) {
throw new PartInitException(NLS.bind(TeamUIMessages.Utils_17,
editorId));
}
return part;
} catch (PartInitException e) {
if (editorId.equals("org.eclipse.ui.DefaultTextEditor")) { //$NON-NLS-1$
throw e;
} else {
return page.openEditor(editorInput,
"org.eclipse.ui.DefaultTextEditor"); //$NON-NLS-1$
}
}
}
public static IEditorDescriptor[] getEditors(IFileRevision revision) {
String name= revision.getName();
IEditorRegistry registry = PlatformUI.getWorkbench()
.getEditorRegistry();
// so far only the revision name is used to find editors
IEditorDescriptor[] editorDescs= registry.getEditors(name/* , getContentType(revision) */);
return IDE.overrideEditorAssociations(name, null, editorDescs);
}
public static IEditorDescriptor getDefaultEditor(IFileRevision revision) {
String name= revision.getName();
// so far only the revision name is used to find the default editor
try {
return IDE.getEditorDescriptor(name);
} catch (PartInitException e) {
// Fallback to old way of getting the editor
IEditorRegistry registry= PlatformUI.getWorkbench().getEditorRegistry();
return registry.getDefaultEditor(name);
}
}
private static String getEditorId(FileRevisionEditorInput editorInput) {
String id= getEditorId(editorInput, getContentType(editorInput));
return id;
}
private static IContentType getContentType(FileRevisionEditorInput editorInput) {
IContentType type = null;
try {
InputStream contents = editorInput.getStorage().getContents();
try {
type = getContentType(editorInput.getFileRevision().getName(), contents);
} finally {
try {
contents.close();
} catch (IOException e) {
// ignore
}
}
} catch (CoreException e) {
TeamUIPlugin.log(IStatus.ERROR, NLS.bind("An error occurred reading the contents of file {0}", new String[] { editorInput.getName() }), e); //$NON-NLS-1$
}
return type;
}
private static IContentType getContentType(String fileName, InputStream contents) {
IContentType type = null;
if (contents != null) {
try {
type = Platform.getContentTypeManager().findContentTypeFor(contents, fileName);
} catch (IOException e) {
TeamUIPlugin.log(IStatus.ERROR, NLS.bind("An error occurred reading the contents of file {0}", fileName), e); //$NON-NLS-1$
}
}
if (type == null) {
type = Platform.getContentTypeManager().findContentTypeFor(fileName);
}
return type;
}
private static String getEditorId(FileRevisionEditorInput editorInput, IContentType type) {
String fileName= editorInput.getFileRevision().getName();
IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
IEditorDescriptor descriptor = registry.getDefaultEditor(fileName, type);
IDE.overrideDefaultEditorAssociation(editorInput, type, descriptor);
String id;
if (descriptor == null || descriptor.isOpenExternal()) {
id = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$
} else {
id = descriptor.getId();
}
return id;
}
/**
* Returns an editor that can be re-used. An open compare editor that has
* un-saved changes cannot be re-used.
*
* @param input
* the input being opened
* @param page
* @param editorInputClasses
* @return an EditorPart or <code>null</code> if none can be found
*/
public static IEditorPart findReusableCompareEditor(
CompareEditorInput input, IWorkbenchPage page,
Class[] editorInputClasses) {
IEditorReference[] editorRefs = page.getEditorReferences();
// first loop looking for an editor with the same input
for (IEditorReference editorRef : editorRefs) {
IEditorPart part = editorRef.getEditor(false);
if (part != null && part instanceof IReusableEditor) {
for (Class editorInputClasse : editorInputClasses) {
// check if the editor input type
// complies with the types given by the caller
if (editorInputClasse.isInstance(part.getEditorInput()) && part.getEditorInput().equals(input)) {
return part;
}
}
}
}
// if none found and "Reuse open compare editors" preference is on use
// a non-dirty editor
if (TeamUIPlugin.getPlugin().getPreferenceStore()
.getBoolean(IPreferenceIds.REUSE_OPEN_COMPARE_EDITOR)) {
for (IEditorReference editorRef : editorRefs) {
IEditorPart part = editorRef.getEditor(false);
if (part != null
&& (part.getEditorInput() instanceof SaveableCompareEditorInput)
&& part instanceof IReusableEditor && !part.isDirty()) {
return part;
}
}
}
// no re-usable editor found
return null;
}
}