/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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:
* Del Myers - initial API and implementation
*******************************************************************************/
package ca.uvic.chisel.javasketch.ui.internal.presentation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Item;
import ca.uvic.chisel.javasketch.SketchPlugin;
import ca.uvic.chisel.javasketch.data.model.IActivation;
import ca.uvic.chisel.javasketch.data.model.IOriginMessage;
import ca.uvic.chisel.javasketch.data.model.IThread;
import ca.uvic.chisel.javasketch.data.model.ITraceModel;
import ca.uvic.chisel.javasketch.data.model.imple.internal.ThreadImpl;
import ca.uvic.chisel.javasketch.internal.JavaSearchUtils;
import ca.uvic.chisel.javasketch.ui.ISketchColorConstants;
import ca.uvic.chisel.widgets.RangeAnnotation;
import ca.uvic.chisel.widgets.RangeSlider;
/**
* @author Del Myers
*
*/
public class MarkRangeForSelectionJob extends Job {
private IJavaElement[] elements;
private IJavaSketchPresenter editor;
public static class InvocationReference implements IAdaptable {
public final IJavaElement javaElement;
public final IActivation activation;
private boolean highlight;
public InvocationReference(IJavaElement element, IActivation activation) {
this(element, activation, false);
}
/**
* @param je
* @param activation2
* @param b
*/
public InvocationReference(IJavaElement element, IActivation activation,
boolean highlight) {
this.javaElement = element;
this.activation = activation;
this.highlight = highlight;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!obj.getClass().equals(InvocationReference.class)) {
return false;
}
InvocationReference that = (InvocationReference) obj;
return this.activation.equals(that.activation) && this.javaElement.equals(that.javaElement);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.activation.hashCode() + this.javaElement.hashCode();
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public Object getAdapter(Class adapter) {
if (IJavaElement.class.equals(adapter)) {
return javaElement;
} else if (IActivation.class.equals(adapter) || ITraceModel.class.equals(adapter)) {
return activation;
}
return null;
}
}
/**
* @param name
*/
public MarkRangeForSelectionJob(IJavaSketchPresenter editor) {
super("Marking Range For Current Selection");
this.editor = editor;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(final IProgressMonitor monitor) {
if (editor.getTimeRange().isDisposed()) {
return Status.OK_STATUS;
}
monitor.beginTask("Refreshing Invocation Ranges", IProgressMonitor.UNKNOWN);
if (elements == null) {
return Status.OK_STATUS;
}
final HashSet<InvocationReference> invocations = new HashSet<InvocationReference>();
IThread thread = (ThreadImpl) editor.getThread();
try {
for (IJavaElement element : elements) {
if (monitor.isCanceled()) {
break;
}
IProgressMonitor subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
List<IActivation> activations = new ArrayList<IActivation>();
if (element instanceof IType) {
activations = JavaSearchUtils.findActivationsForClass(thread, (IType)element, subMonitor);
} else if (element instanceof IMethod) {
activations = JavaSearchUtils.findActivationsForMethod(thread, (IMethod)element, subMonitor);
}
for (IActivation activation : activations) {
invocations.add(new InvocationReference(element, activation));
}
}
editor.getTimeRange().getDisplay().syncExec(new Runnable(){
@Override
public void run() {
updateInUI(monitor, invocations);
}
});
} catch (CoreException e) {
SketchPlugin.getDefault().log(e);
return e.getStatus();
} finally {
monitor.done();
}
return Status.OK_STATUS;
}
/**
* Set the java elements for this job, and reschedules it. This method should
* be used rather than schedule().
* @param elements the elements to mark
*/
public void schedule(IJavaElement[] elements) {
cancel();
this.elements = elements;
schedule();
}
private void updateInUI(IProgressMonitor monitor, Collection<InvocationReference> invocations) {
RangeSlider slider = editor.getTimeRange();
if (slider.isDisposed()) {
return;
}
//first, dispose all of the previous markers
Item item = slider.getItem(slider.getSelectionIndex());
Object selectedData = null;
HashMap<InvocationReference, RangeAnnotation> preservedRanges = new HashMap<InvocationReference, RangeAnnotation>();
for (RangeAnnotation range : slider.getRanges()) {
if (item != null && !item.isDisposed() && item == range) {
selectedData = item.getData();
}
if (range.getData() instanceof InvocationReference) {
preservedRanges.put((InvocationReference) range.getData(), range);
}
}
IStructuredSelection viewSelection = (IStructuredSelection) editor.getSequenceChartViewer().getSelection();
Iterator<?> i = viewSelection.iterator();
while (i.hasNext()) {
if (monitor.isCanceled()) return;
Object next = i.next();
IActivation activation = null;
if (next instanceof IActivation) {
activation = (IActivation) next;
} else if (next instanceof IOriginMessage) {
try {
IOriginMessage message = (IOriginMessage) next;
activation = message.getTarget().getActivation();
} catch (NullPointerException e) {}
}
if (activation != null) {
try {
IJavaElement je = JavaSearchUtils.findElement(activation, new NullProgressMonitor());
if (je instanceof IMethod) {
InvocationReference ref = new InvocationReference(je, activation, true);
if (invocations.contains(ref)) {
invocations.remove(ref);
}
invocations.add(ref);
}
} catch (InterruptedException e) {
} catch (CoreException e) {
}
}
}
//now, add all the new invocations
RangeAnnotation newSelection = null;
for (InvocationReference ref : invocations) {
if (monitor.isCanceled()) return;
if (preservedRanges.containsKey(ref)) {
if (ref.equals(selectedData)) {
newSelection = preservedRanges.get(ref);
}
preservedRanges.remove(ref);
continue;
}
RangeAnnotation annotation = new RangeAnnotation(slider);
annotation.setOffset(ref.activation.getTime());
annotation.setLength(1);
annotation.setText("Activation of " + ref.javaElement.getElementName() + " at " + ref.activation.getTime());
if (!ref.highlight) {
annotation.setForeground(ISketchColorConstants.BLUE);
annotation.setBackground(ISketchColorConstants.LIGHT_BLUE);
} else {
annotation.setForeground(ISketchColorConstants.PURPLE);
annotation.setForeground(ISketchColorConstants.LIGHT_PURPLE);
}
annotation.setData(ref);
if (ref.equals(selectedData)) {
newSelection = annotation;
}
}
for (RangeAnnotation annotation : preservedRanges.values()) {
if (!annotation.isDisposed()) {
annotation.dispose();
}
}
if (newSelection != null && !newSelection.isDisposed()) {
slider.setSelectionIndex(slider.getIndex(newSelection));
}
}
}