/*******************************************************************************
* 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;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import ca.uvic.chisel.javasketch.data.model.ITrace;
import ca.uvic.chisel.javasketch.data.model.ITraceEvent;
import ca.uvic.chisel.javasketch.data.model.ITraceEventListener;
import ca.uvic.chisel.javasketch.data.model.ITraceModel;
import ca.uvic.chisel.javasketch.internal.DBProgramSketch;
import ca.uvic.chisel.javasketch.internal.SketchEvents;
import ca.uvic.chisel.javasketch.internal.interest.DegreeOfInterest;
import ca.uvic.chisel.javasketch.launching.ITraceClient;
import ca.uvic.chisel.javasketch.launching.internal.JavaAgentTraceClient;
import ca.uvic.chisel.javasketch.ui.ISketchColorConstants;
import ca.uvic.chisel.javasketch.ui.ISketchImageConstants;
import ca.uvic.chisel.javasketch.ui.internal.SketchUI;
/**
* The activator class controls the plug-in life cycle
*/
public class SketchPlugin extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "ca.uvic.chisel.javasketch";
// The shared instance
private static SketchPlugin plugin;
// Listener for debug events.
private DebugListener debugListener;
private SketchEvents events;
private DegreeOfInterest doi;
private ColorRegistry colorRegistry;
private Map<String, IProgramSketch> cachedSketches;
private ITraceEventListener staticJavaModelListener;
private class StaticJavaModelListener implements ITraceEventListener {
private long eventTime = 0;
/* (non-Javadoc)
* @see ca.uvic.chisel.javasketch.data.model.ITraceEventListener#handleEvents(ca.uvic.chisel.javasketch.data.model.ITraceEvent[])
*/
@Override
public void handleEvents(ITraceEvent[] events) {
for (ITraceEvent event : events) {
switch (event.getType()) {
case MethodEventType:
case TypeEventType:
long currentTime = System.currentTimeMillis();
if (currentTime - eventTime >= 3000) {
SketchUI.INSTANCE.refreshJavaUI();
eventTime = currentTime;
}
}
}
}
}
private class DebugListener implements IDebugEventSetListener {
@Override
public void handleDebugEvents(DebugEvent[] events) {
SketchPlugin.this.handleDebugEvents(events);
}
}
/**
* The constructor
*/
public SketchPlugin() {
staticJavaModelListener = new StaticJavaModelListener();
doi = new DegreeOfInterest();
cachedSketches = new HashMap<String, IProgramSketch>();
this.debugListener = new DebugListener();
}
protected void handleDebugEvents(DebugEvent[] events) {
for (DebugEvent event: events) {
if (event.getSource() instanceof ITraceClient) {
SketchUI.INSTANCE.refreshCommands();
}
}
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
DebugPlugin.getDefault().addDebugEventListener(debugListener);
events = new SketchEvents();
getPreferenceStore().addPropertyChangeListener(SketchUI.INSTANCE);
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
plugin = null;
DebugPlugin.getDefault().removeDebugEventListener(debugListener);
synchronized(cachedSketches) {
for (IProgramSketch sketch : cachedSketches.values()) {
if (sketch.isConnected()) {
sketch.getPortal().close();
}
}
}
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static SketchPlugin getDefault() {
return plugin;
}
/**
* Returns the associated trace client for the given process, or NULL if one doesn't
* exist.
* @param process
* @return
*/
public ITraceClient getAssociatedClient(IProcess process) {
if (process != null) {
if (process instanceof ITraceClient) {
return (ITraceClient) process;
}
for (IProcess p : process.getLaunch().getProcesses()) {
if (p instanceof ITraceClient) {
return (ITraceClient) p;
}
}
}
return null;
}
/**
* Returns all of the traces stored for this workspace. The returned traces
* are not cached, but there should be no transient information in the
* returned sketches that cannot be computed.
* @return all of the traces stored for this workspace.
*/
public IProgramSketch[] getStoredSketches() {
//all of the traces are stored in the plugin state location.
synchronized (cachedSketches) {
File stateLocation = getStateLocation().toFile();
File[] launchDirectories = stateLocation.listFiles();
for (File launchDirectory : launchDirectories) {
if (launchDirectory.isDirectory()) {
//process all of the directories inside this one
for (File traceDirectory : launchDirectory.listFiles()) {
if (traceDirectory.isDirectory()) {
//check to see if it has the correct data stored
File propertiesFile = new File(traceDirectory, JavaAgentTraceClient.PROCESS_PROPERTIES_FILE);
if (propertiesFile.exists()) {
try {
Properties props = new Properties();
FileInputStream fis = new FileInputStream(propertiesFile);
props.load(fis);
fis.close();
String id = props.getProperty(JavaAgentTraceClient.ID_PROPERTY);
if (!cachedSketches.containsKey(id)) {
FileReader reader = new FileReader(new File(traceDirectory, "launch.configuration"));
char[] buf = new char[1024];
int read = -1;
StringWriter writer = new StringWriter();
while ((read = reader.read(buf)) != -1) {
writer.write(buf, 0, read);
}
reader.close();
writer.close();
ILaunchConfiguration cf = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(writer.toString());
if (cf != null) {
cachedSketches.put(id, new DBProgramSketch(traceDirectory));
}
}
} catch (IOException e) {
Status s = new Status(Status.WARNING, PLUGIN_ID, "An error occurred reading sketch at location " + traceDirectory +". It will be deleted.", e);
getLog().log(s);
delete(traceDirectory);
} catch (CoreException e) {
Status s = new Status(Status.WARNING, PLUGIN_ID, "An error occurred reading sketch at location " + traceDirectory +". It will be deleted.", e);
getLog().log(s);
delete(traceDirectory);
}
} else {
Status s = new Status(Status.WARNING, PLUGIN_ID, "An error occurred reading sketch at location " + traceDirectory +". It will be deleted.");
getLog().log(s);
delete(traceDirectory);
}
}
}
}
}
Collection<IProgramSketch> sketches = cachedSketches.values();
return sketches.toArray(new IProgramSketch[sketches.size()]);
}
}
private void delete(File file) {
IPath storageLocation = getStateLocation();
IPath filePath = new Path(file.getAbsolutePath());
if (!storageLocation.isPrefixOf(filePath)) return;
if (file.isDirectory()) {
File[] children = file.listFiles();
for (File child : children) {
delete(child);
}
}
file.delete();
}
public void addSketchEventListener(ISketchEventListener listener) {
events.addListener(listener);
}
/**
* @param traceNavigatorContentProvider
*/
public void removeSketchEventListener(ISketchEventListener listener) {
events.removeListener(listener);
}
/**
* Returns the stored sketches for the given launch configuration.
* @param configuration the configuration to check.
* @return
*/
public IProgramSketch[] getStoredSketches(String launchConfigurationName) {
ArrayList<IProgramSketch> traces = new ArrayList<IProgramSketch>();
for (IProgramSketch sketch : getStoredSketches()) {
String sketchName = sketch.getTracedLaunchConfiguration().getName();
if (sketchName.equals(launchConfigurationName)) {
traces.add(sketch);
}
}
return traces.toArray(new IProgramSketch[traces.size()]);
}
public void log(Exception e) {
if (e instanceof CoreException) {
getLog().log((((CoreException) e).getStatus()));
} else {
getLog().log(createStatus(e));
}
}
public IStatus createStatus(Exception e) {
String message = e.getMessage();
if (message == null) {
message = "";
}
return new Status(IStatus.ERROR, PLUGIN_ID, message, e);
}
/* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry)
*/
@Override
protected void initializeImageRegistry(ImageRegistry reg) {
reg.put(ISketchImageConstants.ICON_TRACE_EDITOR, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/trace_editor.png"));
reg.put(ISketchImageConstants.ICON_TRACE_ACTIVE, imageDescriptorFromPlugin(PLUGIN_ID, "images/etool16/trace-active.gif"));
reg.put(ISketchImageConstants.ICON_TRACE_INACTIVE, imageDescriptorFromPlugin(PLUGIN_ID, "images/etool16/trace-inactive.gif"));
reg.put(ISketchImageConstants.ICON_PROCESS_TRACE, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/trace_proc.png"));
reg.put(ISketchImageConstants.ICON_THREAD_TRACE, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/trace_thread.png"));
reg.put(ISketchImageConstants.ICON_CALENDAR, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/calendar.png"));
reg.put(ISketchImageConstants.ICON_ACTIVATION, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/activation.png"));
reg.put(ISketchImageConstants.ICON_ANNOTATION, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/annotation.png"));
reg.put(ISketchImageConstants.ICON_ANNOTATIONS, imageDescriptorFromPlugin(PLUGIN_ID, "images/obj16/annotations.png"));
reg.put(ISketchImageConstants.ICON_TRACE, imageDescriptorFromPlugin(PLUGIN_ID, "images/trace_view.png"));
reg.put(ISketchImageConstants.OVERLAY_ANALYSE, imageDescriptorFromPlugin(PLUGIN_ID, "images/dec8/ovr_gear.png"));
reg.put(ISketchImageConstants.OVERLAY_PLAY, imageDescriptorFromPlugin(PLUGIN_ID, "images/dec8/ovr_play.png"));
reg.put(ISketchImageConstants.OVERLAY_STOP, imageDescriptorFromPlugin(PLUGIN_ID, "images/dec8/ovr_stop.png"));
reg.put(ISketchImageConstants.ICON_ELEMENT_FILTERED, imageDescriptorFromPlugin("images/etool16/closedeye.png"));
reg.put(ISketchImageConstants.ICON_ELEMENT_VISIBLE, imageDescriptorFromPlugin("images/etool16/openeye.png"));
reg.put(ISketchImageConstants.ICON_ELEMENT_VISIBLE+"1-3", imageDescriptorFromPlugin("images/etool16/1-3eye.png"));
reg.put(ISketchImageConstants.ICON_ELEMENT_VISIBLE+"2-3", imageDescriptorFromPlugin("images/etool16/2-3eye.png"));
reg.put(ISketchImageConstants.ICON_LOGO, imageDescriptorFromPlugin("images/logo16.png"));
super.initializeImageRegistry(reg);
}
public ColorRegistry getColorRegistry() {
if (this.colorRegistry == null) {
colorRegistry = new ColorRegistry(PlatformUI.getWorkbench().getDisplay());
colorRegistry.put(ISketchColorConstants.RED_KEY, new RGB(200, 0,0));
colorRegistry.put(ISketchColorConstants.GREEN_KEY, new RGB(0, 150,0));
colorRegistry.put(ISketchColorConstants.BLUE_KEY, new RGB(0, 0,200));
colorRegistry.put(ISketchColorConstants.LIGHT_RED_KEY, new RGB(255,225,225));
colorRegistry.put(ISketchColorConstants.LIGHT_GREEN_KEY, new RGB(225,255,225));
colorRegistry.put(ISketchColorConstants.LIGHT_BLUE_KEY, new RGB(225,225,255));
colorRegistry.put(ISketchColorConstants.ERROR_BG_KEY, new RGB(255,250,250));
colorRegistry.put(ISketchColorConstants.CONDITION_BG_KEY, new RGB(250,255,250));
colorRegistry.put(ISketchColorConstants.LOOP_BG_KEY, new RGB(250,250,255));
colorRegistry.put(ISketchColorConstants.AMBER_KEY, new RGB(255, 160, 32));
colorRegistry.put(ISketchColorConstants.LIGHT_AMBER_KEY, new RGB(255, 225, 180));
colorRegistry.put(ISketchColorConstants.LIGHT_PURPLE_KEY, new RGB(255, 225, 255));
colorRegistry.put(ISketchColorConstants.PURPLE_KEY, new RGB(255, 180, 255));
colorRegistry.put(ISketchColorConstants.GRAY_KEY, new RGB(150, 150, 150));
colorRegistry.put(ISketchColorConstants.BLACK_KEY, new RGB(0, 0, 0));
}
return colorRegistry;
}
/**
* Deletes a sketch from the system.
* @param sketch the sketch
*/
public void deleteSketch(IProgramSketch sketch) {
deleteSketch(sketch, true);
}
public void deleteSketch(final IProgramSketch sketch, boolean fork) {
final Runnable work = new Runnable() {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (sketch instanceof DBProgramSketch) {
DBProgramSketch s = (DBProgramSketch) sketch;
if (s != null) {
if (s.equals(getActiveSketch())) {
setActiveSketch(null);
}
getDOI().setSketchHidden(sketch, false, new NullProgressMonitor());
boolean deleted = s.delete();
if (deleted) {
synchronized (cachedSketches) {
Iterator<IProgramSketch> sketches = cachedSketches.values().iterator();
while (sketches.hasNext()) {
IProgramSketch next = sketches.next();
if (next.equals(sketch)) {
sketches.remove();
}
}
}
events.fireEvent(new SketchEvent(sketch, SketchEvent.SketchEventType.SketchDeleted));
}
if (sketch.equals(doi.getActiveSketch())) {
setActiveSketch(null);
}
}
}
}
};
if (fork) {
WorkspaceJob job = new WorkspaceJob("Deleting " + sketch.getLabel()) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor)
throws CoreException {
work.run();
return Status.OK_STATUS;
}
};
job.schedule();
} else {
work.run();
}
}
/**
* @param traceElement
* @return
*/
public IProgramSketch getSketch(ITraceModel traceElement) {
if (traceElement==null) return null;
ITrace trace = traceElement.getTrace();
String launchID = trace.getLaunchID();
IProgramSketch[] sketches = getStoredSketches();
for (IProgramSketch sketch : sketches) {
String sketchLaunch = sketch.getID();
if (sketchLaunch.equals(launchID)) {
return sketch;
}
}
return null;
}
/**
* Sets the active sketch to the given sketch. May be null.
* @param sketch the new active sketch. May be null.
*/
public synchronized void setActiveSketch(final IProgramSketch sketch) {
IProgramSketch activeSketch = doi.getActiveSketch();
if (doi.getActiveSketch() != null) {
if (activeSketch.getTraceData() != null) {
activeSketch.getTraceData().removeListener(staticJavaModelListener);
}
}
try {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
doi.setActiveSketch(sketch, monitor);
if (sketch != null) {
sketch.getTraceData().addListener(staticJavaModelListener);
}
SketchUI.INSTANCE.refreshJavaUI();
}
};
if (Display.getCurrent() != null) {
getWorkbench().getProgressService().busyCursorWhile(runnable);
} else {
runnable.run(new NullProgressMonitor());
}
} catch (InterruptedException e) {
} catch (Exception e) {
log(e);
doi.setActiveSketch(null, new NullProgressMonitor());
SketchUI.INSTANCE.refreshJavaUI();
}
}
/**
* Returns the active sketch in the framework. May be null.
* @return
*/
public synchronized IProgramSketch getActiveSketch() {
return doi.getActiveSketch();
}
public static ImageDescriptor imageDescriptorFromPlugin(String location) {
return imageDescriptorFromPlugin(PLUGIN_ID, location);
}
/**
* @param lastTrace
* @return
*/
public IProgramSketch getSketch(String identifier) {
if (identifier == null) return null;
for (IProgramSketch sketch : getStoredSketches()) {
if (sketch.getID().equals(identifier)) {
return sketch;
}
}
return null;
}
/**
* @return
*/
public IDegreeOfInterest getDOI() {
return doi;
}
}