Skip to main content
summaryrefslogtreecommitdiffstats
blob: 3b458856faddf5cef9fbbc797a9872ce156645f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.core.subscribers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.sync.IRemoteResource;

/**
 * A TeamSubscriber provides synchronization between local resources and a remote location 
 * that is used to share those resources. 
 * 
 * [Note: How can we allow the refresh() operation to optimize the sync calculation based
 * on the currently configured compare criteria?]
 */
abstract public class TeamSubscriber {
	
	private List listeners = new ArrayList(1);
	
	/**
	 * Return the unique id that identified this subscriber.
	 */
	abstract public QualifiedName getId();
	
	/**
	 * Return the name of this subscription, in a format that is suitable for 
	 * display to an end user.
	 * 
	 * @return String representing the name of this subscription. 
	 */
	abstract public String getName();
	
	/**
	 * Return the description of this subscription, in a format that is suitable for 
	 * display to an end user. The description should contain enough details to
	 * understand the connection type of this subscriber. 
	 * 
	 * @return String representing the description of this subscription. 
	 */
	abstract public String getDescription();
	
	/**
	 * Returns <code>true</code> if this resource is supervised by this subscriber. 
	 * A supervised resource is one for which this subscriber maintains the synchronization
	 * state.  Returns <code>false</code> in all other cases. 
	 * 
	 * @return <code>true</code> if this resource is supervised, and 
	 *   <code>false</code> otherwise
	 */
	abstract public boolean isSupervised(IResource resource) throws TeamException;
	
	/**
	 * Returns all non-transient member resources of the given resource.
	 * The result will include entries for resources that exist 
	 * either in the workspace or are implicated in an incoming change. 
	 * Returns an empty list if the given resource exists neither in
	 * the workspace nor in the corresponding team stream, or if the
	 * given resource is transient.
	 * <p>
	 * This is a fast operation; the repository is not contacted.
	 * </p>
	 * <p>
	 * [Issue1 : Is there any filtering on the members? Just the ones
	 *  that changed in some way, or *every member*?
	 * ]</p>
	 *
	 * @param resource the resource
	 * @return a list of member resources
	 * @exception CoreException if this request fails. Reasons include:
	 */
	abstract  public IResource[] members(IResource resource) throws TeamException;
	
	/**
	 * Returns the list of  root resources this subscriber considers for synchronization.
	 * A client should call this method first then can safely call <code>members</code>
	 * to navigate the resources managed by this subscriber.
	 *   
	 * @return a list of resources
	 * @throws TeamException
	 */
	abstract public IResource[] roots();
	
	/**
	 * Returns a handle to the remote resource corresponding to the given
	 * resource, or <code>null</code> if there is no corresponding resource
	 * edition. 
	 * <p> 
	 * This is a fast operation; the repository is not contacted.
	 * </p>
	 *
	 * @param resource the resource
	 * @return a server resource
	 * @exception CoreException if this request fails. Reasons include:
	 * <ul>
	 * <li>???</li>
	 * </ul>
	 */	
	abstract public IRemoteResource getRemoteResource(IResource resource) throws TeamException;
	
	/**
	 * Returns synchronization info for the given resource, or
	 * <code>null</code> if there is no synchronization info
	 * because the subscriber does not apply to this resource.
	 * <p>
	 * Note that sync info may be returned for non-existing
	 * or for resources which have no corresponding remote resource.
	 * </p>
	 * <p> 
	 * This method may take some time; it depends on the comparison criteria
	 * that is used to calculate the synchronization state (e.g. using content
	 * or only timestamps).
	 * </p>
	 *
	 * @param resource the resource of interest
	 * @return sync info
	 */
	abstract public SyncInfo getSyncInfo(IResource resource, IProgressMonitor monitor) throws TeamException; 
	
	/** 
	 * Refreshes the resource hierarchy from the given resources and their 
	 * children (to the specified depth) from the corresponding resources in 
	 * the remote location. Resources are ignored in the following cases:
	 * <ul>
	 * <li>if they do not exist either in the workspace
	 * or in the corresponding remote location</li>
	 * <li>if the given resource is marked as derived (see IResource#isDerived())</li>
	 * <li>if the given resource is not managed by this subscriber</li>
	 * <li>if the given resource is a closed project (they are ineligible for synchronization)</li>
	 * <p>
	 * Typical synchronization operations use the statuses computed by this method 
	 * as the basis for determining what to do.  It is possible for the actual sync 
	 * status of the resource to have changed since the current local sync status 
	 * was refreshed.  Operations typically skip resources with stale sync information.  
	 * The chances of stale information being used can be reduced by running this
	 * method (where feasible) before doing other operations.  Note that this will
	 * of course affect performance.  
	 * </p>
	 * <p>
	 * The depth parameter controls whether refreshing is performed
	 * on just the given resource (depth=<code>DEPTH_ZERO</code>), 
	 * the resource and its children (depth=<code>DEPTH_ONE</code>),
	 * or recursively to the resource and all its descendents (depth=<code>DEPTH_INFINITE</code>).
	 * Use depth <code>DEPTH_ONE</code>, rather than depth 
	 * <code>DEPTH_ZERO</code>, to ensure that new members of a project
	 * or folder are detected.
	 * </p>
	 * <p>
	 * This method might change resources; any changes will be reported
	 * in a subsequent resource change event indicating changes to server sync 
	 * status.
	 * </p>
	 * <p>
	 * This method contacts the server and is therefore long-running; 
	 * progress and cancellation are provided by the given progress monitor.
	 * </p>
	 *
	 * @param resources the resources
	 * @param depth valid values are <code>DEPTH_ZERO</code>, 
	 *  <code>DEPTH_ONE</code>, or <code>DEPTH_INFINITE</code>
	 * @param monitor progress monitor, or <code>null</code> if progress
	 *    reporting and cancellation are not desired
	 * @return status with code <code>OK</code> if there were no problems;
	 *     otherwise a description (possibly a multi-status) consisting of
	 *     low-severity warnings or informational messages. 
	 * @exception CoreException if this method fails. Reasons include:
	 * <ul>
	 * <li> The server could not be contacted.</li>
	 * </ul>
	 */
	abstract public void refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException;
	
	/**
	 * Returns the list of available comparison criteria supported by this subscriber.
	 */
	abstract public ComparisonCriteria[] getComparisonCriterias();
	
	/**
	 * Returns the comparison criteria that will be used by the sync info created by
	 * this subscriber.
	 */
	abstract public ComparisonCriteria getCurrentComparisonCriteria();
	
	/**
	 * Set the current comparison criteria to the one defined by the given id. An exception is
	 * thrown if the id is not suported by this subscriber.
	 */
	abstract public void setCurrentComparisonCriteria(String id) throws TeamException;

	/**
	 * Answers <code>true</code> if the base tree is maintained by this subscriber. If the base
	 * tree is not considered than the subscriber can be considered as not supported three-way
	 * comparisons. Instead comparisons are made between the local and remote only without
	 * consideration for the base.
	 */
	abstract public boolean isThreeWay();
	
	/**
	 * Adds a listener to this team subscriber. 
	 * Has no effect if an identical listener is already registered.
	 * <p>
	 * Team resource change listeners are informed about state changes 
	 * that affect the resources supervised by this subscriber.</p>
	 * 
	 * @param listener a team resource change listener
	 */
	public void addListener(ITeamResourceChangeListener listener) {
		synchronized (listeners) {
			if(! listeners.contains(listener)) {
				listeners.add(listener);
			}
		}
	}

	/**
	 * Removes a listener previously registered with this team subscriber.
	 * Has no affect if an identical listener is not registered.
	 * 
	 * @param listener a team resource change listener
	 */	
	public void removeListener(ITeamResourceChangeListener listener) {
		synchronized (listeners) {
			listeners.remove(listener);
		}
	}
	
	/**
	 * Fires a team resource change event to all registered listeners
	 * Only listeners registered at the time this method is called are notified.
	 * Listener notification makes use of an ISafeRunnable to ensure that
	 * client exceptions do not effect the notification to other clients.
	 */
	protected void fireTeamResourceChange(final TeamDelta[] deltas) {
		ITeamResourceChangeListener[] allListeners;
		// Copy the listener list so we're not calling client code while synchronized
		synchronized(listeners) {
			allListeners = (ITeamResourceChangeListener[]) listeners.toArray(new ITeamResourceChangeListener[listeners.size()]);
		}
		// Notify the listeners safely so all will receive notification
		for (int i = 0; i < allListeners.length; i++) {
			final ITeamResourceChangeListener listener = allListeners[i];
			Platform.run(new ISafeRunnable() {
				public void handleException(Throwable exception) {
					// don't log the exception....it is already being logged in Platform#run
				}
				public void run() throws Exception {
					listener.teamResourceChanged(deltas);
					
				}
			});
		}
	}

	/**
	 * Return an array of all out-of-sync resources (getKind() != 0) that occur 
	 * under the given resources to the specified depth. The purpose of this method is
	 * to provide subscribers a means of optimizing the determination of 
	 * all out-of-sync out-of-sync descendants of a set of resources. 
	 * <p>
	 * A return value of an empty array indicates that there are no out-of-sync resources
	 * supervised by the subscriber. A return of <code>null</code> indicates that the
	 * subscriber does not support this operation in an optimized fashion. In this case,
	 * the caller can determine the out-of-sync resources by traversing the resource
	 * structure form the roots of the subscriber (@see <code>getRoots()</code>).</p>
	 * 
	 * @param resources
	 * @param depth
	 * @param monitor
	 * @return
	 */
	public SyncInfo[] getAllOutOfSync(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
		return null;
	}
	
	/**
	 * Return true if the receiver is equal to object.
	 * @return true if object is the same class as the receiver and has the same ID
	 */
	public boolean equals(Object object) {
		if (object instanceof TeamSubscriber) {
			TeamSubscriber subscriber = (TeamSubscriber) object;
			return getId().equals(subscriber.getId());
		}
		return super.equals(object);
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	public int hashCode() {
		return getId().hashCode();
	}
}

Back to the top