/*******************************************************************************
 * Copyright (c) 2000, 2004 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.ui.views.markers.internal;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.SelectionProviderAction;
import org.eclipse.ui.help.WorkbenchHelp;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MarkerTransfer;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.views.navigator.ShowInNavigatorAction;
import org.eclipse.ui.views.tasklist.ITaskListResourceAdapter;

public abstract class MarkerView extends TableView {

	private static final String WAITING_FOR_WORKSPACE_CHANGES_TO_FINISH = Messages.getString("MarkerView.waiting_on_changes"); //$NON-NLS-1$
	private static final String SEARCHING_FOR_MARKERS = Messages.getString("MarkerView.searching_for_markers"); //$NON-NLS-1$
	private static final String REFRESHING_MARKER_COUNTS = Messages.getString("MarkerView.refreshing_counts"); //$NON-NLS-1$
	private static final String QUEUEING_VIEWER_UPDATES = Messages.getString("MarkerView.queueing_updates"); //$NON-NLS-1$
	private static final String FILTERING_ON_MARKER_LIMIT = Messages.getString("MarkerView.18"); //$NON-NLS-1$
	
	private static final String TAG_SELECTION = "selection"; //$NON-NLS-1$
	private static final String TAG_MARKER = "marker"; //$NON-NLS-1$
	private static final String TAG_RESOURCE = "resource"; //$NON-NLS-1$
	private static final String TAG_ID = "id"; //$NON-NLS-1$
	
	//A private field for keeping track of the number of markers
	//before the busy testing started
	private int preBusyMarkers = 0;

	protected IResource[] focusResources;

	private Clipboard clipboard;

	IResourceChangeListener resourceListener = new IResourceChangeListener() {
		public void resourceChanged(IResourceChangeEvent event) {
			String[] markerTypes = getMarkerTypes();

			boolean refreshNeeded = false;
			
			for (int idx = 0; idx < markerTypes.length; idx++) {					
				IMarkerDelta[] markerDeltas = event.findMarkerDeltas(markerTypes[idx], true);
				List changes = new ArrayList(markerDeltas.length);
				
				examineDelta(markerDeltas, changes);
				
				if (markerDeltas.length != changes.size()) {
					refreshNeeded = true;
				}
				
				MarkerList changed = currentMarkers.findMarkers(changes);
				changed.refresh();
				
				change(changed.asList());
			}
			
			// Refresh everything if markers were added or removed
			if (refreshNeeded) {
				markerCountDirty = true;
				refresh();
			}
		}
	};
	
	protected ActionCopyMarker copyAction;
	protected ActionPasteMarker pasteAction;
	protected SelectionProviderAction revealAction;
	protected SelectionProviderAction openAction;
	protected SelectionProviderAction showInNavigatorAction;
	protected SelectionProviderAction deleteAction;
	protected SelectionProviderAction selectAllAction;
	protected SelectionProviderAction propertiesAction;
	
	private ISelectionListener focusListener = new ISelectionListener() {
		public void selectionChanged(IWorkbenchPart part, ISelection selection) {
			MarkerView.this.focusSelectionChanged(part, selection);
		}
	};
	
	private MarkerList currentMarkers = new MarkerList();
	private int totalMarkers = 0;
	private boolean markerCountDirty = true;

	WorkbenchJob uiJob;
	
	/**
	 * This job is scheduled whenever a filter or resource change occurs. It computes the new
	 * set of markers and schedules a UI Job to cause the changes to be reflected in the UI.
	 */
	
	private RestartableJob refreshJob = null;

	private void internalRefresh(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
		int markerLimit = getMarkerLimit();
		monitor.beginTask(Messages.getString("MarkerView.19"), markerLimit == -1? 60 : 100); //$NON-NLS-1$
		
		haltTableUpdates();
		IJobManager jobMan = Platform.getJobManager();
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		
		try {
			monitor.subTask(WAITING_FOR_WORKSPACE_CHANGES_TO_FINISH);
			
			jobMan.beginRule(root, monitor);
			
			if (monitor.isCanceled()) {
				return;
			}
			
			monitor.subTask(SEARCHING_FOR_MARKERS);
			SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 10);
			currentMarkers = MarkerList.compute(getFilter(), subMonitor, true);
			
			if (monitor.isCanceled()) {
				return;								
			}
			if (markerCountDirty) {
				monitor.subTask(REFRESHING_MARKER_COUNTS);
				totalMarkers = MarkerList.compute(getMarkerTypes()).length;
				markerCountDirty = false;
			}
			
		} catch (CoreException e) {
			throw new InvocationTargetException(e);
		} finally {
			jobMan.endRule(root);
		}
		
		if (monitor.isCanceled()) {
			return;
		}
		
		// Exit immediately if the markers have changed in the meantime.
			
		Collection markers = Arrays.asList(currentMarkers.toArray());
		
		if (markerLimit != -1) {
			
			monitor.subTask(FILTERING_ON_MARKER_LIMIT);
			SubProgressMonitor mon = new SubProgressMonitor(monitor, 40);
			
			markers = SortUtil.getFirst(markers, getSorter(), markerLimit, mon);
			if (monitor.isCanceled()) return;
			currentMarkers = new MarkerList(markers);
		}

		monitor.subTask(QUEUEING_VIEWER_UPDATES);
		
		SubProgressMonitor sub = new SubProgressMonitor(monitor, 50);
		setContents(markers, sub);
		if (monitor.isCanceled()) return;
		
		uiJob.schedule();
		try {
			uiJob.join();
		} catch (InterruptedException e) {
			monitor.done();
		}
		
		monitor.done();
	}
	
	/**
	 * Causes the view to re-sync its contents with the workspace. Note that
	 * changes will be scheduled in a background job, and may not take effect
	 * immediately. 
	 */
	protected void refresh() {
		
		if(uiJob == null)
			createUIJob();
	
		if (refreshJob == null) {
			
			refreshJob = new RestartableJob(
				Messages.format("MarkerView.refreshTitle", new Object[] {getTitle()}),//$NON-NLS-1$
				new IRunnableWithProgress() {
					public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
						internalRefresh(monitor);
					}
				},
				getProgressService());			
		}
		
		refreshJob.restart();		
	}	
	

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
	 */
	public void init(IViewSite site, IMemento memento) throws PartInitException {
		super.init(site, memento);
		getProgressService().showBusyForFamily(ResourcesPlugin.FAMILY_MANUAL_BUILD);
		getProgressService().showBusyForFamily(ResourcesPlugin.FAMILY_AUTO_BUILD);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createPartControl(Composite parent) {
		clipboard = new Clipboard(parent.getDisplay());

		super.createPartControl(parent);

		initDragAndDrop();

		getSite().getPage().addSelectionListener(focusListener);
		focusSelectionChanged(getSite().getPage().getActivePart(), 
				getSite().getPage().getSelection());
		ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceListener);
		refresh();
		
		// Set help on the view itself
		getViewer().getControl().addHelpListener(new HelpListener() {
			/*
			 *  (non-Javadoc)
			 * @see org.eclipse.swt.events.HelpListener#helpRequested(org.eclipse.swt.events.HelpEvent)
			 */
			public void helpRequested(HelpEvent e) {
				String contextId = null;
				// See if there is a context registered for the current selection
				ConcreteMarker marker =
					(ConcreteMarker) ((IStructuredSelection) getViewer().getSelection())
						.getFirstElement();
				if (marker != null) {
					contextId = IDE.getMarkerHelpRegistry().getHelp(marker.getMarker());
				}

				if (contextId == null) {
					contextId = PlatformUI.PLUGIN_ID + ".task_list_view_context";
				}

				WorkbenchHelp.displayHelp(contextId);
			}
		});
	}

	protected void viewerSelectionChanged(IStructuredSelection selection) {
		
			Object[] rawSelection = selection.toArray();
			
			IMarker[] markers = new IMarker[rawSelection.length];
			
			for (int idx = 0; idx < rawSelection.length; idx++) {
				markers[idx] = ((ConcreteMarker)rawSelection[idx]).getMarker();
			}
			
			setSelection(new StructuredSelection(markers));
			
			updateStatusMessage(selection);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#dispose()
	 */
	public void dispose() {
		super.dispose();
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceListener);
		getSite().getPage().removeSelectionListener(focusListener);

		//dispose of selection provider actions
		openAction.dispose();
		copyAction.dispose();
		selectAllAction.dispose();
		deleteAction.dispose();
		revealAction.dispose();
		showInNavigatorAction.dispose();
		propertiesAction.dispose();
		clipboard.dispose();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#createActions()
	 */
	protected void createActions() {
		TableViewer viewer = getViewer();
		revealAction = new ActionRevealMarker(this, getSelectionProvider());
		openAction = new ActionOpenMarker(this, getSelectionProvider());
		copyAction = new ActionCopyMarker(this, getSelectionProvider());
		copyAction.setClipboard(clipboard);
		copyAction.setProperties(getFields());
		pasteAction = new ActionPasteMarker(this, getSelectionProvider());
		pasteAction.setClipboard(clipboard);
		pasteAction.setPastableTypes(getMarkerTypes());
		deleteAction = new ActionRemoveMarker(this, getSelectionProvider());
		selectAllAction = new ActionSelectAll(viewer);
		showInNavigatorAction = new ShowInNavigatorAction(getViewSite().getPage(), getSelectionProvider());
		propertiesAction = new ActionMarkerProperties(this, getSelectionProvider());
		
		super.createActions();
		
		putAction(FILTERS_ACTION_ID, new FiltersAction(this));
	}
	
	protected abstract String[] getMarkerTypes();

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#initToolBar(org.eclipse.jface.action.IToolBarManager)
	 */
	protected void initToolBar(IToolBarManager tbm) {
		tbm.add(deleteAction);
		tbm.add(getAction(TableView.FILTERS_ACTION_ID));
		tbm.update(false);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#registerGlobalActions(org.eclipse.ui.IActionBars)
	 */
	protected void registerGlobalActions(IActionBars actionBars) {
		actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
		actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), pasteAction);
		actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), deleteAction);
		actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), selectAllAction);
		actionBars.setGlobalActionHandler(ActionFactory.PROPERTIES.getId(), propertiesAction);
	}

	protected void initDragAndDrop() {
		int operations = DND.DROP_COPY;
		Transfer[] transferTypes = new Transfer[] { MarkerTransfer.getInstance(), TextTransfer.getInstance()};
		DragSourceListener listener = new DragSourceAdapter() {
			public void dragSetData(DragSourceEvent event) {
				performDragSetData(event);
			}
			public void dragFinished(DragSourceEvent event) {
			}
		};

		getViewer().addDragSupport(operations, transferTypes, listener);
	}

	/**
	 * The user is attempting to drag marker data.  Add the appropriate
	 * data to the event depending on the transfer type.
	 */
	private void performDragSetData(DragSourceEvent event) {
		if (MarkerTransfer.getInstance().isSupportedType(event.dataType)) {
			event.data = ((IStructuredSelection) getViewer().getSelection()).toArray();
			return;
		}
		if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
			List selection = ((IStructuredSelection) getViewer().getSelection()).toList();
			try {
				IMarker[] markers = new IMarker[selection.size()];
				selection.toArray(markers);
				if (markers != null) {
					event.data = copyAction.createMarkerReport(markers);
				}
			} catch (ArrayStoreException e) {
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#fillContextMenu(org.eclipse.jface.action.IMenuManager)
	 */
	protected void fillContextMenu(IMenuManager manager) {
		if (manager == null)
			return;
		manager.add(openAction);
		manager.add(showInNavigatorAction);
		manager.add(new Separator());
		manager.add(copyAction);
		pasteAction.updateEnablement();
		manager.add(pasteAction);
		manager.add(deleteAction);
		manager.add(selectAllAction);
		fillContextMenuAdditions(manager);
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
		manager.add(new Separator());
		manager.add(propertiesAction);
	}

	protected void fillContextMenuAdditions(IMenuManager manager) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#getFilter()
	 */
	protected abstract MarkerFilter getFilter();
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#handleKeyPressed(org.eclipse.swt.events.KeyEvent)
	 */
	protected void handleKeyPressed(KeyEvent event) {
		if (event.character == SWT.DEL && event.stateMask == 0 && deleteAction.isEnabled()) {
			deleteAction.run();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#handleOpenEvent(org.eclipse.jface.viewers.OpenEvent)
	 */
	protected void handleOpenEvent(OpenEvent event) {
		openAction.run();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#saveSelection(org.eclipse.ui.IMemento)
	 */
	protected void saveSelection(IMemento memento) {
		IStructuredSelection selection = (IStructuredSelection)getViewer().getSelection();
		IMemento selectionMem = memento.createChild(TAG_SELECTION);
		for (Iterator iterator = selection.iterator(); iterator.hasNext();) {
			ConcreteMarker marker = (ConcreteMarker) iterator.next();
			IMemento elementMem = selectionMem.createChild(TAG_MARKER);
			elementMem.putString(TAG_RESOURCE, marker.getMarker().getResource().getFullPath().toString());
			elementMem.putString(TAG_ID, String.valueOf(marker.getMarker().getId()));
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.internal.tableview.TableView#restoreSelection(org.eclipse.ui.IMemento)
	 */
	protected IStructuredSelection restoreSelection(IMemento memento) {
		if (memento == null) {
			return new StructuredSelection();
		}
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		IMemento selectionMemento = memento.getChild(TAG_SELECTION);
		if (selectionMemento == null) {
			return new StructuredSelection();
		}
		ArrayList selectionList = new ArrayList();
		IMemento[] markerMems = selectionMemento.getChildren(TAG_MARKER);
		for (int i = 0; i < markerMems.length; i++) {
			try {
				long id = new Long(markerMems[i].getString(TAG_ID)).longValue();
				IResource resource = root.findMember(markerMems[i].getString(TAG_RESOURCE));
				if (resource != null) {
					IMarker marker = resource.findMarker(id);
					if (marker != null)
						selectionList.add(currentMarkers.getMarker(marker));
				}
			} catch (CoreException e) {
			}
		}
		return new StructuredSelection(selectionList);
	}

	protected abstract String[] getRootTypes();

	/**
	 * @param part
	 * @param selection
	 */
	protected void focusSelectionChanged(IWorkbenchPart part, ISelection selection) {

		List resources = new ArrayList();
		if (part instanceof IEditorPart) {
			IEditorInput input = ((IEditorPart) part).getEditorInput();
			if (input instanceof FileEditorInput) {
				resources.add(((FileEditorInput) input).getFile());
			}
		} else {
			if (selection instanceof IStructuredSelection) {
				for (Iterator iterator = ((IStructuredSelection) selection).iterator(); iterator.hasNext();) {
					Object object = iterator.next();
					if (object instanceof IAdaptable) {
						ITaskListResourceAdapter taskListResourceAdapter;
						Object adapter = ((IAdaptable) object).getAdapter(ITaskListResourceAdapter.class);
						if (adapter != null && adapter instanceof ITaskListResourceAdapter) {
							taskListResourceAdapter = (ITaskListResourceAdapter) adapter;
						} else {
							taskListResourceAdapter = DefaultMarkerResourceAdapter.getDefault();
						}

						IResource resource = taskListResourceAdapter.getAffectedResource((IAdaptable) object);
						if (resource != null) {
							resources.add(resource);
						}
					}
				}
			}
		}

		IResource[] focus = new IResource[resources.size()];
		resources.toArray(focus);
		updateFocusResource(focus);
	}

	/**
	 * 
	 * @param resources
	 */
	protected abstract void updateFilterSelection(IResource[] resources);
	
	void updateFocusResource(IResource[] resources) {
		boolean updateNeeded = updateNeeded(focusResources, resources);
		if (updateNeeded) {
			focusResources = resources;
			updateFilterSelection(resources);
			refresh();
		}
	}

	private boolean updateNeeded(IResource[] oldResources, IResource[] newResources) {
		//determine if an update if refiltering is required
		MarkerFilter filter = getFilter();
		if (!filter.isEnabled()) {
			return false;
		}
		
		int onResource = filter.getOnResource();
		if (onResource == MarkerFilter.ON_ANY_RESOURCE || onResource == MarkerFilter.ON_WORKING_SET) {
			return false;
		}
		if (newResources == null || newResources.length < 1) {
			return false;
		}
		if (oldResources == null || oldResources.length < 1) {
			return true;
		}
		if (Arrays.equals(oldResources, newResources)) {
			return false;
		}
		if (onResource == MarkerFilter.ON_ANY_RESOURCE_OF_SAME_PROJECT) {
			Collection oldProjects = MarkerFilter.getProjectsAsCollection(oldResources);
			Collection newProjects = MarkerFilter.getProjectsAsCollection(newResources);
			
			if (oldProjects.size() == newProjects.size()) {
				return !newProjects.containsAll(oldProjects);
			} else {
				return true;
			}
		} 
		
		return true;
	}

	/**
	 * Returns the marker limit or -1 if unlimited
	 *  
	 * @return
	 */
	private int getMarkerLimit() {
		MarkerFilter filter = getFilter();

		if (!filter.isEnabled() || !filter.getFilterOnMarkerLimit()) {
			return -1;
		}
		
		return filter.getMarkerLimit();			
	}
	
	private boolean withinMarkerLimit(int toTest) {
		int limit = getMarkerLimit();
		
		return (limit == -1 || toTest <= limit);
	}
	
	void updateTitle() {
		String currentTitle = getTitle();
		String viewName = getConfigurationElement().getAttribute("name"); //$NON-NLS-1$
		String status = ""; //$NON-NLS-1$
		int filteredCount = currentMarkers.getItemCount();
		int totalCount = getTotalMarkers();
		if (filteredCount == totalCount) {
			status = Messages.format("filter.itemsMessage", new Object[] { new Integer(totalCount)}); //$NON-NLS-1$
		} else {
			status = Messages.format("filter.matchedMessage", new Object[] { new Integer(filteredCount), new Integer(totalCount)}); //$NON-NLS-1$
		}
		setContentDescription(status);
	}
	
	/**
	 * Updates the message displayed in the status line.  This method is
	 * invoked in the following cases:
	 * <ul>
	 * <li>when this view is first created</li>
	 * <li>when new elements are added</li>
	 * <li>when something is deleted</li>
	 * <li>when the filters change</li>
	 * </ul>
	 * <p>
	 * By default, this method calls <code>updateStatusMessage(IStructuredSelection)</code>
	 * with the current selection or <code>null</code>.  Classes wishing to override
	 * this functionality, should just override the method
	 * <code>updateStatusMessage(IStructuredSelection)</code>.
	 * </p>
	 */
	protected void updateStatusMessage() {
		ISelection selection = getViewer().getSelection();
		
		if (selection instanceof IStructuredSelection)
			updateStatusMessage((IStructuredSelection) selection);
		else 
			updateStatusMessage(null);
	}

	/**
	 * Updates that message displayed in the status line.  If the
	 * selection parameter is <code>null</code> or its size is 0, the status 
	 * area is blanked out.  If only 1 marker is selected, the
	 * status area is updated with the contents of the message
	 * attribute of this marker.  In other cases (more than one marker
	 * is selected) the status area indicates how many items have
	 * been selected.
	 * <p>
	 * This method may be overwritten.
	 * </p><p>
	 * This method is called whenever a selection changes in this view.
	 * </p>
	 * @param selection a valid selection or <code>null</code>
	 */
	protected void updateStatusMessage(IStructuredSelection selection) {
		String message = ""; //$NON-NLS-1$
		
		if (selection == null || selection.size() == 0){
			// Show stats on all items in the view
			message = updateSummaryVisible();
		} else if (selection.size() == 1) {
			// Use the Message attribute of the marker
			ConcreteMarker marker = (ConcreteMarker)selection.getFirstElement();
			message = marker.getDescription(); //$NON-NLS-1$
		} else if (selection.size() > 1) {
			// Show stats on only those items in the selection
			message = updateSummarySelected(selection);
		}
		getViewSite().getActionBars().getStatusLineManager().setMessage(message);		
	}

	/**
	 * @param selection
	 * @return the summary status message
	 */
	protected String updateSummarySelected(IStructuredSelection selection) {
		// Show how many items selected
		return Messages.format("marker.statusSummarySelected", new Object[] {new Integer(selection.size()), ""}); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * @return the update summary 
	 */
	protected String updateSummaryVisible() {
		return ""; //$NON-NLS-1$
	}

	public abstract void openFiltersDialog();
	
	/**
	 * Given a selection of IMarker, reveals the corresponding elements in the viewer
	 * 
	 * @param structuredSelection
	 * @param reveal
	 */
	public void setSelection(IStructuredSelection structuredSelection, boolean reveal) {
		TableViewer viewer = getViewer();
		
		List newSelection = new ArrayList(structuredSelection.size());
		
		for (Iterator i = structuredSelection.iterator(); i.hasNext();) {
			Object next = i.next();
			if (next instanceof IMarker) {
				ConcreteMarker marker = currentMarkers.getMarker((IMarker)next);
				if (marker != null) {
					newSelection.add(marker);
				}
			}
		}

		if (viewer != null)
			viewer.setSelection(new StructuredSelection(newSelection), reveal);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.markers.internal.TableView#setContents(java.util.Collection)
	 */
	void setContents(Collection contents, IProgressMonitor mon) {		
		if (withinMarkerLimit(contents.size())) {
			super.setContents(contents, mon);	
		} else {
			super.setContents(Collections.EMPTY_LIST, mon);
		}
	}

	protected MarkerList getVisibleMarkers() {
		return currentMarkers;
	}
	
	/**
	 * Returns the total number of markers. Should not be called while the marker
	 * list is still updating.
	 * 
	 * @return the total number of markers in the workspace (including everything that doesn't pass the filters)
	 */
	int getTotalMarkers() {
		// The number of visible markers should never exceed the total number of markers in
		// the workspace. If this assertation fails, it probably indicates some sort of concurrency problem
		// (most likely, getTotalMarkers was called while we were still computing the marker lists)
		Assert.isTrue(totalMarkers >= currentMarkers.getItemCount());

		return totalMarkers;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.markers.internal.TableView#sorterChanged()
	 */
	protected void sorterChanged() {
		refresh();
	}

	private static void examineDelta(IMarkerDelta[] deltas, List changes) {
		for (int idx = 0; idx < deltas.length; idx++) {
			IMarkerDelta delta = deltas[idx];
			int kind = delta.getKind();
			
			if (kind == IResourceDelta.CHANGED) {
				changes.add(deltas[idx].getMarker());
			}
		}
	}
    
    /* (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#showBusy(boolean)
     */
    public void showBusy(boolean busy) {
        super.showBusy(busy);
        
        if(busy){
        	preBusyMarkers = totalMarkers;
        }
        else{//Only bold if there has been a change in count
        	if(totalMarkers != preBusyMarkers)
        		getProgressService().warnOfContentChange();
        }
        
    }
    
    /**
     * Create the UIJob used in the receiver for updates.
     *
     */
    private void createUIJob(){
    	uiJob = new WorkbenchJob(Messages.getString("MarkerView.refreshProgress")) { //$NON-NLS-1$
    		
    		public IStatus runInUIThread(IProgressMonitor monitor) {						
    			updateStatusMessage();
    			updateTitle();
    			return Status.OK_STATUS;
    		}
    	};
    	uiJob.setPriority(Job.INTERACTIVE);
		uiJob.setSystem(true);
    }
	
}
