| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.views.markers; |
| |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.resources.mapping.ResourceMapping; |
| import org.eclipse.core.resources.mapping.ResourceMappingContext; |
| import org.eclipse.core.resources.mapping.ResourceTraversal; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.WorkbenchException; |
| import org.eclipse.ui.XMLMemento; |
| import org.eclipse.ui.internal.ide.IDEInternalPreferences; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.Policy; |
| import org.eclipse.ui.internal.ide.StatusUtil; |
| import org.eclipse.ui.progress.IWorkbenchSiteProgressService; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.eclipse.ui.views.markers.FilterConfigurationArea; |
| import org.eclipse.ui.views.markers.MarkerField; |
| import org.eclipse.ui.views.markers.MarkerItem; |
| import org.eclipse.ui.views.markers.internal.MarkerGroup; |
| import org.eclipse.ui.views.markers.internal.MarkerMessages; |
| import org.eclipse.ui.views.markers.internal.MarkerSupportRegistry; |
| import org.eclipse.ui.views.markers.internal.MarkerType; |
| import org.eclipse.ui.views.markers.internal.ProblemFilter; |
| import org.eclipse.ui.views.markers.internal.Util; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * The CachedMarkerBuilder is the object that generates the list of markers from |
| * a generator. |
| * |
| * @since 3.4 |
| * |
| */ |
| public class CachedMarkerBuilder { |
| |
| private static final MarkerCategory[] EMPTY_CATEGORY_ARRAY = new MarkerCategory[0]; |
| private static final MarkerEntry[] EMPTY_ENTRY_ARRAY = new MarkerEntry[0]; |
| |
| private static final int SHORT_DELAY = 100;// The 100 ms short delay for |
| // scheduling |
| |
| private static final int TIME_OUT = 30000;// The 30s long delay to run |
| |
| private static final String TAG_FILTERS_SECTION = "filterGroups"; //$NON-NLS-1$ |
| private static final String TAG_GROUP_ENTRY = "filterGroup"; //$NON-NLS-1$ |
| private static final String TAG_AND = "andFilters"; //$NON-NLS-1$ |
| private static final String TAG_CATEGORY_GROUP = "categoryGroup"; //$NON-NLS-1$ |
| private static final String TAG_COLUMN_VISIBILITY = "visible"; //$NON-NLS-1$ |
| private static final String VALUE_NONE = "none"; //$NON-NLS-1$ |
| private static final String TAG_LEGACY_FILTER_ENTRY = "filter"; //$NON-NLS-1$ |
| private static final Integer[] EMPTY_MARKER_COUNTS = { new Integer(0), |
| new Integer(0), new Integer(0) }; |
| |
| private boolean building = true;// Start with nothing until we have |
| // something |
| |
| private MarkerCategory[] categories; |
| private MarkerMap currentMap = null; |
| |
| private MarkerContentGenerator generator; // The MarkerContentGenerator we |
| // are |
| // building for |
| |
| private Job markerProcessJob; |
| |
| private IWorkbenchSiteProgressService progressService; |
| |
| private Job updateJob; |
| |
| private MarkerGroup categoryGroup; |
| |
| private Collection enabledFilters; |
| private Collection filters; |
| private IResource[] focusResources = MarkerSupportInternalUtilities.EMPTY_RESOURCE_ARRAY; |
| private MarkerField[] visibleFields; |
| |
| private boolean andFilters = false; |
| private MarkerComparator comparator; |
| private IMemento memento; |
| private String viewId; |
| |
| // The time the build started. A -1 indicates no build in progress. |
| private long preBuildTime = -1; |
| private IResourceChangeListener resourceListener; |
| private IPropertyChangeListener preferenceListener; |
| |
| // without a builder update |
| |
| /** |
| * Create a new instance of the receiver. Update using the updateJob. |
| * |
| * @param contentGenerator |
| * @param id |
| * id of the view we are building for |
| * @param memento |
| * the memento to restore from |
| */ |
| public CachedMarkerBuilder(MarkerContentGenerator contentGenerator, |
| String id, IMemento memento) { |
| this.generator = contentGenerator; |
| this.viewId = id; |
| initialiseVisibleFields(memento); |
| initializePreferenceListener(); |
| |
| this.memento = memento; |
| if (memento == null) |
| setDefaultCategoryGroup(contentGenerator); |
| else { |
| // Set up the category group if it has been set or set a default. |
| String categoryGroupID = memento.getString(TAG_CATEGORY_GROUP); |
| if (categoryGroupID == null) |
| setDefaultCategoryGroup(contentGenerator); |
| else { |
| if (categoryGroupID.equals(VALUE_NONE)) |
| this.categoryGroup = null; |
| else { |
| MarkerGroup newGroup = contentGenerator |
| .getMarkerGroup(categoryGroupID); |
| if (newGroup == null) |
| setDefaultCategoryGroup(contentGenerator); |
| else |
| this.categoryGroup = newGroup; |
| } |
| } |
| } |
| |
| createMarkerProcessJob(); |
| resourceListener = getUpdateListener(); |
| // Hook up to the resource changes after all widget have been created |
| ResourcesPlugin.getWorkspace().addResourceChangeListener( |
| resourceListener, |
| IResourceChangeEvent.POST_CHANGE |
| | IResourceChangeEvent.PRE_BUILD |
| | IResourceChangeEvent.POST_BUILD); |
| |
| } |
| |
| /** |
| * Add the resources in resourceMapping to the resourceCollection |
| * |
| * @param resourceCollection |
| * @param resourceMapping |
| */ |
| private void addResources(Collection resourceCollection, |
| ResourceMapping resourceMapping) { |
| |
| try { |
| ResourceTraversal[] traversals = resourceMapping.getTraversals( |
| ResourceMappingContext.LOCAL_CONTEXT, |
| new NullProgressMonitor()); |
| for (int i = 0; i < traversals.length; i++) { |
| ResourceTraversal traversal = traversals[i]; |
| IResource[] result = traversal.getResources(); |
| for (int j = 0; j < result.length; j++) { |
| resourceCollection.add(result[j]); |
| } |
| } |
| } catch (CoreException e) { |
| Policy.handle(e); |
| } |
| |
| } |
| |
| /** |
| * Return whether the filters are being ANDed or ORed. |
| * |
| * @return boolean |
| */ |
| boolean andFilters() { |
| return andFilters; |
| } |
| |
| /** |
| * Build all of the markers in the receiver. |
| * |
| * @param monitor |
| */ |
| void buildAllMarkers(IProgressMonitor monitor) { |
| building = true; |
| MarkerMap newMarkers; |
| try { |
| |
| monitor.beginTask(MarkerMessages.MarkerView_19, 60); |
| |
| monitor.subTask(MarkerMessages.MarkerView_waiting_on_changes); |
| |
| if (monitor.isCanceled()) |
| return; |
| |
| monitor.subTask(MarkerMessages.MarkerView_searching_for_markers); |
| SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 10); |
| newMarkers = generator.generateFilteredMarkers(subMonitor, |
| andFilters(), focusResources, getEnabledFilters()); |
| |
| if (monitor.isCanceled()) |
| return; |
| |
| sortAndMakeCategories(new SubProgressMonitor(monitor, 30), |
| newMarkers); |
| monitor.done(); |
| } finally { |
| building = false; |
| } |
| |
| } |
| |
| /** |
| * Break the marker up into categories |
| * |
| * @param markers |
| * @param start |
| * the start index in the markers |
| * @param end |
| * the last index to check |
| * @param sortIndex - |
| * the parent of the field |
| * @return MarkerCategory[] or <code>null</code> if we are at the bottom |
| * of the tree |
| */ |
| MarkerCategory[] buildHierarchy(MarkerMap markers, int start, int end, |
| int sortIndex) { |
| MarkerComparator sorter = getComparator(); |
| |
| if (sortIndex > 0) { |
| return null;// Are we out of categories? |
| } |
| |
| Collection categories = new ArrayList(); |
| |
| Object previous = null; |
| int categoryStart = start; |
| |
| Object[] elements = markers.toArray(); |
| |
| for (int i = start; i <= end; i++) { |
| |
| if (previous != null) { |
| // Are we at a category boundary? |
| if (sorter.compareCategory(previous, elements[i]) != 0) { |
| categories |
| .add(new MarkerCategory( |
| this, |
| categoryStart, |
| i - 1, |
| getCategoryGroup() |
| .getMarkerField() |
| .getValue( |
| markers |
| .elementAt(categoryStart)))); |
| categoryStart = i; |
| } |
| } |
| previous = elements[i]; |
| |
| } |
| |
| if (end >= categoryStart) { |
| categories.add(new MarkerCategory(this, categoryStart, end, |
| getCategoryGroup().getMarkerField().getValue( |
| markers.elementAt(categoryStart)))); |
| } |
| |
| MarkerCategory[] nodes = new MarkerCategory[categories.size()]; |
| categories.toArray(nodes); |
| return nodes; |
| |
| } |
| |
| /** |
| * Cancel the pending jobs in the receiver. |
| */ |
| private void cancelJobs() { |
| markerProcessJob.cancel(); |
| updateJob.cancel(); |
| } |
| |
| /** |
| * Return a collection of all of the configuration fields for this generator |
| * |
| * @return Collection of {@link FilterConfigurationArea} |
| */ |
| Collection createFilterConfigurationFields() { |
| Collection result = new ArrayList(); |
| for (int i = 0; i < visibleFields.length; i++) { |
| FilterConfigurationArea area = MarkerSupportInternalUtilities |
| .generateFilterArea(visibleFields[i]); |
| if (area != null) |
| result.add(area); |
| |
| } |
| return result; |
| } |
| |
| /** |
| * Create the job for updating the markers. |
| */ |
| private void createMarkerProcessJob() { |
| markerProcessJob = new Job(MarkerMessages.MarkerView_processUpdates) { |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object) |
| */ |
| public boolean belongsTo(Object family) { |
| return MarkerContentGenerator.CACHE_UPDATE_FAMILY == family; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| protected IStatus run(IProgressMonitor monitor) { |
| updateJob.cancel(); |
| buildAllMarkers(monitor); |
| updateJob.schedule(); |
| return Status.OK_STATUS; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.progress.WorkbenchJob#shouldRun() |
| */ |
| public boolean shouldRun() { |
| |
| // Hold off while everything is active |
| if (preBuildTime > 0 |
| && System.currentTimeMillis() - preBuildTime < TIME_OUT) |
| return false; |
| |
| // Clear it if we are past the time out. |
| preBuildTime = -1; |
| // Do not run if the change came in before there is a viewer |
| return IDEWorkbenchPlugin.getDefault().getBundle().getState() == Bundle.ACTIVE; |
| } |
| }; |
| markerProcessJob.setSystem(true); |
| |
| } |
| |
| /** |
| * Disable all of the filters in the receiver. |
| */ |
| void disableAllFilters() { |
| Collection allFilters = getEnabledFilters(); |
| Iterator enabled = allFilters.iterator(); |
| while (enabled.hasNext()) { |
| MarkerFieldFilterGroup group = (MarkerFieldFilterGroup) enabled |
| .next(); |
| group.setEnabled(false); |
| } |
| allFilters.clear(); |
| writeFiltersPreference(); |
| scheduleMarkerUpdate(); |
| |
| } |
| |
| /** |
| * Dispose any listeners in the receiver. |
| */ |
| void dispose() { |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener( |
| resourceListener); |
| IDEWorkbenchPlugin.getDefault().getPreferenceStore() |
| .removePropertyChangeListener(preferenceListener); |
| |
| } |
| |
| /** |
| * Return all of the filters for the receiver. |
| * |
| * @return Collection of MarkerFieldFilterGroup |
| */ |
| Collection getAllFilters() { |
| if (filters == null) { |
| filters = new ArrayList(); |
| IConfigurationElement[] filterReferences = generator |
| .getFilterReferences(); |
| for (int i = 0; i < filterReferences.length; i++) { |
| filters.add(new MarkerFieldFilterGroup(filterReferences[i], |
| this)); |
| } |
| |
| // Honour the deprecated problemFilters |
| if (viewId.equals(IPageLayout.ID_PROBLEM_VIEW)) { |
| Iterator problemFilters = MarkerSupportRegistry.getInstance() |
| .getRegisteredFilters().iterator(); |
| while (problemFilters.hasNext()) |
| filters.add(new CompatibilityMarkerFieldFilterGroup( |
| (ProblemFilter) problemFilters.next(), this)); |
| } |
| |
| // Apply the last settings |
| loadFiltersPreference(); |
| |
| } |
| return filters; |
| } |
| |
| /** |
| * Return the categories for the receiver. |
| * |
| * @return MarkerCategory[] or <code>null</code> if there are no |
| * categories. |
| */ |
| public MarkerCategory[] getCategories() { |
| if (building) { |
| return null; |
| } |
| return categories; |
| } |
| |
| /** |
| * Return the group used to generate categories. |
| * |
| * @return MarkerGroup or <code>null</code>. |
| */ |
| MarkerGroup getCategoryGroup() { |
| |
| return categoryGroup; |
| } |
| |
| /** |
| * Return a new instance of the receiver with the field |
| * |
| * @return MarkerComparator |
| */ |
| MarkerComparator getComparator() { |
| |
| if (comparator == null) { |
| MarkerField field = null; |
| if (getCategoryGroup() != null) |
| field = getCategoryGroup().getMarkerField(); |
| comparator = new MarkerComparator(field, generator.getAllFields()); |
| comparator.restore(this.memento); |
| } |
| return comparator; |
| } |
| |
| /** |
| * Return the elements in the adapter. |
| * |
| * @return MarkerSupportItem[] |
| */ |
| MarkerSupportItem[] getElements() { |
| |
| if (refreshingMarkers()) { |
| return MarkerSupportInternalUtilities.EMPTY_MARKER_ITEM_ARRAY; |
| } |
| if (isShowingHierarchy() && categories != null) { |
| return categories; |
| } |
| return currentMap.toArray(); |
| } |
| |
| /** |
| * Return the currently enabled filters. |
| * |
| * @return Collection of MarkerFieldFilterGroup |
| */ |
| Collection getEnabledFilters() { |
| if (enabledFilters == null) { |
| enabledFilters = new HashSet(); |
| Iterator filtersIterator = getAllFilters().iterator(); |
| while (filtersIterator.hasNext()) { |
| MarkerFieldFilterGroup next = (MarkerFieldFilterGroup) filtersIterator |
| .next(); |
| if (next.isEnabled()) |
| enabledFilters.add(next); |
| } |
| } |
| return enabledFilters; |
| } |
| |
| /** |
| * Return the generator for the receiver. |
| * |
| * @return MarkerContentGenerator |
| */ |
| MarkerContentGenerator getGenerator() { |
| return generator; |
| } |
| |
| /** |
| * Return the fields not being shown currently. |
| * |
| * @return Object[] |
| */ |
| Object[] getHiddenFields() { |
| MarkerField[] all = getGenerator().getAllFields(); |
| MarkerField[] visible = getVisibleFields(); |
| |
| Collection hidden = new HashSet(); |
| for (int i = 0; i < all.length; i++) { |
| hidden.add(all[i]); |
| } |
| for (int i = 0; i < visible.length; i++) { |
| hidden.remove(visible[i]); |
| } |
| return hidden.toArray(); |
| } |
| |
| /** |
| * Get the name of the filters preference for the receiver, |
| * |
| * @return String |
| */ |
| private String getLegacyFiltersPreferenceName() { |
| |
| if (viewId.equals(IPageLayout.ID_BOOKMARKS)) |
| return IDEInternalPreferences.BOOKMARKS_FILTERS; |
| if (viewId.equals(IPageLayout.ID_TASK_LIST)) |
| return IDEInternalPreferences.TASKS_FILTERS; |
| return IDEInternalPreferences.PROBLEMS_FILTERS; |
| |
| } |
| |
| /** |
| * Get the counts of errors,warnings and others in that order. |
| * |
| * @return Integer[] |
| */ |
| Integer[] getMarkerCounts() { |
| if (currentMap == null) |
| return EMPTY_MARKER_COUNTS; |
| return currentMap.getMarkerCounts(); |
| } |
| |
| /** |
| * Get the raw list of marker entries. |
| * |
| * @return list of MarkerEntry |
| */ |
| MarkerEntry[] getMarkerEntries() { |
| if (refreshingMarkers()) |
| return EMPTY_ENTRY_ARRAY; |
| |
| return currentMap.toArray(); |
| } |
| |
| /** |
| * Get the MarkerItem that matches marker. |
| * |
| * @param marker |
| * @return MarkerItem or <code>null<code> if it cannot be found |
| */ |
| MarkerItem getMarkerItem(IMarker marker) { |
| if (refreshingMarkers()) |
| return null; |
| return currentMap.getMarkerItem(marker); |
| } |
| |
| /** |
| * Get the name for the preferences for the receiver. |
| * |
| * @return String |
| */ |
| private String getMementoPreferenceName() { |
| return getClass().getName() + viewId; |
| } |
| |
| /** |
| * Return the primary sort field |
| * |
| * @return MarkerField |
| */ |
| MarkerField getPrimarySortField() { |
| return getComparator().getPrimarySortField(); |
| } |
| |
| /** |
| * Get the sort direction of field |
| * |
| * @param field |
| * @return int one of {@link MarkerComparator#ASCENDING} or |
| * {@link MarkerComparator#DESCENDING} |
| */ |
| int getSortDirection(MarkerField field) { |
| if (getComparator().descendingFields.contains(field)) |
| return MarkerComparator.DESCENDING; |
| return MarkerComparator.ASCENDING; |
| } |
| |
| /** |
| * Return the total number of markers. |
| * |
| * @return int |
| */ |
| int getTotalMarkerCount() { |
| MarkerSupportItem[] elements = getElements(); |
| if (elements.length == 0 || elements[0].isConcrete()) |
| return elements.length; |
| int length = 0; |
| for (int i = 0; i < elements.length; i++) { |
| length += elements[i].getChildren().length; |
| } |
| |
| return length; |
| } |
| |
| /** |
| * Return the resource listener for the builder |
| * |
| * @return IResourceChangeListener |
| */ |
| private IResourceChangeListener getUpdateListener() { |
| return new IResourceChangeListener() { |
| |
| /** |
| * Returns whether or not the given even contains marker deltas for |
| * this view. |
| * |
| * @param event |
| * the resource change event |
| * @return <code>true</code> if the event contains at least one |
| * relevant marker delta |
| * @since 3.3 |
| */ |
| private boolean hasMarkerDelta(IResourceChangeEvent event) { |
| Iterator markerTypes = generator.getMarkerTypes().iterator(); |
| while (markerTypes.hasNext()) { |
| MarkerType type = (MarkerType) markerTypes.next(); |
| |
| if (event.findMarkerDeltas(type.getId(), true).length > 0) |
| return true; |
| |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) |
| */ |
| public void resourceChanged(IResourceChangeEvent event) { |
| if (!hasMarkerDelta(event)) |
| return; |
| |
| if (event.getType() == IResourceChangeEvent.PRE_BUILD) { |
| preBuild(); |
| return; |
| } |
| |
| if (event.getType() == IResourceChangeEvent.POST_BUILD) { |
| postBuild(); |
| scheduleMarkerUpdate(); |
| return; |
| } |
| |
| // After 30 seconds do updates anyways |
| if (progressService == null) |
| markerProcessJob.schedule(TIME_OUT); |
| else |
| progressService.schedule(markerProcessJob, TIME_OUT); |
| |
| } |
| |
| }; |
| } |
| |
| /** |
| * Get the fields that this content generator is displaying. |
| * |
| * @return {@link MarkerField}[] |
| */ |
| MarkerField[] getVisibleFields() { |
| return visibleFields; |
| } |
| |
| /** |
| * Return whether or not the receiver has markers without scheduling |
| * anything if it doesn't. |
| * |
| * @return boolean <code>true</code> if the markers have not been |
| * calculated. |
| */ |
| boolean hasNoMarkers() { |
| return currentMap == null; |
| } |
| |
| /** |
| * Initialize the visible fields based on the initial settings or the |
| * contents of the {@link IMemento} |
| * |
| * @param memento |
| * IMemento |
| */ |
| private void initialiseVisibleFields(IMemento memento) { |
| |
| if (memento == null |
| || memento.getChildren(TAG_COLUMN_VISIBILITY).length == 0) { |
| MarkerField[] initialFields = getGenerator().getInitialVisible(); |
| |
| visibleFields = new MarkerField[initialFields.length]; |
| System.arraycopy(initialFields, 0, visibleFields, 0, |
| initialFields.length); |
| return; |
| } |
| |
| IMemento[] visible = memento.getChildren(TAG_COLUMN_VISIBILITY); |
| Collection newVisible = new ArrayList(); |
| |
| MarkerField[] all = getGenerator().getAllFields(); |
| Hashtable allTable = new Hashtable(); |
| |
| for (int i = 0; i < all.length; i++) { |
| allTable.put(all[i].getConfigurationElement().getAttribute( |
| MarkerSupportInternalUtilities.ATTRIBUTE_ID), all[i]); |
| } |
| |
| for (int i = 0; i < visible.length; i++) { |
| String key = visible[i].getID(); |
| if (allTable.containsKey(key)) { |
| newVisible.add(allTable.get(key)); |
| } |
| } |
| |
| visibleFields = new MarkerField[newVisible.size()]; |
| newVisible.toArray(visibleFields); |
| } |
| |
| /** |
| * Create a preference listener for any preference updates. |
| */ |
| private void initializePreferenceListener() { |
| preferenceListener = new IPropertyChangeListener() { |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) |
| */ |
| public void propertyChange(PropertyChangeEvent event) { |
| if (event.getProperty().equals(getMementoPreferenceName())) { |
| rebuildFilters(); |
| } |
| |
| } |
| }; |
| IDEWorkbenchPlugin.getDefault().getPreferenceStore() |
| .addPropertyChangeListener(preferenceListener); |
| |
| } |
| |
| /** |
| * Return whether or not the receiver is building. |
| * |
| * @return boolean |
| */ |
| public boolean isBuilding() { |
| return building; |
| } |
| |
| /** |
| * Return whether or not we are showing a hierarchy,. |
| * |
| * @return <code>true</code> if a hierarchy is being shown. |
| */ |
| boolean isShowingHierarchy() { |
| return categoryGroup != null; |
| } |
| |
| /** |
| * Load the settings from the memento. |
| * |
| * @param memento |
| */ |
| private void loadFilterSettings(IMemento memento) { |
| |
| if (memento == null) |
| return; |
| |
| Boolean andValue = memento.getBoolean(TAG_AND); |
| if (andValue != null) |
| setAndFilters(andValue.booleanValue()); |
| IMemento children[] = memento.getChildren(TAG_GROUP_ENTRY); |
| |
| for (int i = 0; i < children.length; i++) { |
| IMemento child = children[i]; |
| String id = child.getString(IMemento.TAG_ID); |
| if (id == null) |
| continue; |
| if (!loadGroupWithID(child, id)) |
| |
| // Did not find a match must have been added by the user |
| loadUserFilter(child); |
| } |
| |
| } |
| |
| /** |
| * Load the filters defined in memento string. |
| * |
| * @param mementoString |
| */ |
| private void loadFiltersFrom(String mementoString) { |
| if (mementoString.equals(IPreferenceStore.STRING_DEFAULT_DEFAULT)) |
| return; |
| |
| try { |
| loadFilterSettings(XMLMemento.createReadRoot(new StringReader( |
| mementoString))); |
| } catch (WorkbenchException e) { |
| StatusManager.getManager().handle(e.getStatus()); |
| } |
| } |
| |
| /** |
| * Load the filters preference. |
| */ |
| private void loadFiltersPreference() { |
| |
| loadFiltersFrom(IDEWorkbenchPlugin.getDefault().getPreferenceStore() |
| .getString(getMementoPreferenceName())); |
| |
| String legacyFilters = getLegacyFiltersPreferenceName(); |
| String migrationPreference = legacyFilters |
| + MarkerSupportInternalUtilities.MIGRATE_PREFERENCE_CONSTANT; |
| |
| if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean( |
| migrationPreference)) |
| return;// Already migrated |
| |
| // Load any defined in a pre 3.4 workbench |
| loadLegacyFiltersFrom(IDEWorkbenchPlugin.getDefault() |
| .getPreferenceStore().getString(legacyFilters)); |
| |
| // Mark as migrated |
| IDEWorkbenchPlugin.getDefault().getPreferenceStore().setValue( |
| migrationPreference, true); |
| } |
| |
| /** |
| * Load the group with id from the child if there is a matching system group |
| * registered. |
| * |
| * @param child |
| * @param id |
| * @return <code>true</code> if a matching group was found |
| */ |
| private boolean loadGroupWithID(IMemento child, String id) { |
| Iterator groups = getAllFilters().iterator(); |
| |
| while (groups.hasNext()) { |
| MarkerFieldFilterGroup group = (MarkerFieldFilterGroup) groups |
| .next(); |
| if (id.equals(group.getID())) { |
| group.loadSettings(child); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Load the legacy filter into the system. |
| * |
| * @param child |
| */ |
| private void loadLegacyFilter(IMemento child) { |
| MarkerFieldFilterGroup newGroup = new MarkerFieldFilterGroup(null, this); |
| newGroup.legacyLoadSettings(child); |
| getAllFilters().add(newGroup); |
| |
| } |
| |
| /** |
| * Load the pre-3.4 filters. |
| * |
| * @param mementoString |
| */ |
| private void loadLegacyFiltersFrom(String mementoString) { |
| |
| if (mementoString.equals(IPreferenceStore.STRING_DEFAULT_DEFAULT)) |
| return; |
| IMemento memento; |
| try { |
| memento = XMLMemento |
| .createReadRoot(new StringReader(mementoString)); |
| restoreLegacyFilters(memento); |
| } catch (WorkbenchException e) { |
| StatusManager.getManager().handle(e.getStatus()); |
| return; |
| } |
| |
| } |
| |
| /** |
| * Load the user supplied filter |
| * |
| * @param child |
| */ |
| private void loadUserFilter(IMemento child) { |
| MarkerFieldFilterGroup newGroup = new MarkerFieldFilterGroup(null, this); |
| newGroup.loadSettings(child); |
| getAllFilters().add(newGroup); |
| } |
| |
| /** |
| * Post build has happened. Let it all run. |
| */ |
| protected void postBuild() { |
| preBuildTime = -1; |
| |
| } |
| |
| /** |
| * We are in a pre build state. Do not update until the post build happens. |
| */ |
| protected void preBuild() { |
| preBuildTime = System.currentTimeMillis(); |
| |
| } |
| |
| /** |
| * Rebuild the list of filters |
| */ |
| protected void rebuildFilters() { |
| filters = null; |
| enabledFilters = null; |
| scheduleMarkerUpdate(); |
| |
| } |
| |
| /** |
| * Refresh the sort order and categories of the receiver. |
| * |
| * @param service |
| * The service to run the operation in. |
| */ |
| void refreshContents(IWorkbenchSiteProgressService service) { |
| try { |
| service.busyCursorWhile(new IRunnableWithProgress() { |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public void run(IProgressMonitor monitor) { |
| |
| // Let the build finish before trying to sort |
| if (refreshingMarkers()) |
| return; |
| sortAndMakeCategories(monitor, currentMap); |
| } |
| }); |
| } catch (InvocationTargetException e) { |
| StatusManager.getManager().handle( |
| StatusUtil.newStatus(IStatus.ERROR, |
| e.getLocalizedMessage(), e)); |
| } catch (InterruptedException e) { |
| StatusManager.getManager().handle( |
| StatusUtil.newStatus(IStatus.ERROR, |
| e.getLocalizedMessage(), e)); |
| } |
| |
| } |
| |
| /** |
| * Check if the markers are still being built. If so schedule an update. |
| * |
| * @return <code>true</code> if the map is empty. |
| */ |
| private boolean refreshingMarkers() { |
| if (currentMap == null) {// First time? |
| scheduleMarkerUpdate(); |
| return true; |
| } |
| return building; |
| } |
| |
| /** |
| * Restore the pre-3.4 filters. |
| * |
| * @param memento |
| */ |
| private void restoreLegacyFilters(IMemento memento) { |
| |
| IMemento[] sections = null; |
| if (memento != null) |
| sections = memento.getChildren(TAG_LEGACY_FILTER_ENTRY); |
| |
| for (int i = 0; i < sections.length; i++) { |
| IMemento child = sections[i]; |
| String id = child.getString(IMemento.TAG_ID); |
| if (id == null) |
| continue; |
| loadLegacyFilter(child); |
| } |
| |
| } |
| |
| /** |
| * Save the state of the receiver to memento |
| * |
| * @param memento |
| * @param displayedFields - |
| * the currently displayed fields in order |
| */ |
| void saveState(IMemento memento, MarkerField[] displayedFields) { |
| getComparator().saveState(memento); |
| |
| if (categoryGroup == null) |
| memento.putString(TAG_CATEGORY_GROUP, VALUE_NONE); |
| else |
| memento.putString(TAG_CATEGORY_GROUP, getCategoryGroup().getId()); |
| |
| for (int i = 0; i < displayedFields.length; i++) { |
| |
| memento.createChild(TAG_COLUMN_VISIBILITY, displayedFields[i] |
| .getConfigurationElement().getAttribute( |
| MarkerSupportInternalUtilities.ATTRIBUTE_ID)); |
| } |
| } |
| |
| /** |
| * Schedule an update of the markers with a delay. |
| * |
| */ |
| public void scheduleMarkerUpdate() { |
| cancelJobs(); |
| currentMap = null; |
| building = true; |
| if (progressService != null) { |
| progressService.schedule(markerProcessJob, SHORT_DELAY); |
| } else { |
| markerProcessJob.schedule(SHORT_DELAY); |
| } |
| } |
| |
| /** |
| * Set whether the filters are being ANDed or ORed. |
| * |
| * @param and |
| */ |
| void setAndFilters(boolean and) { |
| andFilters = and; |
| } |
| |
| /** |
| * Set the category group. |
| * |
| * @param group |
| * {@link MarkerGroup} or <code>null</code>. |
| */ |
| void setCategoryGroup(MarkerGroup group) { |
| this.categoryGroup = group; |
| if (group == null) |
| getComparator().setCategory(null); |
| else |
| getComparator().setCategory(group.getMarkerField()); |
| scheduleMarkerUpdate(); |
| |
| } |
| |
| /** |
| * Categorise by the default setting for contentGenerator. |
| * |
| * @param contentGenerator |
| */ |
| private void setDefaultCategoryGroup(MarkerContentGenerator contentGenerator) { |
| String categoryName = contentGenerator.getCategoryName(); |
| if (categoryName != null) { |
| MarkerGroup group = contentGenerator.getMarkerGroup(categoryName); |
| if (group != null) |
| categoryGroup = group; |
| } |
| |
| } |
| |
| /** |
| * Set the generator and update the contents. |
| * |
| * @param generator |
| */ |
| void setGenerator(MarkerContentGenerator generator) { |
| this.generator = generator; |
| scheduleMarkerUpdate(); |
| } |
| |
| /** |
| * Set the primary sort field for the receiver. |
| * |
| * @param field |
| */ |
| void setPrimarySortField(MarkerField field) { |
| |
| getComparator().setPrimarySortField(field); |
| |
| } |
| |
| /** |
| * Set the progress service for the receiver. |
| * |
| * @param service |
| */ |
| void setProgressService(IWorkbenchSiteProgressService service) { |
| progressService = service; |
| if (service != null) { |
| service.showBusyForFamily(ResourcesPlugin.FAMILY_MANUAL_BUILD); |
| service.showBusyForFamily(ResourcesPlugin.FAMILY_AUTO_BUILD); |
| service |
| .showBusyForFamily(MarkerContentGenerator.CACHE_UPDATE_FAMILY); |
| } |
| |
| } |
| |
| /** |
| * Set the updateJob for the receiver. |
| * |
| * @param job |
| */ |
| public void setUpdateJob(Job job) { |
| updateJob = job; |
| |
| } |
| |
| /** |
| * Sort the newMarkers and build categories if required. |
| * |
| * @param monitor |
| * @param newMarkers |
| */ |
| void sortAndMakeCategories(IProgressMonitor monitor, MarkerMap newMarkers) { |
| |
| // Allow the keys to get regenerated |
| Arrays.sort(newMarkers.toArray(), getComparator()); |
| |
| monitor.worked(50); |
| |
| if (newMarkers.getSize() == 0) { |
| categories = EMPTY_CATEGORY_ARRAY; |
| currentMap = newMarkers; |
| monitor.done(); |
| return; |
| } |
| |
| monitor.subTask(MarkerMessages.MarkerView_queueing_updates); |
| |
| if (monitor.isCanceled()) |
| return; |
| |
| if (isShowingHierarchy()) { |
| MarkerCategory[] newCategories = buildHierarchy(newMarkers, 0, |
| newMarkers.getSize() - 1, 0); |
| if (monitor.isCanceled()) |
| return; |
| categories = newCategories; |
| } |
| |
| monitor.worked(50); |
| |
| currentMap = newMarkers; |
| currentMap.clearAttributeCaches(); |
| } |
| |
| /** |
| * Add group to the enabled filters. |
| * |
| * @param group |
| */ |
| void toggleFilter(MarkerFieldFilterGroup group) { |
| Collection enabled = getEnabledFilters(); |
| if (enabled.remove(group)) // true if it was present |
| group.setEnabled(false); |
| |
| else { |
| group.setEnabled(true); |
| enabled.add(group); |
| } |
| writeFiltersPreference(); |
| scheduleMarkerUpdate(); |
| } |
| |
| /** |
| * Update the focus resources from list. If there is an update required |
| * return <code>true</code>. This method assumes that there are filters |
| * on resources enabled. |
| * |
| * @param elements |
| */ |
| void updateFocusElements(Object[] elements) { |
| Collection resourceCollection = new ArrayList(); |
| for (int i = 0; i < elements.length; i++) { |
| if (elements[i] instanceof IResource) { |
| resourceCollection.add(elements[i]); |
| } else { |
| addResources(resourceCollection, |
| ((ResourceMapping) elements[i])); |
| } |
| } |
| |
| focusResources = new IResource[resourceCollection.size()]; |
| resourceCollection.toArray(focusResources); |
| } |
| |
| /** |
| * Update the receiver for a change in selection. |
| * |
| * @param newElements |
| */ |
| void updateForNewSelection(Object[] newElements) { |
| if (updateNeeded(newElements)) { |
| updateFocusElements(newElements); |
| scheduleMarkerUpdate(); |
| } |
| |
| } |
| |
| /** |
| * Update the receiver from the dialog. |
| * |
| * @param dialog |
| */ |
| void updateFrom(FiltersConfigurationDialog dialog) { |
| setAndFilters(dialog.andFilters()); |
| filters = dialog.getFilters(); |
| enabledFilters = null; |
| |
| writeFiltersPreference(); |
| scheduleMarkerUpdate(); |
| |
| } |
| |
| /** |
| * Return whether or not the list contains a resource that will require |
| * regeneration. |
| * |
| * @return boolean <code>true</code> if regeneration is required. |
| */ |
| boolean updateNeeded(Object[] newElements) { |
| |
| Iterator filters = getEnabledFilters().iterator(); |
| |
| while (filters.hasNext()) { |
| MarkerFieldFilterGroup filter = (MarkerFieldFilterGroup) filters |
| .next(); |
| |
| int scope = filter.getScope(); |
| if (scope == MarkerFieldFilterGroup.ON_ANY |
| || scope == MarkerFieldFilterGroup.ON_WORKING_SET) |
| continue; |
| |
| if (newElements == null || newElements.length < 1) |
| continue; |
| |
| if (focusResources.length == 0) |
| return true; // We had nothing now we have something |
| |
| if (Arrays.equals(focusResources, newElements)) |
| continue; |
| |
| if (scope == MarkerFieldFilterGroup.ON_ANY_IN_SAME_CONTAINER) { |
| Collection oldProjects = MarkerFieldFilterGroup |
| .getProjectsAsCollection(focusResources); |
| Collection newProjects = MarkerFieldFilterGroup |
| .getProjectsAsCollection(newElements); |
| |
| if (oldProjects.size() == newProjects.size() |
| && newProjects.containsAll(oldProjects)) |
| continue; |
| return true;// Something must be different |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * |
| */ |
| private void writeFiltersPreference() { |
| XMLMemento memento = XMLMemento.createWriteRoot(TAG_FILTERS_SECTION); |
| |
| writeFiltersSettings(memento); |
| |
| StringWriter writer = new StringWriter(); |
| try { |
| memento.save(writer); |
| } catch (IOException e) { |
| IDEWorkbenchPlugin.getDefault().getLog().log(Util.errorStatus(e)); |
| } |
| |
| IDEWorkbenchPlugin.getDefault().getPreferenceStore().putValue( |
| getMementoPreferenceName(), writer.toString()); |
| IDEWorkbenchPlugin.getDefault().savePluginPreferences(); |
| } |
| |
| /** |
| * Write the settings for the filters to the memento. |
| * |
| * @param memento |
| */ |
| private void writeFiltersSettings(XMLMemento memento) { |
| |
| memento.putBoolean(TAG_AND, andFilters); |
| |
| Iterator groups = getAllFilters().iterator(); |
| while (groups.hasNext()) { |
| MarkerFieldFilterGroup group = (MarkerFieldFilterGroup) groups |
| .next(); |
| IMemento child = memento |
| .createChild(TAG_GROUP_ENTRY, group.getID()); |
| group.saveFilterSettings(child); |
| } |
| |
| } |
| |
| /** |
| * Set the visible fields. |
| * |
| * @param visible |
| */ |
| void setVisibleFields(Collection visible) { |
| |
| MarkerField[] newFields = new MarkerField[visible.size()]; |
| visible.toArray(newFields); |
| visibleFields = newFields; |
| |
| } |
| } |