Skip to main content
summaryrefslogtreecommitdiffstats
blob: 67a33a05b36d9b7ecaf1a56ddb18bb5c25c1e118 (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.internal.ui.synchronize.views;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Control;
import org.eclipse.team.core.subscribers.*;
import org.eclipse.team.ui.synchronize.SyncInfoDiffNode;

/**
 * This class provides the contents for a StructuredViewer using a SyncSet as the model
 */
public abstract class SyncSetContentProvider implements IStructuredContentProvider, ISyncSetChangedListener {
	
	protected Viewer viewer;
	
	// parents who need a label update accumulated while handling sync set changes
	private Set parentsToUpdate = new HashSet();
	
	protected SyncInfoSet getSyncInfoSet(Object input) {
		if (input == null) {
			return null;
		}
		if(input instanceof SyncInfoDiffNode) {
			return ((SyncInfoDiffNode)input).getSyncInfoSet();
		}
		if(input instanceof SyncInfoSet) {
			return (SyncInfoSet)input;
		}
		return null;
	}
	
	protected SyncInfoSet getSyncInfoSet() {
		if(viewer == null || viewer.getControl().isDisposed()) {
			return null;	
		}
		return getSyncInfoSet(viewer.getInput());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
	 */
	public void inputChanged(Viewer v, Object oldInput, Object newInput) {
		
		this.viewer = v;
		SyncInfoSet oldSyncSet = getSyncInfoSet(oldInput);
		SyncInfoSet newSyncSet = getSyncInfoSet(newInput);

		if (oldSyncSet != newSyncSet) {
			if (oldSyncSet != null) {
				oldSyncSet.removeSyncSetChangedListener(this);
			}
			if (newSyncSet != null) {
				newSyncSet.addSyncSetChangedListener(this);
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
	 */
	public abstract Object[] getElements(Object inputElement);
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
	 */
	public void dispose() {
		SyncInfoSet input = getSyncInfoSet();
		if (input != null) {
			input.removeSyncSetChangedListener(this);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.team.ccvs.syncviews.views.ISyncSetChangedListener#syncSetChanged(org.eclipse.team.ccvs.syncviews.views.SyncSetChangedEvent)
	 */
	public void syncSetChanged(final ISyncInfoSetChangeEvent event, IProgressMonitor monitor) {
		final Control ctrl = viewer.getControl();
		if (ctrl != null && !ctrl.isDisposed()) {
			ctrl.getDisplay().asyncExec(new Runnable() {
				public void run() {
					if (!ctrl.isDisposed()) {
						BusyIndicator.showWhile(ctrl.getDisplay(), new Runnable() {
							public void run() {
								handleSyncSetChanges(event);
							}
						});
					}
				}
			});
		}
	}
	
	/**
	 * Update the viewer with the sync-set changes, aditions and removals
	 * in the given event. This method is invoked from within the UI thread.
	 * @param event
	 */
	protected void handleSyncSetChanges(ISyncInfoSetChangeEvent event) {
		viewer.getControl().setRedraw(false);
		if (event.isReset()) {
			// On a reset, refresh the entire view
			((StructuredViewer) viewer).refresh();
		} else {
			handleResourceChanges(event);
			handleResourceRemovals(event);
			handleResourceAdditions(event);
			updateParentLabels();
		}
		viewer.getControl().setRedraw(true);
	}
	
	/**
	 * Update the viewer for the sync set changes in the provided event.
	 * This method is invoked by <code>handleSyncSetChanges</code>.
	 * Subclasses may override.
	 * @param event
	 * @see #handleSyncSetChanges(SyncSetChangedEvent)
	 */
	protected void handleResourceChanges(ISyncInfoSetChangeEvent event) {
		// Refresh the viewer for each changed resource
		SyncInfo[] infos = event.getChangedResources();
		for (int i = 0; i < infos.length; i++) {
			IResource local = infos[i].getLocal();
			((StructuredViewer) viewer).refresh(getModelObject(local), true);
			updateParentLabels(local);
		}
	}
	
	/**
	 * Update the viewer for the sync set removals in the provided event.
	 * This method is invoked by <code>handleSyncSetChanges</code>.
	 * Subclasses may override.
	 * @param event
	 */
	protected void handleResourceRemovals(ISyncInfoSetChangeEvent event) {
		// Update the viewer for each removed resource
		IResource[] removed = event.getRemovedRoots();
		for (int i = 0; i < removed.length; i++) {
			IResource resource = removed[i];
			((StructuredViewer) viewer).refresh(getModelObject(resource));
			updateParentLabels(resource);
		}
	}
	
	/**
	 * Update the viewer for the sync set additions in the provided event.
	 * This method is invoked by <code>handleSyncSetChanges</code>.
	 * Subclasses may override.
	 * @param event
	 */
	protected void handleResourceAdditions(ISyncInfoSetChangeEvent event) {
		// Update the viewer for each of the added resource's parents
		IResource[] added = event.getAddedRoots();
		for (int i = 0; i < added.length; i++) {
			IResource resource = added[i];
			((StructuredViewer) viewer).refresh(getModelObject(resource.getParent()));
			updateParentLabels(resource);
		}
	}
	
	public StructuredViewer getViewer() {
		return (StructuredViewer)viewer;
	}
	
	/**
	 * Return the children of the given container who are either out-of-sync or contain
	 * out-of-sync resources.
	 */
	public Object[] members(IResource resource) {
		IResource[] resources = getSyncInfoSet().members(resource);
		Object[] result = new Object[resources.length];
		for (int i = 0; i < resources.length; i++) {
			IResource child = resources[i];
			result[i] = getModelObject(child);
		}
		return result;
	}
	
	/**
	 * Return the SyncInfo for the given model object that was returned by 
	 * SyncSet#members(IResource). If syncSet is null, then the 
	 * sync info will also be null.
	 * 
	 * @param element
	 * @return
	 */
	public static SyncInfo getSyncInfo(Object element) {
		if (element instanceof SyncInfo) {
			return ((SyncInfo) element);
		}  else if (element instanceof SyncInfoDiffNode) {
			SyncInfoDiffNode syncResource = (SyncInfoDiffNode)element;
			return syncResource.getSyncInfo();
		}
		throw new NullPointerException();
	}
	
	/**
	 * Return the IResource for the given model object that was returned by 
	 * SyncSet#members(IResource). Return <code>null</code> if the given
	 * object does not have a corresponding IResource.
	 * 
	 * @param element
	 * @return
	 */
	public static IResource getResource(Object obj) {
		if (obj instanceof SyncInfo) {
			return ((SyncInfo) obj).getLocal();
		}  else if (obj instanceof SyncInfoDiffNode) {
			return ((SyncInfoDiffNode)obj).getResource();
		}
		return null;
	}
	
	/**
	 * Return the sync kind for the given model object that was returned by 
	 * SyncSet#members(IResource). If syncSet is null, then the 
	 * sync kind for SyncContainers will always be 0.
	 * 
	 * @param element
	 * @return
	 */
	public static int getSyncKind(Object element) {
		SyncInfo info = getSyncInfo(element);
		if (info != null) {
			return info.getKind();
		}
		return SyncInfo.IN_SYNC;
	}
	
	/**
	 * Get the model object (SyncSet, SyncInfo or SyncContainer) that is the
	 * parent of the given model object.
	 * 
	 * @param syncSet
	 * @param object
	 * @return
	 */
	public Object getParent(Object object) {
		IResource resource = getResource(object);
		if (resource == null) return null;
		IContainer parent = resource.getParent();
		return getModelObject(parent);
	}
	
	/**
	 * Return the model object for the given IResource.
	 * @param resource
	 */
	public Object getModelObject(IResource resource) {
		return new SyncInfoDiffNode(getSyncInfoSet(), resource);
	}
	
	protected Object[] getModelObjects(IResource[] resources) {
		Object[] result = new Object[resources.length];
		for (int i = 0; i < resources.length; i++) {
			result[i] = getModelObject(resources[i]);
		}
		return result;
	}
	
	/**
	 * Forces the viewer to update the labels for parents whose children have changed
	 * during this round of sync set changes.
	 */
	protected void updateParentLabels() {
		try {
			getViewer().update(
					parentsToUpdate.toArray(new Object[parentsToUpdate.size()]),
					null 
			);	
		} finally {
			parentsToUpdate.clear();
		}
	}
	
	/**
	 * Forces the viewer to update the labels for parents of this element. This
	 * can be useful when parents labels include information about their children
	 * that needs updating when a child changes.
	 * <p>
	 * This method should only be called while processing sync set changes.
	 * Changed parents are accumulated and updated at the end of the change processing
	 */
	protected void updateParentLabels(IResource resource) {
		IResource parent = resource.getParent();
		while(parent.getType() != IResource.ROOT) {
			parentsToUpdate.add(getModelObject(parent));
			parent = parent.getParent();
		}
	}
}

Back to the top