/******************************************************************************* * 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.ui.synchronize; import java.util.Arrays; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IBasicPropertyConstants; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.subscribers.Subscriber; import org.eclipse.team.core.synchronize.SyncInfoFilter; import org.eclipse.team.core.synchronize.SyncInfoTree; import org.eclipse.team.internal.core.subscribers.SubscriberSyncInfoCollector; import org.eclipse.team.internal.ui.IPreferenceIds; import org.eclipse.team.internal.ui.TeamUIMessages; import org.eclipse.team.internal.ui.TeamUIPlugin; import org.eclipse.team.internal.ui.Utils; import org.eclipse.team.internal.ui.synchronize.IRefreshSubscriberListener; import org.eclipse.team.internal.ui.synchronize.IRefreshable; import org.eclipse.team.internal.ui.synchronize.RefreshParticipantJob; import org.eclipse.team.internal.ui.synchronize.RefreshSubscriberParticipantJob; import org.eclipse.team.internal.ui.synchronize.RefreshUserNotificationPolicy; import org.eclipse.team.internal.ui.synchronize.RefreshUserNotificationPolicyInModalDialog; import org.eclipse.team.internal.ui.synchronize.SubscriberParticipantPage; import org.eclipse.team.internal.ui.synchronize.SubscriberRefreshSchedule; import org.eclipse.team.internal.ui.synchronize.SynchronizePageConfiguration; import org.eclipse.team.ui.TeamUI; import org.eclipse.ui.IMemento; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.IPageBookViewPage; import org.eclipse.ui.progress.IProgressConstants2; /** * A synchronize participant that displays synchronization information for local resources that are * managed via a {@link Subscriber}. It maintains a dynamic collection of all out-of-sync resources * by listening to workspace resource changes and remote changes thus creating a live view of * changes in the workspace. *
* The subscriber can be configured to be synchronized in the background based on a schedule. This * effectively refreshes the subscriber and updates the dynamic sync set. *
* Subclasses will typically want to override the following methods:
*null
,
* the participant will include all roots of its subscriber
*
* @param roots the root resources to consider or null
* to consider all roots of the subscriber
*/
private void setResources(IResource[] roots) {
collector.setRoots(roots);
}
/**
* Refresh this participants synchronization state and displays the result in a model dialog.
* @param shell
*
* @param resources the resources to be refreshed.
* @param jobName
* @param taskName the task name to be shown to the user
* @param configuration
* @param site the site in which to run the refresh
*/
public final void refreshInDialog(Shell shell, IResource[] resources, String jobName, String taskName, ISynchronizePageConfiguration configuration, IWorkbenchSite site) {
IRefreshSubscriberListener listener = new RefreshUserNotificationPolicyInModalDialog(shell, taskName, configuration, this);
internalRefresh(resources, jobName, taskName, site, listener);
}
/**
* Refresh a participant in the background the result of the refresh are shown in the progress view. Refreshing
* can also be considered synchronizing, or refreshing the synchronization state. Basically this is a long
* running operation that will update the participants sync info sets with new changes detected on the
* server. Either or both of the shortTaskName
and longTaskName
can be null
* in which case, the default values for these are returned by the methods getShortTaskName()
and
* getLongTaskName(IResource[])
will be used.
*
* @param resources the resources to be refreshed.
* @param shortTaskName the taskName of the background job that will run the synchronize or null
* if the default job name is desired.
* @param longTaskName the taskName of the progress monitor running the synchronize or null
* if the default job name is desired.
* @param site the workbench site the synchronize is running from. This can be used to notify the site
* that a job is running.
*/
public final void refresh(IResource[] resources, String shortTaskName, String longTaskName, IWorkbenchSite site) {
IRefreshSubscriberListener listener = new RefreshUserNotificationPolicy(this);
internalRefresh(resources, shortTaskName, longTaskName, site, listener);
}
/**
* Refresh a participant. The returned status describes the result of the refresh.
* @param resources
* @param taskName
* @param monitor
* @return a status
*/
public final IStatus refreshNow(IResource[] resources, String taskName, IProgressMonitor monitor) {
Job.getJobManager().cancel(this);
RefreshParticipantJob job = new RefreshSubscriberParticipantJob(this, taskName, taskName, resources, null);
return job.run(monitor);
}
@Override
public void dispose() {
Job.getJobManager().cancel(this);
refreshSchedule.dispose();
TeamUI.removePropertyChangeListener(this);
collector.dispose();
scope.dispose();
}
@Override
public String getName() {
String name = super.getName();
return NLS.bind(TeamUIMessages.SubscriberParticipant_namePattern, new String[] { name, scope.getName() });
}
/**
* Return the name of the participant as specified in the plugin manifest file.
* This method is provided to give access to this name since it is masked by
* the getName()
method defined in this class.
* @return the name of the participant as specified in the plugin manifest file
* @since 3.1
*/
protected final String getShortName() {
return super.getName();
}
/**
* Returns the SyncInfoTree
for this participant. This set
* contains the out-of-sync resources supervised by this participant.
*
* @return the sync info set that contains the out-of-sync resources
* for this participant.
*/
public SyncInfoTree getSyncInfoSet() {
return getSubscriberSyncInfoCollector().getSyncInfoSet();
}
/**
* Return the Subscriber
associated with this this participant. This
* method will only return null
if the participant has not been initialized
* yet.
*
* @return the Subscriber
associated with this this participant.
*/
public Subscriber getSubscriber() {
if (collector == null) return null;
return collector.getSubscriber();
}
/**
* Returns a participant that matches the given resource scoping
*
* @param ID the type id of participants to match
* @param resources the resources to match in the scope
* @return a participant that matches the given resource scoping
*/
public static SubscriberParticipant getMatchingParticipant(String ID, IResource[] resources) {
ISynchronizeParticipantReference[] refs = TeamUI.getSynchronizeManager().getSynchronizeParticipants();
for (int i = 0; i < refs.length; i++) {
ISynchronizeParticipantReference reference = refs[i];
if(reference.getId().equals(ID)) {
SubscriberParticipant p;
try {
p = (SubscriberParticipant)reference.getParticipant();
} catch (TeamException e) {
continue;
}
IResource[] roots = p.getResources();
Arrays.sort(resources, Utils.resourceComparator);
Arrays.sort(roots, Utils.resourceComparator);
if (Arrays.equals(resources, roots)) {
return p;
}
}
}
return null;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(TeamUI.GLOBAL_IGNORES_CHANGED)) {
collector.reset();
}
if (event.getProperty().equals(ISynchronizeScope.ROOTS)) {
setResources(scope.getRoots());
}
if (event.getProperty().equals(ISynchronizeScope.NAME)) {
// Force a name change event, which will cause this classes getName to be called
// and updated with the correct working set name.
firePropertyChange(this, IBasicPropertyConstants.P_TEXT, null, getName());
}
}
@Override
public void init(String secondaryId, IMemento memento) throws PartInitException {
super.init(secondaryId, memento);
if(memento != null) {
IMemento settings = memento.getChild(CTX_SUBSCRIBER_PARTICIPANT_SETTINGS);
if(settings != null) {
SubscriberRefreshSchedule schedule = SubscriberRefreshSchedule.init(settings.getChild(CTX_SUBSCRIBER_SCHEDULE_SETTINGS), createRefreshable());
setRefreshSchedule(schedule);
this.scope = AbstractSynchronizeScope.createScope(settings);
scope.addPropertyChangeListener(this);
}
}
}
@Override
public void saveState(IMemento memento) {
super.saveState(memento);
IMemento settings = memento.createChild(CTX_SUBSCRIBER_PARTICIPANT_SETTINGS);
refreshSchedule.saveState(settings.createChild(CTX_SUBSCRIBER_SCHEDULE_SETTINGS));
AbstractSynchronizeScope.saveScope(scope, settings);
}
/**
* Reset the sync set of the participant by repopulating it from scratch.
*/
public void reset() {
getSubscriberSyncInfoCollector().reset();
}
/**
* Return the SubscriberSyncInfoCollector
for the participant.
* This collector maintains the set of all out-of-sync resources for the
* subscriber.
*
* @return the SubscriberSyncInfoCollector
for this participant
* @noreference This method is not intended to be referenced by clients.
* @nooverride This method is not intended to be re-implemented or extended
* by clients.
*/
public SubscriberSyncInfoCollector getSubscriberSyncInfoCollector() {
return collector;
}
/**
* @noreference This method is not intended to be referenced by clients.
* @nooverride This method is not intended to be re-implemented or extended by clients.
*/
public void setRefreshSchedule(SubscriberRefreshSchedule schedule) {
if (refreshSchedule != schedule) {
if (refreshSchedule != null) {
refreshSchedule.dispose();
}
this.refreshSchedule = schedule;
}
// Always fir the event since the schedule may have been changed
firePropertyChange(this, AbstractSynchronizeParticipant.P_SCHEDULED, schedule, schedule);
}
/**
* @noreference This method is not intended to be referenced by clients.
* @nooverride This method is not intended to be re-implemented or extended by clients.
*/
public SubscriberRefreshSchedule getRefreshSchedule() {
return refreshSchedule;
}
@Override
protected void initializeConfiguration(ISynchronizePageConfiguration configuration) {
configuration.setProperty(SynchronizePageConfiguration.P_PARTICIPANT_SYNC_INFO_SET, collector.getSyncInfoSet());
}
@Override
public void run(IWorkbenchPart part) {
refresh(getResources(), null, null, part != null ? part.getSite() : null);
}
/**
* Returns the short task name (e.g. no more than 25 characters) to describe the behavior of the
* refresh operation to the user. This is typically shown in the status line when this subscriber is refreshed
* in the background. When refreshed in the foreground, only the long task name is shown.
*
* @return the short task name to show in the status line.
*/
protected String getShortTaskName() {
return TeamUIMessages.Participant_synchronizing;
}
/**
* Returns the long task name to describe the behavior of the
* refresh operation to the user. This is typically shown in the status line when this subscriber is refreshed
* in the background.
*
* @return the long task name
* @deprecated use getLongTaskName(IResource[]) instead
*/
@Deprecated
protected String getLongTaskName() {
return TeamUIMessages.Participant_synchronizing;
}
/**
* Returns the long task name to describe the behavior of the
* refresh operation to the user. This is typically shown in the status line when this subscriber is refreshed
* in the background.
* @param resources
* @return the long task name
* @since 3.1
*/
protected String getLongTaskName(IResource[] resources) {
int resourceCount = 0;
if (getResources().length == resources.length) {
// Assume that the resources are the same as the roots.
// If we are wrong, the message may no mention the specific resources which is OK
ISynchronizeScope scope = getScope();
if (scope instanceof ResourceScope) {
resourceCount = scope.getRoots().length;
}
} else {
resourceCount = resources.length;
}
if (resourceCount == 1) {
return NLS.bind(TeamUIMessages.Participant_synchronizingMoreDetails, new String[] { getShortName(), resources[0].getFullPath().toString() });
} else if (resourceCount > 1) {
return NLS.bind(TeamUIMessages.Participant_synchronizingResources, new String[] { getShortName(), Integer.toString(resourceCount) });
}
// A resource count of zero means that it is a non-resource scope so we can print the scope name
return NLS.bind(TeamUIMessages.Participant_synchronizingDetails, new String[] { getName() });
}
/**
* This method is invoked before the given configuration is used to
* create the page (see createPage(ISynchronizePageConfiguration)
).
* The configuration would have been initialized by
* initializeConfiguration(ISynchronizePageConfiguration)
* but may have also been tailored further. This method gives the participant
* a chance to validate those changes before the page is created.
*
* @param configuration the page configuration that is about to be used to create a page.
*/
protected void validateConfiguration(ISynchronizePageConfiguration configuration) {
// Do nothing by default
}
/**
* Subclasses must call this method to initialize the participant. Typically this
* method is called in {@link #init(String, IMemento)}. This method will initialize
* the sync info collector.
*
* @param subscriber the subscriber to associate with this participant.
*/
protected void setSubscriber(Subscriber subscriber) {
if (scope == null) {
scope = new WorkspaceScope();
}
collector = new SubscriberSyncInfoCollector(subscriber, scope.getRoots());
// listen for global ignore changes
TeamUI.addPropertyChangeListener(this);
// Start collecting changes
collector.start();
// Start the refresh now that a subscriber has been added
SubscriberRefreshSchedule schedule = getRefreshSchedule();
if(schedule.isEnabled()) {
getRefreshSchedule().startJob();
}
}
/**
* Provide a filter that is used to filter the contents of the sync info set for the participant. Normally, all out-of-sync
* resources from the subscriber will be included in the participant's set. However, a filter can be used to exclude
* some of these out-of-sync resources, if desired.
*
* Subclasses can invoke this method any time after setSubscriber
has been invoked.
*