/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* 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:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.framesoc.ui.perspective;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.part.ViewPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.soctrace.framesoc.core.bus.FramesocBus;
import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopic;
import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopicList;
import fr.inria.soctrace.framesoc.core.bus.FramesocBusVariable;
import fr.inria.soctrace.framesoc.core.bus.IFramesocBusListener;
import fr.inria.soctrace.framesoc.ui.loaders.TraceLoader.TraceChange;
import fr.inria.soctrace.framesoc.ui.model.GanttTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.HistogramTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.PieTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.SynchronizeTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.TableTraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.TraceIntervalAction;
import fr.inria.soctrace.framesoc.ui.model.TraceIntervalDescriptor;
import fr.inria.soctrace.lib.model.Trace;
import fr.inria.soctrace.lib.model.utils.SoCTraceException;
import fr.inria.soctrace.lib.query.TraceQuery;
import fr.inria.soctrace.lib.query.conditions.ConditionsConstants.ComparisonOperation;
import fr.inria.soctrace.lib.query.conditions.SimpleCondition;
import fr.inria.soctrace.lib.storage.DBObject;
import fr.inria.soctrace.lib.storage.SystemDBObject;
/**
* Base class for Framesoc views displaying a trace (histogram, pie-chart, table, ...).
*
* <p>
* The views extending this class are also referred as Framesoc analysis views.
*
* <p>
* This base class factorizes the following functionalities:
* <ul>
* <li>correct secondary id management at view creation
* <li>notification on the Framesoc Bus of the selected trace when the view is given focus
* <li>unregister all followed topics (if any) at disposal
* </ul>
*
* <p>
* Furthermore the class provides a protected method for basic trace alias change management (
* {@link #handleCommonTopics(String, Object)}), to be called at the beginning of the
* {@link IFramesocBusListener#handle(String, Object)} implementation in subclasses.
*
* <p>
* The class defines also the generic API to show a trace in a view, through the abstract method
* {@link #showTrace(Trace, Object)}.
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public abstract class FramesocPart extends ViewPart implements IFramesocBusListener {
/**
* Logger
*/
private final static Logger logger = LoggerFactory.getLogger(FramesocPart.class);
/**
* Highlight start constant
*/
private final static String HIGHLIGHT_START = "> ";
/**
* Followed topics
*/
protected FramesocBusTopicList topics = null;
/**
* Current shown trace
*/
protected Trace currentShownTrace = null;
/**
* Base class constructor, initialize the trace selection and the topics objects. Subclasses
* must invoke the super() method at the beginning of their constructors.
*/
public FramesocPart() {
topics = new FramesocBusTopicList(this);
topics.addTopic(FramesocBusTopic.TOPIC_UI_TRACES_SYNCHRONIZED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_REFRESH_TRACES_NEEDED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_SYSTEM_INITIALIZED);
topics.addTopic(FramesocBusTopic.TOPIC_UI_FOCUSED_TRACE);
topics.registerAll();
}
@Override
public void createPartControl(final Composite parent) {
// get the secondary id that the view was created with
String secondaryId = getViewSite().getSecondaryId();
// If the secondary id was null that means that Eclipse opened the view, and not us.
// That's bad because it creates a view without a secondary ID and causes the view
// to be "different" from one that we opened ourselves.
if (secondaryId == null) {
logger.debug("View created by Eclipse.");
// we need to do it asynchronously as we can't close the view until the create code has
// finished
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
// destroy ourselves
logger.debug("Destroy the view created by Eclipse.");
getViewSite().getPage().hideView(FramesocPart.this);
// open the same view our way
logger.debug("Create the view with a secondary ID.");
FramesocPartManager.getInstance().getPartInstance(getId(), null, false);
}
});
// return now as we're done with the non-secondary-id view
return;
}
createFramesocPartControl(parent);
}
/**
* Notify on the bus the shown trace when the view gets the focus. Subclasses should call super
* setFocus at the end of their setFocus methods.
*/
@Override
public void setFocus() {
if (currentShownTrace != null) {
logger.debug("current shown: {}", currentShownTrace.getAlias());
Trace selected = ((Trace) FramesocBus.getInstance().getVariable(
FramesocBusVariable.TRACE_VIEW_SELECTED_TRACE));
// If selected is null or it is different than the current trace
// then change the selected trace
if (selected == null || !selected.equals(currentShownTrace)) {
FramesocBus.getInstance().send(FramesocBusTopic.TOPIC_UI_FOCUSED_TRACE,
currentShownTrace);
}
}
}
/**
* Dispose method: unregister all the topics. Subclasses must call super.dispose() at the end of
* their dispose methods.
*/
@Override
public void dispose() {
if (topics != null)
topics.unregisterAll();
// tell the view manager we've been disposed
FramesocPartManager.getInstance().disposeFramesocPart(this);
super.dispose();
}
/**
* Return the shown trace, if any
*
* @return the shown trace or null if no trace is shown
*/
public Trace getCurrentShownTrace() {
return currentShownTrace;
}
/**
* Check if a trace is already shown
*
* @return true if a trace is already shown
*/
public boolean traceAlreadyShown() {
return (currentShownTrace != null);
}
/**
* Activate the view
*/
public void activateView() {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
getSite().getPage().activate(FramesocPart.this);
}
});
}
/**
* Hide the view
*/
public void hideView() {
// we need an asyncExec, otherwise we get a ConcurrentModificationException
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
getSite().getPage().hideView(FramesocPart.this);
}
});
}
/**
* @deprecated Use {@link #highlightTitle(boolean)} instead
*/
public void higlightTitle(boolean highlight) {
highlightTitle(highlight);
}
public void highlightTitle(boolean highlight) {
if (currentShownTrace != null) {
String name = currentShownTrace.getAlias();
logger.trace("Name before: '" + name + "'");
if (highlight) {
this.setPartName(HIGHLIGHT_START + getGroupString() + name);
} else {
this.setPartName(getGroupString() + name);
}
logger.trace("Name after: '" + name + "'");
}
}
private String getGroupString() {
int group = FramesocPartManager.getInstance().getPartGroup(currentShownTrace, this);
if (group == FramesocPartManager.NO_GROUP) {
return "";
}
if (FramesocPartManager.getInstance().isUniqueGroup(currentShownTrace, group)) {
return "";
}
return "(" + (group + 1) + ") ";
}
@Override
public void handle(FramesocBusTopic topic, Object data) {
// manage the common topics
handleCommonTopics(topic, data);
// call the concrete class method
partHandle(topic, data);
}
/**
* Handle the topics related to trace update/delete/selection.
* <ul>
* <li>TOPIC_UI_REFRESH_TRACES_NEEDED: the view title is updated
* <li>TOPIC_UI_TRACES_SYNCHRONIZED: updated or deleted shown trace
* <li>TOPIC_UI_SYSTEM_INITIALIZED: shown trace no more existing
* <li>TOPIC_UI_FOCUSED_TRACE: focused trace has changed
* </ul>
*
* @param topic
* bus topic
* @param data
* bus event data
*/
private void handleCommonTopics(FramesocBusTopic topic, Object data) {
if (currentShownTrace == null)
return;
Trace t = currentShownTrace;
if (topic.equals(FramesocBusTopic.TOPIC_UI_REFRESH_TRACES_NEEDED)) {
this.setPartName(t.getAlias());
} else if (topic.equals(FramesocBusTopic.TOPIC_UI_TRACES_SYNCHRONIZED)) {
@SuppressWarnings("unchecked")
Map<TraceChange, List<Trace>> traceChangeMap = ((Map<TraceChange, List<Trace>>) data);
if (traceChangeMap.get(TraceChange.REMOVE).contains(t)) {
hideView();
} else if (traceChangeMap.get(TraceChange.UPDATE).contains(t)) {
this.setPartName(t.getAlias());
}
} else if (topic.equals(FramesocBusTopic.TOPIC_UI_SYSTEM_INITIALIZED)) {
// check if the trace still exists
SystemDBObject sysDB = null;
boolean hide = false;
try {
sysDB = SystemDBObject.openNewInstance();
TraceQuery tq = new TraceQuery(sysDB);
tq.setElementWhere(new SimpleCondition("ID", ComparisonOperation.EQ, String
.valueOf(t.getId())));
List<Trace> ts = tq.getList();
if (ts.isEmpty())
hide = true;
Iterator<Trace> it = ts.iterator();
if (!it.hasNext())
hide = true;
if (!it.next().equals(t))
hide = true;
} catch (Exception e) {
logger.error(e.getMessage());
hide = true;
} finally {
DBObject.finalClose(sysDB);
if (hide)
hideView();
}
} else if (topic.equals(FramesocBusTopic.TOPIC_UI_FOCUSED_TRACE) && data != null) {
logger.debug("Updating titles after TOPIC_UI_FOCUSED_TRACE");
if (currentShownTrace.equals((Trace) data)) {
logger.debug("Highlight " + getPartName());
highlightTitle(true);
} else {
logger.debug("Unhighlight " + getPartName());
highlightTitle(false);
}
}
}
/**
* Enable/disable all the toolbar actions.
*
* @param enabled
* enable flag
*/
protected void enableActions(boolean enabled) {
IActionBars actionBars = getViewSite().getActionBars();
IToolBarManager toolBar = actionBars.getToolBarManager();
for (IContributionItem item : toolBar.getItems()) {
if (item instanceof ActionContributionItem) {
((ActionContributionItem) item).getAction().setEnabled(enabled);
}
}
}
protected TraceIntervalAction createTableAction() {
return new TableTraceIntervalAction(this) {
@Override
public TraceIntervalDescriptor getTraceIntervalDescriptor() {
return getIntervalDescriptor();
}
};
}
protected TraceIntervalAction createGanttAction() {
return new GanttTraceIntervalAction(this) {
@Override
public TraceIntervalDescriptor getTraceIntervalDescriptor() {
return getIntervalDescriptor();
}
};
}
protected TraceIntervalAction createPieAction() {
return new PieTraceIntervalAction(this) {
@Override
public TraceIntervalDescriptor getTraceIntervalDescriptor() {
return getIntervalDescriptor();
}
};
}
protected TraceIntervalAction createHistogramAction() {
return new HistogramTraceIntervalAction(this) {
@Override
public TraceIntervalDescriptor getTraceIntervalDescriptor() {
return getIntervalDescriptor();
}
};
}
protected TraceIntervalAction createSynchronizeAction() {
return new SynchronizeTraceIntervalAction(this) {
@Override
public TraceIntervalDescriptor getTraceIntervalDescriptor() {
return getIntervalDescriptor();
}
};
}
/**
* Return the trace interval descriptor corresponding to the current shown interval. The base
* class implementation returns the current shown trace and the whole trace duration.
*
* @return the trace interval descriptor for the current shown interval
*/
protected TraceIntervalDescriptor getIntervalDescriptor() {
if (currentShownTrace == null)
return null;
TraceIntervalDescriptor des = new TraceIntervalDescriptor();
des.setTrace(currentShownTrace);
des.setStartTimestamp(currentShownTrace.getMinTimestamp());
des.setEndTimestamp(currentShownTrace.getMaxTimestamp());
return des;
}
/**
* Method called in the handle() method of this base class.
*
* The base class implementation does nothing. Subclasses may redefine this method to handle
* other topics, beside the common ones handled by this class (
* {@link #handleCommonTopics(String, Object)}).
*
* @param topic
* bus topic
* @param data
* bus event data
*/
public void partHandle(FramesocBusTopic topic, Object data) {
}
/*
* Abstract methods
*/
/**
* Create the part control. This method is called in FramesocPart createPartControl after
* performing some checks on the secondary ID.
*
* @param parent
*/
protected abstract void createFramesocPartControl(final Composite parent);
/**
* Return the view ID.
*
* @return the view ID.
*/
public abstract String getId();
/**
* Show the passed trace, considering the parameters contained in data. These parameters are
* specific for different view types. See concrete Framesoc views documentation. Note that data
* is null when this method is called by the contextual menu in trace explorer, so in this case
* the view should implement a default behavior (e.g. show the whole trace).
*
* @param trace
* the trace that should to be shown.
* @param data
* view specific data
*
* @throws SoCTraceException
*/
public abstract void showTrace(Trace trace, Object data);
}