001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.provisional.views.markers;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Collection;
015: import java.util.Iterator;
016:
017: import org.eclipse.core.resources.IResourceChangeEvent;
018: import org.eclipse.core.resources.IResourceChangeListener;
019: import org.eclipse.core.resources.ResourcesPlugin;
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.core.runtime.SubProgressMonitor;
024: import org.eclipse.core.runtime.jobs.Job;
025: import org.eclipse.ui.PlatformUI;
026: import org.eclipse.ui.internal.provisional.views.markers.api.MarkerItem;
027: import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
028: import org.eclipse.ui.views.markers.internal.MarkerGroup;
029: import org.eclipse.ui.views.markers.internal.MarkerMessages;
030: import org.eclipse.ui.views.markers.internal.MarkerType;
031:
032: /**
033: * The CachedMarkerBuilder is the object that generates the list of markers from
034: * a generator.
035: *
036: * @since 3.4
037: *
038: */
039: public class CachedMarkerBuilder {
040:
041: private static final MarkerCategory[] EMPTY_CATEGORY_ARRAY = new MarkerCategory[0];
042:
043: private static final int SHORT_DELAY = 100;// The 100 ms short delay for
044: // scheduling
045:
046: private static final int TIME_OUT = 30000;// The 30s long delay to run
047:
048: private boolean building = true;// Start with nothing until we have
049: // something
050:
051: private MarkerCategory[] categories;
052: private MarkerMap currentMap = null;
053:
054: MarkerContentGenerator generator; // The MarkerContentGenerator we are
055: // building for
056:
057: private Job markerProcessJob;
058:
059: private IWorkbenchSiteProgressService progressService;
060:
061: private Job updateJob;
062:
063: // without a builder update
064:
065: /**
066: * Create a new instance of the receiver. Update using the updateJob.
067: *
068: * @param contentGenerator
069: */
070: CachedMarkerBuilder(MarkerContentGenerator contentGenerator) {
071: this .generator = contentGenerator;
072: createMarkerProcessJob();
073: // Hook up to the resource changes after all widget have been created
074: ResourcesPlugin.getWorkspace().addResourceChangeListener(
075: getUpdateListener(),
076: IResourceChangeEvent.POST_CHANGE
077: | IResourceChangeEvent.PRE_BUILD
078: | IResourceChangeEvent.POST_BUILD);
079: }
080:
081: /**
082: * Build all of the markers in the receiver.
083: *
084: * @param monitor
085: */
086: public void buildAllMarkers(IProgressMonitor monitor) {
087: building = true;
088: MarkerMap newMarkers;
089: try {
090:
091: monitor.beginTask(MarkerMessages.MarkerView_19, 60);
092:
093: monitor
094: .subTask(MarkerMessages.MarkerView_waiting_on_changes);
095:
096: if (monitor.isCanceled())
097: return;
098:
099: monitor
100: .subTask(MarkerMessages.MarkerView_searching_for_markers);
101: SubProgressMonitor subMonitor = new SubProgressMonitor(
102: monitor, 10);
103: newMarkers = generator.generateFilteredMarkers(subMonitor);
104:
105: if (monitor.isCanceled())
106: return;
107:
108: Arrays
109: .sort(newMarkers.toArray(), generator
110: .getComparator());
111:
112: monitor.worked(30);
113:
114: if (newMarkers.getSize() == 0) {
115: categories = EMPTY_CATEGORY_ARRAY;
116: currentMap = newMarkers;
117: monitor.done();
118: return;
119: }
120:
121: monitor.subTask(MarkerMessages.MarkerView_queueing_updates);
122:
123: if (monitor.isCanceled())
124: return;
125:
126: if (generator.isShowingHierarchy()) {
127: MarkerCategory[] newCategories = buildHierarchy(
128: newMarkers, 0, newMarkers.getSize() - 1, 0);
129: if (monitor.isCanceled())
130: return;
131: categories = newCategories;
132: }
133:
134: currentMap = newMarkers;
135: monitor.done();
136: } finally {
137: building = false;
138: }
139:
140: }
141:
142: /**
143: * Break the marker up into categories
144: *
145: * @param markers
146: * @param start
147: * the start index in the markers
148: * @param end
149: * the last index to check
150: * @param sortIndex -
151: * the parent of the field
152: * @return MarkerCategory[] or <code>null</code> if we are at the bottom
153: * of the tree
154: */
155: MarkerCategory[] buildHierarchy(MarkerMap markers, int start,
156: int end, int sortIndex) {
157: MarkerComparator sorter = generator.getComparator();
158:
159: if (sortIndex > 0) {
160: return null;// Are we out of categories?
161: }
162:
163: Collection categories = new ArrayList();
164:
165: Object previous = null;
166: int categoryStart = start;
167:
168: Object[] elements = markers.toArray();
169:
170: for (int i = start; i <= end; i++) {
171:
172: if (previous != null) {
173: // Are we at a category boundary?
174: if (sorter.compareCategory(previous, elements[i]) != 0) {
175: categories
176: .add(new MarkerCategory(
177: this ,
178: categoryStart,
179: i - 1,
180: generator
181: .getCategoryGroup()
182: .getMarkerField()
183: .getValue(
184: markers
185: .elementAt(categoryStart))));
186: categoryStart = i;
187: }
188: }
189: previous = elements[i];
190:
191: }
192:
193: if (end >= categoryStart) {
194: categories
195: .add(new MarkerCategory(
196: this ,
197: categoryStart,
198: end,
199: generator
200: .getCategoryGroup()
201: .getMarkerField()
202: .getValue(
203: markers
204: .elementAt(categoryStart))));
205: }
206:
207: MarkerCategory[] nodes = new MarkerCategory[categories.size()];
208: categories.toArray(nodes);
209: return nodes;
210:
211: }
212:
213: /**
214: * Cancel the pending jobs in the receiver.
215: */
216: private void cancelJobs() {
217: markerProcessJob.cancel();
218: updateJob.cancel();
219: }
220:
221: /**
222: * Create the job for updating the markers.
223: */
224: private void createMarkerProcessJob() {
225: markerProcessJob = new Job(
226: MarkerMessages.MarkerView_processUpdates) {
227: /*
228: * (non-Javadoc)
229: *
230: * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
231: */
232: public boolean belongsTo(Object family) {
233: return MarkerContentGenerator.CACHE_UPDATE_FAMILY == family;
234: }
235:
236: /*
237: * (non-Javadoc)
238: *
239: * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
240: */
241: protected IStatus run(IProgressMonitor monitor) {
242: updateJob.cancel();
243: buildAllMarkers(monitor);
244: updateJob.schedule();
245: return Status.OK_STATUS;
246: }
247:
248: /*
249: * (non-Javadoc)
250: *
251: * @see org.eclipse.ui.progress.WorkbenchJob#shouldRun()
252: */
253: public boolean shouldRun() {
254: // Do not run if the change came in before there is a viewer
255: return PlatformUI.isWorkbenchRunning();
256: }
257: };
258: markerProcessJob.setSystem(true);
259:
260: }
261:
262: /**
263: * Return the categories for the receiver.
264: *
265: * @return MarkerCategory[] or <code>null</code> if there are no
266: * categories.
267: */
268: public MarkerCategory[] getCategories() {
269: if (building) {
270: return null;
271: }
272: return categories;
273: }
274:
275: /**
276: * Return the {@link MarkerGroup} being used for categorisation.
277: *
278: * @return {@link MarkerGroup} or <code>null</code>.
279: */
280: MarkerGroup getCategoryGroup() {
281: return generator.getCategoryGroup();
282: }
283:
284: /**
285: * Return the elements in the adapter.
286: *
287: * @return Object[]
288: */
289: public MarkerItem[] getElements() {
290:
291: if (currentMap == null) {// First time?
292: scheduleMarkerUpdate();
293: building = true;
294: }
295: if (building) {
296: return MarkerSupportInternalUtilities.EMPTY_MARKER_ITEM_ARRAY;
297: }
298: if (generator.isShowingHierarchy() && categories != null) {
299: return categories;
300: }
301: return currentMap.toArray();
302: }
303:
304: /**
305: * Return the generator for the receiver.
306: *
307: * @return MarkerContentGenerator
308: */
309: MarkerContentGenerator getGenerator() {
310: return generator;
311: }
312:
313: /**
314: * Get the raw list of marker entries.
315: *
316: * @return list of MarkerEntry
317: */
318: public MarkerEntry[] getMarkerEntries() {
319: return currentMap.toArray();
320: }
321:
322: /**
323: * Return the total number of markers.
324: * @return int
325: */
326: public int getTotalMarkerCount() {
327: MarkerItem[] elements = getElements();
328: if (elements.length == 0 || elements[0].isConcrete())
329: return elements.length;
330: int length = 0;
331: for (int i = 0; i < elements.length; i++) {
332: length += elements[i].getChildren().length;
333: }
334:
335: return length;
336: }
337:
338: /**
339: * Return the resource listener for the builder
340: *
341: * @return IResourceChangeListener
342: */
343: private IResourceChangeListener getUpdateListener() {
344: return new IResourceChangeListener() {
345:
346: /**
347: * Returns whether or not the given even contains marker deltas for
348: * this view.
349: *
350: * @param event
351: * the resource change event
352: * @return <code>true</code> if the event contains at least one
353: * relevant marker delta
354: * @since 3.3
355: */
356: private boolean hasMarkerDelta(IResourceChangeEvent event) {
357: Iterator markerTypes = generator.getMarkerTypes()
358: .iterator();
359: while (markerTypes.hasNext()) {
360: MarkerType type = (MarkerType) markerTypes.next();
361:
362: if (event.findMarkerDeltas(type.getId(), true).length > 0)
363: return true;
364:
365: }
366: return false;
367: }
368:
369: /*
370: * (non-Javadoc)
371: *
372: * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
373: */
374: public void resourceChanged(IResourceChangeEvent event) {
375: if (!hasMarkerDelta(event))
376: return;
377:
378: if (event.getType() == IResourceChangeEvent.POST_BUILD) {
379: scheduleMarkerUpdate();
380: return;
381: }
382:
383: // After 30 seconds do updates anyways
384: if (progressService == null)
385: markerProcessJob.schedule(TIME_OUT);
386: else
387: progressService
388: .schedule(markerProcessJob, TIME_OUT);
389:
390: }
391:
392: };
393: }
394:
395: /**
396: * Return the current list of markers.
397: *
398: * @return MarkerMap
399: */
400: public MarkerMap getVisibleMarkers() {
401: if (currentMap == null) {// First time?
402: scheduleMarkerUpdate();
403: building = true;
404: }
405: if (building) {
406: return MarkerMap.EMPTY_MAP;
407: }
408: return currentMap;
409: }
410:
411: /**
412: * Return whether or not the receiver has markers without scheduling
413: * anything if it doesn't.
414: *
415: * @return boolean <code>true</code> if the markers have not been
416: * calculated.
417: */
418: public boolean hasNoMarkers() {
419: return currentMap == null;
420: }
421:
422: /**
423: * Return whether or not the receiver is building.
424: *
425: * @return boolean
426: */
427: boolean isBuilding() {
428: return building;
429: }
430:
431: /**
432: * Schedule an update of the markers with a delay.
433: *
434: */
435: void scheduleMarkerUpdate() {
436: cancelJobs();
437: progressService.schedule(markerProcessJob, SHORT_DELAY);
438: }
439:
440: /**
441: * Set the category group.
442: * @param group {@link MarkerGroup} or <code>null</code>.
443: */
444: void setCategoryGroup(MarkerGroup group) {
445: generator.setCategoryGroup(group);
446: scheduleMarkerUpdate();
447:
448: }
449:
450: /**
451: * The filters have changed. Set the list and regenerate the receiver.
452: *
453: * @param filters
454: */
455: void setFilters(Collection filters) {
456: generator.setFilters(filters);
457: scheduleMarkerUpdate();
458:
459: }
460:
461: /**
462: * Set the generator and update the contents.
463: *
464: * @param generator
465: */
466: void setGenerator(MarkerContentGenerator generator) {
467: this .generator = generator;
468: scheduleMarkerUpdate();
469: }
470:
471: /**
472: * Set the progress service for the receiver.
473: *
474: * @param service
475: */
476: public void setProgressService(IWorkbenchSiteProgressService service) {
477: progressService = service;
478:
479: }
480:
481: /**
482: * Set the updateJob for the receiver.
483: *
484: * @param job
485: */
486: public void setUpdateJob(Job job) {
487: updateJob = job;
488:
489: }
490:
491: /**
492: * Toggle the enabled state of the filter for group.
493: *
494: * @param group
495: */
496: public void toggleFilter(MarkerFieldFilterGroup group) {
497: getGenerator().toggleFilter(group);
498: scheduleMarkerUpdate();
499:
500: }
501:
502: /**
503: * Update the receiver for a change in selection.
504: *
505: * @param newElements
506: */
507: public void updateForNewSelection(Object[] newElements) {
508: if (generator.updateNeeded(newElements)) {
509: generator.updateFocusElements(newElements);
510: scheduleMarkerUpdate();
511: }
512:
513: }
514:
515: }
|