blob: 1ea1ed2d26e87bc06d1c7dd973721550c7239fb6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2015 Tasktop Technologies 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:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.java.ui;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.AbstractContextListener;
import org.eclipse.mylyn.context.core.ContextChangeEvent;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionElement;
/**
* @author Mik Kersten
*/
public class LandmarkMarkerManager extends AbstractContextListener {
private static final String ID_MARKER_LANDMARK = "org.eclipse.mylyn.context.ui.markers.landmark"; //$NON-NLS-1$
private final Map<IInteractionElement, Long> markerMap = new ConcurrentHashMap<IInteractionElement, Long>();
private final LandmarkUpdateJob updateJob = new LandmarkUpdateJob(
Messages.LandmarkMarkerManager_Updating_Landmark_Markers);
public LandmarkMarkerManager() {
super();
}
@Override
public void contextChanged(ContextChangeEvent event) {
LinkedHashSet<LandmarkUpdateOperation> runnables = new LinkedHashSet<LandmarkUpdateOperation>();
switch (event.getEventKind()) {
case ACTIVATED:
case DEACTIVATED:
modelUpdated();
break;
case CLEARED:
if (event.isActiveContext()) {
modelUpdated();
}
break;
case LANDMARKS_ADDED:
for (IInteractionElement element : event.getElements()) {
LandmarkUpdateOperation runnable = createAddLandmarkMarkerOperation(element);
if (runnable != null) {
runnables.add(runnable);
}
}
break;
case ELEMENTS_DELETED:
case LANDMARKS_REMOVED:
for (IInteractionElement element : event.getElements()) {
LandmarkUpdateOperation runnable = createRemoveLandmarkMarkerOperation(element);
if (runnable != null) {
runnables.add(runnable);
}
}
break;
}
updateJob.updateMarkers(runnables);
}
private void modelUpdated() {
try {
// remove all known landmark markers
LinkedHashSet<LandmarkUpdateOperation> runnables = new LinkedHashSet<LandmarkUpdateOperation>();
for (IInteractionElement node : markerMap.keySet()) {
LandmarkUpdateOperation runnable = createRemoveLandmarkMarkerOperation(node);
if (runnable != null) {
runnables.add(runnable);
}
}
markerMap.clear();
// add any nodes that are landmarks
for (IInteractionElement node : ContextCore.getContextManager().getActiveLandmarks()) {
LandmarkUpdateOperation runnable = createAddLandmarkMarkerOperation(node);
if (runnable != null) {
runnables.add(runnable);
}
}
updateJob.updateMarkers(runnables);
} catch (Throwable t) {
StatusHandler.log(new Status(IStatus.ERROR, JavaUiBridgePlugin.ID_PLUGIN,
"Could not update landmark markers", t)); //$NON-NLS-1$
}
}
private LandmarkUpdateOperation createAddLandmarkMarkerOperation(final IInteractionElement node) {
if (node == null || node.getContentType() == null) {
return null;
}
if (JavaStructureBridge.CONTENT_TYPE.equals(node.getContentType())) {
final IJavaElement element = JavaCore.create(node.getHandleIdentifier());
if (!element.exists()) {
return null;
}
if (element instanceof IMember) {
try {
final ISourceRange range = ((IMember) element).getNameRange();
IResource resource = element.getUnderlyingResource();
if (resource instanceof IFile) {
LandmarkUpdateOperation runnable = new LandmarkUpdateOperation(resource) {
public void run(IProgressMonitor monitor) throws CoreException {
IMarker marker = getResource().createMarker(ID_MARKER_LANDMARK);
if (marker != null && range != null) {
marker.setAttribute(IMarker.CHAR_START, range.getOffset());
marker.setAttribute(IMarker.CHAR_END, range.getOffset() + range.getLength());
marker.setAttribute(IMarker.MESSAGE, Messages.LandmarkMarkerManager_Mylyn_Landmark);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
markerMap.put(node, marker.getId());
}
}
};
return runnable;
}
} catch (JavaModelException e) {
StatusHandler.log(new Status(IStatus.ERROR, JavaUiBridgePlugin.ID_PLUGIN,
"Could not update marker", e)); //$NON-NLS-1$
}
}
}
return null;
}
private LandmarkUpdateOperation createRemoveLandmarkMarkerOperation(final IInteractionElement node) {
if (node == null) {
return null;
}
if (JavaStructureBridge.CONTENT_TYPE.equals(node.getContentType())) {
IJavaElement element = JavaCore.create(node.getHandleIdentifier());
if (!element.exists()) {
return null;
}
if (element.getAncestor(IJavaElement.COMPILATION_UNIT) != null // stuff
// from .class files
&& element instanceof ISourceReference) {
try {
IResource resource = element.getUnderlyingResource();
LandmarkUpdateOperation runnable = new LandmarkUpdateOperation(resource) {
public void run(IProgressMonitor monitor) throws CoreException {
if (getResource() != null) {
try {
if (markerMap.containsKey(node)) {
long id = markerMap.get(node);
IMarker marker = getResource().getMarker(id);
if (marker != null) {
marker.delete();
}
}
} catch (NullPointerException e) {
// FIXME avoid NPE
StatusHandler.log(new Status(IStatus.ERROR, JavaUiBridgePlugin.ID_PLUGIN,
"Could not update marker", e)); //$NON-NLS-1$
}
}
}
};
return runnable;
} catch (JavaModelException e) {
// ignore the Java Model errors
}
}
}
return null;
}
/**
* IWorkspaceRunnable that has a reference to the resource that it is operating on
*/
private abstract class LandmarkUpdateOperation implements IWorkspaceRunnable {
private final IResource resource;
public LandmarkUpdateOperation(IResource resource) {
Assert.isNotNull(resource);
this.resource = resource;
}
public IResource getResource() {
return resource;
}
}
/**
* Job to handle updating the landmark markers in the background
*/
private class LandmarkUpdateJob extends Job {
private static final int NOT_SCHEDULED = -1;
private final LinkedHashSet<LandmarkUpdateOperation> queue = new LinkedHashSet<LandmarkUpdateOperation>();
private long scheduleTime = NOT_SCHEDULED;
public LandmarkUpdateJob(String name) {
super(name);
setSystem(true);
}
public synchronized void updateMarkers(LinkedHashSet<LandmarkUpdateOperation> operations) {
queue.addAll(operations);
if (queue.size() > 0) {
if (scheduleTime == NOT_SCHEDULED) {
scheduleTime = System.currentTimeMillis();
schedule();
}
}
}
@Override
public IStatus run(IProgressMonitor monitor) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace == null) {
return Status.CANCEL_STATUS;
}
LinkedHashSet<LandmarkUpdateOperation> operations = null;
synchronized (this) {
operations = new LinkedHashSet<LandmarkUpdateOperation>(queue);
queue.clear();
scheduleTime = NOT_SCHEDULED;
}
if (operations != null) {
try {
monitor.beginTask(Messages.LandmarkMarkerManager_Updating_Landmark_Markers, operations.size());
for (LandmarkUpdateOperation runnable : operations) {
try {
workspace.run(runnable, runnable.getResource(), IWorkspace.AVOID_UPDATE, monitor);
} catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, JavaUiBridgePlugin.ID_PLUGIN,
"Could not update landmark marker", e)); //$NON-NLS-1$
} catch (OperationCanceledException e) {
return Status.CANCEL_STATUS;
}
monitor.worked(1);
}
} finally {
monitor.done();
}
}
return Status.OK_STATUS;
}
}
}