001: /*******************************************************************************
002: * Copyright (c) 2003, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.progress;
011:
012: import java.util.Collection;
013: import java.util.HashSet;
014: import java.util.Iterator;
015:
016: import org.eclipse.core.runtime.IProgressMonitor;
017: import org.eclipse.core.runtime.IStatus;
018: import org.eclipse.core.runtime.Status;
019: import org.eclipse.core.runtime.jobs.Job;
020: import org.eclipse.ui.IWorkbenchPreferenceConstants;
021: import org.eclipse.ui.PlatformUI;
022: import org.eclipse.ui.internal.util.PrefUtil;
023: import org.eclipse.ui.progress.WorkbenchJob;
024:
025: /**
026: * The ProgressViewUpdater is the singleton that updates viewers.
027: */
028: class ProgressViewUpdater implements IJobProgressManagerListener {
029:
030: private static ProgressViewUpdater singleton;
031:
032: private IProgressUpdateCollector[] collectors;
033:
034: Job updateJob;
035:
036: UpdatesInfo currentInfo = new UpdatesInfo();
037:
038: Object updateLock = new Object();
039:
040: boolean debug;
041:
042: /**
043: * The UpdatesInfo is a private class for keeping track of the updates
044: * required.
045: */
046: class UpdatesInfo {
047:
048: Collection additions = new HashSet();
049:
050: Collection deletions = new HashSet();
051:
052: Collection refreshes = new HashSet();
053:
054: boolean updateAll = false;
055:
056: private UpdatesInfo() {
057: //Create a new instance of the info
058: }
059:
060: /**
061: * Add an add update
062: *
063: * @param addition
064: */
065: void add(JobTreeElement addition) {
066: additions.add(addition);
067: }
068:
069: /**
070: * Add a remove update
071: *
072: * @param removal
073: */
074: void remove(JobTreeElement removal) {
075: deletions.add(removal);
076: }
077:
078: /**
079: * Add a refresh update
080: *
081: * @param refresh
082: */
083: void refresh(JobTreeElement refresh) {
084: refreshes.add(refresh);
085: }
086:
087: /**
088: * Reset the caches after completion of an update.
089: */
090: void reset() {
091: additions.clear();
092: deletions.clear();
093: refreshes.clear();
094: updateAll = false;
095: }
096:
097: void processForUpdate() {
098: HashSet staleAdditions = new HashSet();
099:
100: Iterator additionsIterator = additions.iterator();
101: while (additionsIterator.hasNext()) {
102: JobTreeElement treeElement = (JobTreeElement) additionsIterator
103: .next();
104: if (!treeElement.isActive()) {
105: if (deletions.contains(treeElement)) {
106: staleAdditions.add(treeElement);
107: }
108: }
109: }
110:
111: additions.removeAll(staleAdditions);
112:
113: HashSet obsoleteRefresh = new HashSet();
114: Iterator refreshIterator = refreshes.iterator();
115: while (refreshIterator.hasNext()) {
116: JobTreeElement treeElement = (JobTreeElement) refreshIterator
117: .next();
118: if (deletions.contains(treeElement)
119: || additions.contains(treeElement)) {
120: obsoleteRefresh.add(treeElement);
121: }
122:
123: //Also check for groups that are being added
124: Object parent = treeElement.getParent();
125: if (parent != null
126: && (deletions.contains(parent) || additions
127: .contains(parent))) {
128: obsoleteRefresh.add(treeElement);
129: }
130:
131: if (!treeElement.isActive()) {
132: //If it is done then delete it
133: obsoleteRefresh.add(treeElement);
134: deletions.add(treeElement);
135: }
136: }
137:
138: refreshes.removeAll(obsoleteRefresh);
139:
140: }
141: }
142:
143: /**
144: * Return a new instance of the receiver.
145: *
146: * @return ProgressViewUpdater
147: */
148: static ProgressViewUpdater getSingleton() {
149: if (singleton == null) {
150: singleton = new ProgressViewUpdater();
151: }
152: return singleton;
153: }
154:
155: /**
156: * Return whether or not there is a singleton for updates to avoid creating
157: * extra listeners.
158: *
159: * @return boolean <code>true</code> if there is already
160: * a singleton
161: */
162: static boolean hasSingleton() {
163: return singleton != null;
164: }
165:
166: static void clearSingleton() {
167: if (singleton != null) {
168: ProgressManager.getInstance().removeListener(singleton);
169: }
170: singleton = null;
171: }
172:
173: /**
174: * Create a new instance of the receiver.
175: */
176: private ProgressViewUpdater() {
177: createUpdateJob();
178: collectors = new IProgressUpdateCollector[0];
179: ProgressManager.getInstance().addListener(this );
180: debug = PrefUtil.getAPIPreferenceStore().getBoolean(
181: IWorkbenchPreferenceConstants.SHOW_SYSTEM_JOBS);
182: }
183:
184: /**
185: * Add the new collector to the list of collectors.
186: *
187: * @param newCollector
188: */
189: void addCollector(IProgressUpdateCollector newCollector) {
190: IProgressUpdateCollector[] newCollectors = new IProgressUpdateCollector[collectors.length + 1];
191: System.arraycopy(collectors, 0, newCollectors, 0,
192: collectors.length);
193: newCollectors[collectors.length] = newCollector;
194: collectors = newCollectors;
195: }
196:
197: /**
198: * Remove the collector from the list of collectors.
199: *
200: * @param provider
201: */
202: void removeCollector(IProgressUpdateCollector provider) {
203: HashSet newCollectors = new HashSet();
204: for (int i = 0; i < collectors.length; i++) {
205: if (!collectors[i].equals(provider)) {
206: newCollectors.add(collectors[i]);
207: }
208: }
209: IProgressUpdateCollector[] newArray = new IProgressUpdateCollector[newCollectors
210: .size()];
211: newCollectors.toArray(newArray);
212: collectors = newArray;
213: //Remove ourselves if there is nothing to update
214: if (collectors.length == 0) {
215: clearSingleton();
216: }
217: }
218:
219: /**
220: * Schedule an update.
221: */
222: void scheduleUpdate() {
223: if (PlatformUI.isWorkbenchRunning()) {
224: //Add in a 100ms delay so as to keep priority low
225: updateJob.schedule(100);
226: }
227: }
228:
229: /**
230: * Create the update job that handles the updatesInfo.
231: */
232: private void createUpdateJob() {
233: updateJob = new WorkbenchJob(
234: ProgressMessages.ProgressContentProvider_UpdateProgressJob) {
235: /*
236: * (non-Javadoc)
237: *
238: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
239: */
240: public IStatus runInUIThread(IProgressMonitor monitor) {
241:
242: //Abort the job if there isn't anything
243: if (collectors.length == 0) {
244: return Status.CANCEL_STATUS;
245: }
246:
247: if (currentInfo.updateAll) {
248: synchronized (updateLock) {
249: currentInfo.reset();
250: }
251: for (int i = 0; i < collectors.length; i++) {
252: collectors[i].refresh();
253: }
254:
255: } else {
256: //Lock while getting local copies of the caches.
257: Object[] updateItems;
258: Object[] additionItems;
259: Object[] deletionItems;
260: synchronized (updateLock) {
261: currentInfo.processForUpdate();
262:
263: updateItems = currentInfo.refreshes.toArray();
264: additionItems = currentInfo.additions.toArray();
265: deletionItems = currentInfo.deletions.toArray();
266:
267: currentInfo.reset();
268: }
269:
270: for (int v = 0; v < collectors.length; v++) {
271: IProgressUpdateCollector collector = collectors[v];
272:
273: if (updateItems.length > 0) {
274: collector.refresh(updateItems);
275: }
276: if (additionItems.length > 0) {
277: collector.add(additionItems);
278: }
279: if (deletionItems.length > 0) {
280: collector.remove(deletionItems);
281: }
282: }
283: }
284:
285: return Status.OK_STATUS;
286: }
287: };
288: updateJob.setSystem(true);
289: updateJob.setPriority(Job.DECORATE);
290: updateJob.setProperty(
291: ProgressManagerUtil.INFRASTRUCTURE_PROPERTY,
292: new Object());
293:
294: }
295:
296: /**
297: * Get the updates info that we are using in the receiver.
298: *
299: * @return Returns the currentInfo.
300: */
301: UpdatesInfo getCurrentInfo() {
302: return currentInfo;
303: }
304:
305: /**
306: * Refresh the supplied JobInfo.
307: * @param info
308: */
309: public void refresh(JobInfo info) {
310:
311: if (isUpdateJob(info.getJob())) {
312: return;
313: }
314:
315: synchronized (updateLock) {
316: currentInfo.refresh(info);
317: GroupInfo group = info.getGroupInfo();
318: if (group != null) {
319: currentInfo.refresh(group);
320: }
321: }
322: //Add in a 100ms delay so as to keep priority low
323: scheduleUpdate();
324:
325: }
326:
327: /* (non-Javadoc)
328: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshJobInfo(org.eclipse.ui.internal.progress.JobInfo)
329: */
330: public void refreshJobInfo(JobInfo info) {
331:
332: if (isUpdateJob(info.getJob())) {
333: return;
334: }
335:
336: synchronized (updateLock) {
337: currentInfo.refresh(info);
338: }
339: //Add in a 100ms delay so as to keep priority low
340: scheduleUpdate();
341:
342: }
343:
344: /* (non-Javadoc)
345: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshGroup(org.eclipse.ui.internal.progress.GroupInfo)
346: */
347: public void refreshGroup(GroupInfo info) {
348: synchronized (updateLock) {
349: currentInfo.refresh(info);
350: }
351: //Add in a 100ms delay so as to keep priority low
352: scheduleUpdate();
353:
354: }
355:
356: /* (non-Javadoc)
357: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#addGroup(org.eclipse.ui.internal.progress.GroupInfo)
358: */
359: public void addGroup(GroupInfo info) {
360:
361: synchronized (updateLock) {
362: currentInfo.add(info);
363: }
364: scheduleUpdate();
365:
366: }
367:
368: /*
369: * (non-Javadoc)
370: *
371: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshAll()
372: */
373: public void refreshAll() {
374:
375: synchronized (updateLock) {
376: currentInfo.updateAll = true;
377: }
378:
379: //Add in a 100ms delay so as to keep priority low
380: scheduleUpdate();
381:
382: }
383:
384: /*
385: * (non-Javadoc)
386: *
387: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#add(org.eclipse.ui.internal.progress.JobInfo)
388: */
389: public void addJob(JobInfo info) {
390:
391: if (isUpdateJob(info.getJob())) {
392: return;
393: }
394:
395: synchronized (updateLock) {
396: GroupInfo group = info.getGroupInfo();
397:
398: if (group == null) {
399: currentInfo.add(info);
400: } else {
401: currentInfo.refresh(group);
402: }
403: }
404: scheduleUpdate();
405:
406: }
407:
408: /*
409: * (non-Javadoc)
410: *
411: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#removeJob(org.eclipse.ui.internal.progress.JobInfo)
412: */
413: public void removeJob(JobInfo info) {
414:
415: if (isUpdateJob(info.getJob())) {
416: return;
417: }
418:
419: synchronized (updateLock) {
420: GroupInfo group = info.getGroupInfo();
421: if (group == null) {
422: currentInfo.remove(info);
423: } else {
424: group.removeJobInfo(info);
425: currentInfo.refresh(group);
426: }
427: }
428: scheduleUpdate();
429: }
430:
431: /* (non-Javadoc)
432: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#removeGroup(org.eclipse.ui.internal.progress.GroupInfo)
433: */
434: public void removeGroup(GroupInfo group) {
435: synchronized (updateLock) {
436: currentInfo.remove(group);
437: }
438: scheduleUpdate();
439:
440: }
441:
442: /*
443: * (non-Javadoc)
444: *
445: * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#showsDebug()
446: */
447: public boolean showsDebug() {
448: return debug;
449: }
450:
451: /**
452: * Return whether or not this is the update job. This is used to determine
453: * if a final refresh is required.
454: *
455: * @param job
456: * @return boolean <code>true</true> if this is the
457: * update job
458: */
459: boolean isUpdateJob(Job job) {
460: return job.equals(updateJob);
461: }
462: }
|