blob: ca6b7eea43327d4741ce22c87678e3450fc4d4d7 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2011, 2015 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Bernd Hufmann - Initial API and implementation
**********************************************************************/
package org.eclipse.tracecompass.tmf.ui.views.uml2sd.loader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.uml2sd.ITmfSyncSequenceDiagramEvent;
import org.eclipse.tracecompass.tmf.core.uml2sd.TmfSyncSequenceDiagramEvent;
import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.SDView;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Frame;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.GraphNode;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.core.Lifeline;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.Criteria;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterCriteria;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.dialogs.FilterListDialog;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDAdvancedPagingProvider;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFilterProvider;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDFindProvider;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.handlers.provider.ISDGraphNodeSupporter;
import org.eclipse.tracecompass.tmf.ui.views.uml2sd.load.IUml2SDLoader;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressConstants;
/**
* <p>
* This class is a reference implementation of the
* <code>org.eclipse.tracecompass.tmf.ui.Uml2SDLoader</code> extension point. It
* provides a Sequence Diagram loader for a user space trace with specific trace
* content for sending and receiving signals between components. I also includes
* a default implementation for the <code>ITmfEvent</code> parsing.
* </p>
*
* The class <code>TmfUml2SDSyncLoader</code> analyzes events from type
* <code>ITmfEvent</code> and creates events type
* <code>ITmfSyncSequenceDiagramEvent</code> if the <code>ITmfEvent</code>
* contains all relevant information. The analysis checks that the event type
* strings contains either string SEND or RECEIVE. If event type matches these
* key words, the analyzer will look for strings sender, receiver and signal in
* the event fields of type <code>ITmfEventField</code>. If all the data is
* found a sequence diagram event from can be created. Note that Sync Messages
* are assumed, which means start and end time are the same. <br>
* <br>
* The parsing of the <code>ITmfEvent</code> is done in the method
* <code>getSequnceDiagramEvent()</code> of class
* <code>TmfUml2SDSyncLoader</code>. By extending the class
* <code>TmfUml2SDSyncLoader</code> and overwriting
* <code>getSequnceDiagramEvent()</code> a customized parsing algorithm can be
* implemented.<br>
* <br>
* Note that combined traces of multiple components, that contain the trace
* information about the same interactions are not supported in the class
* <code>TmfUml2SDSyncLoader</code>.
*
* @version 1.0
* @author Bernd Hufmann
*/
public class TmfUml2SDSyncLoader extends TmfComponent implements IUml2SDLoader, ISDFindProvider, ISDFilterProvider, ISDAdvancedPagingProvider, ISelectionListener {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
/**
* Default title name.
*/
protected static final String TITLE = Messages.TmfUml2SDSyncLoader_ViewName;
/**
* Maximum number of messages per page.
*/
protected static final int MAX_NUM_OF_MSG = 10000;
private static final int INDEXING_THREAD_SLEEP_VALUE = 100;
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
// Experiment attributes
/**
* The TMF trace reference.
*/
protected ITmfTrace fTrace = null;
/**
* The current indexing event request.
*/
protected ITmfEventRequest fIndexRequest = null;
/**
* The current request to fill a page.
*/
protected ITmfEventRequest fPageRequest = null;
/**
* Flag whether the time range signal was sent by this loader class or not
*/
protected volatile boolean fIsSignalSent = false;
// The view and event attributes
/**
* The sequence diagram view reference.
*/
protected SDView fView = null;
/**
* The current sequence diagram frame reference.
*/
protected Frame fFrame = null;
/**
* The list of sequence diagram events of current page.
*/
protected List<ITmfSyncSequenceDiagramEvent> fEvents = new ArrayList<>();
// Checkpoint and page attributes
/**
* The checkpoints of the whole sequence diagram trace (i.e. start time
* stamp of each page)
*/
protected List<TmfTimeRange> fCheckPoints = new ArrayList<>(MAX_NUM_OF_MSG);
/**
* The current page displayed.
*/
protected volatile int fCurrentPage = 0;
/**
* The current time selected.
*/
protected ITmfTimestamp fCurrentTime = null;
/**
* Flag to specify that selection of message is done by selection or by
* signal.
*/
protected volatile boolean fIsSelect = false;
// Search attributes
/**
* The job for searching across pages.
*/
protected SearchJob fFindJob = null;
/**
* List of found nodes within a page.
*/
protected List<GraphNode> fFindResults = new ArrayList<>();
/**
* The current find criteria reference
*/
protected Criteria fFindCriteria = null;
/**
* The current find index within the list of found nodes
* (<code>fFindeResults</code> within a page.
*/
protected volatile int fCurrentFindIndex = 0;
// Filter attributes
/**
* The list of active filters.
*/
protected List<FilterCriteria> fFilterCriteria = null;
// Thread synchronization
/**
* The synchronization lock.
*/
protected ReentrantLock fLock = new ReentrantLock();
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Default constructor
*/
public TmfUml2SDSyncLoader() {
super(TITLE);
}
/**
* Constructor
*
* @param name
* Name of loader
*/
public TmfUml2SDSyncLoader(String name) {
super(name);
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
/**
* Returns the current time if available else null.
*
* @return the current time if available else null
*/
public ITmfTimestamp getCurrentTime() {
fLock.lock();
try {
if (fCurrentTime != null) {
return fCurrentTime;
}
return null;
} finally {
fLock.unlock();
}
}
/**
* Waits for the page request to be completed
*/
public void waitForCompletion() {
fLock.lock();
ITmfEventRequest request = fPageRequest;
fLock.unlock();
if (request != null) {
try {
request.waitForCompletion();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* Handler for the trace opened signal.
*
* @param signal
* The trace opened signal
*/
@TmfSignalHandler
public void traceOpened(TmfTraceOpenedSignal signal) {
fTrace = signal.getTrace();
loadTrace();
}
/**
* Signal handler for the trace selected signal.
*
* Spawns a request to index the trace (checkpoints creation) as well as it
* fills the first page.
*
* @param signal
* The trace selected signal
*/
@TmfSignalHandler
public void traceSelected(TmfTraceSelectedSignal signal) {
// Update the trace reference
ITmfTrace trace = signal.getTrace();
if (!trace.equals(fTrace)) {
fTrace = trace;
}
loadTrace();
}
/**
* Method for loading the current selected trace into the view. Sub-class
* need to override this method to add the view specific implementation.
*/
protected void loadTrace() {
ITmfEventRequest indexRequest = null;
fLock.lock();
try {
final Job job = new IndexingJob("Indexing " + getName() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
job.setUser(false);
job.schedule();
indexRequest = fIndexRequest;
cancelOngoingRequests();
TmfTimeRange window = TmfTimeRange.ETERNITY;
fIndexRequest = new TmfEventRequest(ITmfEvent.class, window, 0,
ITmfEventRequest.ALL_DATA, ITmfEventRequest.ExecutionType.BACKGROUND) {
private ITmfTimestamp fFirstTime = null;
private ITmfTimestamp fLastTime = null;
private int fNbSeqEvents = 0;
private final List<ITmfSyncSequenceDiagramEvent> fSdEvents = new ArrayList<>(MAX_NUM_OF_MSG);
@Override
public void handleData(ITmfEvent event) {
super.handleData(event);
ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event);
ITmfTimestamp firstTime = fFirstTime;
ITmfTimestamp lastTime = fLastTime;
if (sdEvent != null) {
++fNbSeqEvents;
if (firstTime == null) {
firstTime = event.getTimestamp();
fFirstTime = firstTime;
}
lastTime = event.getTimestamp();
fLastTime = lastTime;
if ((fNbSeqEvents % MAX_NUM_OF_MSG) == 0) {
fLock.lock();
try {
fCheckPoints.add(new TmfTimeRange(firstTime, lastTime));
if (fView != null) {
fView.updateCoolBar();
}
} finally {
fLock.unlock();
}
fFirstTime = null;
}
if (fNbSeqEvents > MAX_NUM_OF_MSG) {
// page is full
return;
}
fSdEvents.add(sdEvent);
if (fNbSeqEvents == MAX_NUM_OF_MSG) {
fillCurrentPage(fSdEvents);
}
}
}
@Override
public void handleSuccess() {
final ITmfTimestamp firstTime = fFirstTime;
final ITmfTimestamp lastTime = fLastTime;
if ((firstTime != null) && (lastTime != null)) {
fLock.lock();
try {
fCheckPoints.add(new TmfTimeRange(firstTime, lastTime));
if (fView != null) {
fView.updateCoolBar();
}
} finally {
fLock.unlock();
}
}
if (fNbSeqEvents <= MAX_NUM_OF_MSG) {
fillCurrentPage(fSdEvents);
}
super.handleSuccess();
}
@Override
public void handleCompleted() {
if (fEvents.isEmpty()) {
fFrame = new Frame();
// make sure that view is not null when setting frame
SDView sdView;
fLock.lock();
try {
sdView = fView;
} finally {
fLock.unlock();
}
if (sdView != null) {
sdView.setFrameSync(fFrame);
}
}
super.handleCompleted();
job.cancel();
}
};
} finally {
fLock.unlock();
}
if (indexRequest != null && !indexRequest.isCompleted()) {
indexRequest.cancel();
}
resetLoader();
fTrace.sendRequest(fIndexRequest);
}
/**
* Signal handler for the trace closed signal.
*
* @param signal
* The trace closed signal
*/
@TmfSignalHandler
public void traceClosed(TmfTraceClosedSignal signal) {
if (signal.getTrace() != fTrace) {
return;
}
ITmfEventRequest indexRequest = null;
fLock.lock();
try {
indexRequest = fIndexRequest;
fIndexRequest = null;
cancelOngoingRequests();
if (fFilterCriteria != null) {
fFilterCriteria.clear();
}
FilterListDialog.deactivateSavedGlobalFilters();
} finally {
fTrace = null;
fLock.unlock();
}
if (indexRequest != null && !indexRequest.isCompleted()) {
indexRequest.cancel();
}
resetLoader();
}
/**
* Moves to the page that contains the time provided by the signal. The
* messages will be selected if the provided time is the time of a message.
*
* @param signal
* The selection range signal
* @since 1.0
*/
@TmfSignalHandler
public void selectionRangeUpdated(TmfSelectionRangeUpdatedSignal signal) {
fLock.lock();
try {
if ((signal.getSource() != this) && (fFrame != null) && (!fCheckPoints.isEmpty())) {
fCurrentTime = signal.getBeginTime();
fIsSelect = true;
moveToMessage();
}
} finally {
fLock.unlock();
}
}
/**
* Moves to the page that contains the current time provided by signal. No
* message will be selected however the focus will be set to the message if
* the provided time is the time of a message.
*
* @param signal
* The window range signal
* @since 1.0
*/
@TmfSignalHandler
public void windowRangeUpdated(TmfWindowRangeUpdatedSignal signal) {
fLock.lock();
try {
if ((signal.getSource() != this) && (fFrame != null) && !fIsSignalSent && (!fCheckPoints.isEmpty())) {
TmfTimeRange newTimeRange = signal.getCurrentRange();
fIsSelect = false;
fCurrentTime = newTimeRange.getStartTime();
moveToMessage();
}
} finally {
fLock.unlock();
}
}
@Override
public void setViewer(SDView viewer) {
fLock.lock();
try {
fView = viewer;
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().addPostSelectionListener(this);
fView.setSDFindProvider(this);
fView.setSDPagingProvider(this);
fView.setSDFilterProvider(this);
resetLoader();
IEditorPart editor = fView.getSite().getPage().getActiveEditor();
if (editor instanceof ITmfTraceEditor) {
ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
if (trace != null) {
traceSelected(new TmfTraceSelectedSignal(this, trace));
}
}
} finally {
fLock.unlock();
}
}
@Override
public String getTitleString() {
return getName();
}
@Override
public void dispose() {
super.dispose();
ITmfEventRequest indexRequest = null;
fLock.lock();
try {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
// During Eclipse shutdown the active workbench window is null
if (window != null) {
window.getSelectionService().removePostSelectionListener(this);
}
indexRequest = fIndexRequest;
fIndexRequest = null;
cancelOngoingRequests();
fView.setSDFindProvider(null);
fView.setSDPagingProvider(null);
fView.setSDFilterProvider(null);
fView = null;
} finally {
fLock.unlock();
}
if (indexRequest != null && !indexRequest.isCompleted()) {
indexRequest.cancel();
}
}
@Override
public boolean isNodeSupported(int nodeType) {
switch (nodeType) {
case ISDGraphNodeSupporter.LIFELINE:
case ISDGraphNodeSupporter.SYNCMESSAGE:
return true;
default:
break;
}
return false;
}
@Override
public String getNodeName(int nodeType, String loaderClassName) {
switch (nodeType) {
case ISDGraphNodeSupporter.LIFELINE:
return Messages.TmfUml2SDSyncLoader_CategoryLifeline;
case ISDGraphNodeSupporter.SYNCMESSAGE:
return Messages.TmfUml2SDSyncLoader_CategoryMessage;
default:
break;
}
return ""; //$NON-NLS-1$
}
@Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
ISelection sel = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection();
if ((sel != null) && (sel instanceof StructuredSelection)) {
StructuredSelection stSel = (StructuredSelection) sel;
if (stSel.getFirstElement() instanceof TmfSyncMessage) {
TmfSyncMessage syncMsg = ((TmfSyncMessage) stSel.getFirstElement());
ITmfTimestamp startTime = syncMsg.getStartTime();
if (startTime == null) {
startTime = TmfTimestamp.BIG_BANG;
}
broadcast(new TmfSelectionRangeUpdatedSignal(this, startTime, startTime, fTrace));
}
}
}
@Override
public boolean find(Criteria toSearch) {
fLock.lock();
try {
if (fFrame == null) {
return false;
}
if ((fFindResults == null) || (fFindCriteria == null) || !fFindCriteria.compareTo(toSearch)) {
fFindResults = new CopyOnWriteArrayList<>();
fFindCriteria = toSearch;
if (fFindCriteria.isLifeLineSelected()) {
for (int i = 0; i < fFrame.lifeLinesCount(); i++) {
if (fFindCriteria.matches(fFrame.getLifeline(i).getName())) {
fFindResults.add(fFrame.getLifeline(i));
}
}
}
ArrayList<GraphNode> msgs = new ArrayList<>();
if (fFindCriteria.isSyncMessageSelected()) {
for (int i = 0; i < fFrame.syncMessageCount(); i++) {
if (fFindCriteria.matches(fFrame.getSyncMessage(i).getName())) {
msgs.add(fFrame.getSyncMessage(i));
}
}
}
if (!msgs.isEmpty()) {
fFindResults.addAll(msgs);
}
List<GraphNode> selection = fView.getSDWidget().getSelection();
if ((selection != null) && (selection.size() == 1)) {
fCurrentFindIndex = fFindResults.indexOf(selection.get(0)) + 1;
} else {
fCurrentFindIndex = 0;
}
} else {
// ++ is not atomic, but we are in a lock
fCurrentFindIndex = fCurrentFindIndex + 1;
}
if (fFindResults.size() > fCurrentFindIndex) {
GraphNode current = fFindResults.get(fCurrentFindIndex);
fView.getSDWidget().moveTo(current);
return true;
}
fFindResults = null;
fCurrentFindIndex = 0;
return findInNextPages(fFindCriteria); // search in other page
} finally {
fLock.unlock();
}
}
@Override
public void cancel() {
cancelOngoingRequests();
}
@Override
public boolean filter(List<FilterCriteria> filters) {
fLock.lock();
try {
cancelOngoingRequests();
if (filters == null) {
fFilterCriteria = new ArrayList<>();
} else {
List<FilterCriteria> list = filters;
fFilterCriteria = new ArrayList<>(list);
}
fillCurrentPage(fEvents);
} finally {
fLock.unlock();
}
return true;
}
@Override
public boolean hasNextPage() {
fLock.lock();
try {
int size = fCheckPoints.size();
if (size > 0) {
return fCurrentPage < (size - 1);
}
} finally {
fLock.unlock();
}
return false;
}
@Override
public boolean hasPrevPage() {
fLock.lock();
try {
return fCurrentPage > 0;
} finally {
fLock.unlock();
}
}
@Override
public void nextPage() {
fLock.lock();
try {
// Safety check
if (fCurrentPage >= (fCheckPoints.size() - 1)) {
return;
}
cancelOngoingRequests();
fCurrentTime = null;
// ++ is not atomic but we are in a lock
fCurrentPage = fCurrentPage + 1;
moveToPage();
} finally {
fLock.unlock();
}
}
@Override
public void prevPage() {
fLock.lock();
try {
// Safety check
if (fCurrentPage <= 0) {
return;
}
cancelOngoingRequests();
fCurrentTime = null;
// -- is not atomic but we are in a lock
fCurrentPage = fCurrentPage - 1;
moveToPage();
} finally {
fLock.unlock();
}
}
@Override
public void firstPage() {
fLock.lock();
try {
cancelOngoingRequests();
fCurrentTime = null;
fCurrentPage = 0;
moveToPage();
} finally {
fLock.unlock();
}
}
@Override
public void lastPage() {
fLock.lock();
try {
cancelOngoingRequests();
fCurrentTime = null;
fCurrentPage = fCheckPoints.size() - 1;
moveToPage();
} finally {
fLock.unlock();
}
}
@Override
public int currentPage() {
fLock.lock();
try {
return fCurrentPage;
} finally {
fLock.unlock();
}
}
@Override
public int pagesCount() {
fLock.lock();
try {
return fCheckPoints.size();
} finally {
fLock.unlock();
}
}
@Override
public void pageNumberChanged(int pagenNumber) {
int localPageNumber = pagenNumber;
fLock.lock();
try {
cancelOngoingRequests();
if (localPageNumber < 0) {
localPageNumber = 0;
}
int size = fCheckPoints.size();
if (localPageNumber > (size - 1)) {
localPageNumber = size - 1;
}
fCurrentPage = localPageNumber;
moveToPage();
} finally {
fLock.unlock();
}
}
@Override
public void broadcast(TmfSignal signal) {
fIsSignalSent = true;
super.broadcast(signal);
fIsSignalSent = false;
}
/**
* Cancels any ongoing find operation
*/
protected void cancelOngoingRequests() {
fLock.lock();
ITmfEventRequest pageRequest = null;
try {
// Cancel the search thread
if (fFindJob != null) {
fFindJob.cancel();
}
fFindResults = null;
fFindCriteria = null;
fCurrentFindIndex = 0;
pageRequest = fPageRequest;
fPageRequest = null;
} finally {
fLock.unlock();
}
if (pageRequest != null && !pageRequest.isCompleted()) {
pageRequest.cancel();
}
}
/**
* Resets loader attributes
*/
protected void resetLoader() {
fLock.lock();
try {
fCurrentTime = null;
fEvents.clear();
fCheckPoints.clear();
fCurrentPage = 0;
fCurrentFindIndex = 0;
fFindCriteria = null;
fFindResults = null;
fView.setFrameSync(new Frame());
fFrame = null;
} finally {
fLock.unlock();
}
}
/**
* Fills current page with sequence diagram content.
*
* @param events
* sequence diagram events
*/
protected void fillCurrentPage(List<ITmfSyncSequenceDiagramEvent> events) {
fLock.lock();
try {
fEvents = new ArrayList<>(events);
if (fView != null && !events.isEmpty()) {
fView.toggleWaitCursorAsync(true);
}
} finally {
fLock.unlock();
}
final Frame frame = new Frame();
if (!events.isEmpty()) {
Map<String, Lifeline> nodeToLifelineMap = new HashMap<>();
frame.setName(Messages.TmfUml2SDSyncLoader_FrameName);
for (int i = 0; i < events.size(); i++) {
ITmfSyncSequenceDiagramEvent sdEvent = events.get(i);
if ((nodeToLifelineMap.get(sdEvent.getSender()) == null) && (!filterLifeLine(sdEvent.getSender()))) {
Lifeline lifeline = new Lifeline();
lifeline.setName(sdEvent.getSender());
nodeToLifelineMap.put(sdEvent.getSender(), lifeline);
frame.addLifeLine(lifeline);
}
if ((nodeToLifelineMap.get(sdEvent.getReceiver()) == null) && (!filterLifeLine(sdEvent.getReceiver()))) {
Lifeline lifeline = new Lifeline();
lifeline.setName(sdEvent.getReceiver());
nodeToLifelineMap.put(sdEvent.getReceiver(), lifeline);
frame.addLifeLine(lifeline);
}
}
int eventOccurence = 1;
for (int i = 0; i < events.size(); i++) {
ITmfSyncSequenceDiagramEvent sdEvent = events.get(i);
// Check message filter
if (filterMessage(sdEvent)) {
continue;
}
// Set the message sender and receiver
Lifeline startLifeline = nodeToLifelineMap.get(sdEvent.getSender());
Lifeline endLifeline = nodeToLifelineMap.get(sdEvent.getReceiver());
// Check if any of the lifelines were filtered
if ((startLifeline == null) || (endLifeline == null)) {
continue;
}
int tmp = Math.max(startLifeline.getEventOccurrence(), endLifeline.getEventOccurrence());
eventOccurence = Math.max(eventOccurence, tmp);
startLifeline.setCurrentEventOccurrence(eventOccurence);
endLifeline.setCurrentEventOccurrence(eventOccurence);
TmfSyncMessage message = new TmfSyncMessage(sdEvent, eventOccurence++);
message.setStartLifeline(startLifeline);
message.setEndLifeline(endLifeline);
message.setTime(sdEvent.getStartTime());
// add the message to the frame
frame.addMessage(message);
}
fLock.lock();
try {
if (!fView.getSDWidget().isDisposed()) {
fView.getSDWidget().getDisplay().asyncExec(() -> {
fLock.lock();
try {
// check if view was disposed in the meanwhile
if ((fView != null) && (!fView.getSDWidget().isDisposed())) {
fFrame = frame;
fView.setFrame(fFrame);
if (fCurrentTime != null) {
moveToMessageInPage();
}
if (fFindCriteria != null) {
find(fFindCriteria);
}
fView.toggleWaitCursorAsync(false);
}
} finally {
fLock.unlock();
}
});
}
} finally {
fLock.unlock();
}
}
}
/**
* Moves to a certain message defined by timestamp (across pages)
*/
protected void moveToMessage() {
int page = 0;
fLock.lock();
try {
page = getPage(fCurrentTime);
if (page == fCurrentPage) {
moveToMessageInPage();
return;
}
fCurrentPage = page;
moveToPage(false);
} finally {
fLock.unlock();
}
}
/**
* Moves to a certain message defined by timestamp in current page
*/
protected void moveToMessageInPage() {
fLock.lock();
try {
if (!fView.getSDWidget().isDisposed()) {
// Check for GUI thread
if (Display.getCurrent() != null) {
// Already in GUI thread - execute directly
TmfSyncMessage prevMessage = null;
TmfSyncMessage syncMessage = null;
boolean isExactTime = false;
for (int i = 0; i < fFrame.syncMessageCount(); i++) {
if (fFrame.getSyncMessage(i) instanceof TmfSyncMessage) {
syncMessage = (TmfSyncMessage) fFrame.getSyncMessage(i);
if (syncMessage.getStartTime().compareTo(fCurrentTime) == 0) {
isExactTime = true;
break;
} else if ((syncMessage.getStartTime().compareTo(fCurrentTime) > 0) && (prevMessage != null)) {
syncMessage = prevMessage;
break;
}
prevMessage = syncMessage;
}
}
if (fIsSelect && isExactTime) {
fView.getSDWidget().moveTo(syncMessage);
} else {
fView.getSDWidget().ensureVisible(syncMessage);
fView.getSDWidget().clearSelection();
fView.getSDWidget().redraw();
}
} else {
// Not in GUI thread - queue action in GUI thread.
fView.getSDWidget().getDisplay().asyncExec(this::moveToMessageInPage);
}
}
} finally {
fLock.unlock();
}
}
/**
* Moves to a certain message defined by timestamp (across pages)
*/
protected void moveToPage() {
moveToPage(true);
}
/**
* Moves to a certain page.
*
* @param notifyAll
* true to broadcast time range signal to other signal handlers
* else false
*/
protected void moveToPage(boolean notifyAll) {
TmfTimeRange window = null;
fLock.lock();
try {
// Safety check
if (fCurrentPage > fCheckPoints.size()) {
return;
}
window = fCheckPoints.get(fCurrentPage);
} finally {
fLock.unlock();
}
if (window == null) {
window = TmfTimeRange.ETERNITY;
}
fPageRequest = new TmfEventRequest(ITmfEvent.class, window, 0,
ITmfEventRequest.ALL_DATA, ITmfEventRequest.ExecutionType.FOREGROUND) {
private final List<ITmfSyncSequenceDiagramEvent> fSdEvent = new ArrayList<>();
@Override
public void handleData(ITmfEvent event) {
super.handleData(event);
ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event);
if (sdEvent != null) {
fSdEvent.add(sdEvent);
}
}
@Override
public void handleSuccess() {
fillCurrentPage(fSdEvent);
super.handleSuccess();
}
};
fTrace.sendRequest(fPageRequest);
if (notifyAll) {
TmfTimeRange timeRange = getSignalTimeRange(window.getStartTime());
broadcast(new TmfWindowRangeUpdatedSignal(this, timeRange, fTrace));
}
}
/**
* Gets page that contains timestamp
*
* @param time
* The timestamp
* @return page that contains the time
*/
protected int getPage(ITmfTimestamp time) {
int page;
int size;
fLock.lock();
try {
size = fCheckPoints.size();
for (page = 0; page < size; page++) {
TmfTimeRange timeRange = fCheckPoints.get(page);
if (timeRange.getEndTime().compareTo(time) >= 0) {
break;
}
}
if (page >= size) {
page = size - 1;
}
return page;
} finally {
fLock.unlock();
}
}
/**
* Background search in trace for expression in criteria.
*
* @param findCriteria
* The find criteria
* @return true if background request was started else false
*/
protected boolean findInNextPages(Criteria findCriteria) {
fLock.lock();
try {
if (fFindJob != null) {
return true;
}
int nextPage = fCurrentPage + 1;
if ((nextPage) >= fCheckPoints.size()) {
// we are at the end
return false;
}
TmfTimeRange window = new TmfTimeRange(fCheckPoints.get(nextPage).getStartTime(), fCheckPoints.get(fCheckPoints.size() - 1).getEndTime());
fFindJob = new SearchJob(findCriteria, window);
fFindJob.schedule();
fView.toggleWaitCursorAsync(true);
} finally {
fLock.unlock();
}
return true;
}
/**
* Gets time range for time range signal.
*
* @param startTime
* The start time of time range.
* @return the time range
*/
protected TmfTimeRange getSignalTimeRange(ITmfTimestamp startTime) {
fLock.lock();
try {
TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
long offset = fTrace == null ? 0 : currentRange.getEndTime().getDelta(currentRange.getStartTime()).toNanos();
ITmfTimestamp initialEndOfWindow = TmfTimestamp.create(startTime.getValue() + offset, startTime.getScale());
return new TmfTimeRange(startTime, initialEndOfWindow);
} finally {
fLock.unlock();
}
}
/**
* Checks if filter criteria matches the message name in given SD event.
*
* @param sdEvent
* The SD event to check
* @return true if match else false.
*/
protected boolean filterMessage(ITmfSyncSequenceDiagramEvent sdEvent) {
fLock.lock();
try {
if (fFilterCriteria != null) {
for (FilterCriteria criteria : fFilterCriteria) {
if (criteria.isActive() && criteria.getCriteria().isSyncMessageSelected() && criteria.getCriteria().matches(sdEvent.getName())) {
return true;
}
}
}
} finally {
fLock.unlock();
}
return false;
}
/**
* Checks if filter criteria matches a lifeline name (sender or receiver) in
* given SD event.
*
* @param lifeline
* the message receiver
* @return true if match else false.
*/
protected boolean filterLifeLine(String lifeline) {
fLock.lock();
try {
if (fFilterCriteria != null) {
for (FilterCriteria criteria : fFilterCriteria) {
if (criteria.isActive() && criteria.getCriteria().isLifeLineSelected() && criteria.getCriteria().matches(lifeline)) {
return true;
}
}
}
} finally {
fLock.unlock();
}
return false;
}
/**
* Job to search in trace for given time range.
*/
protected class SearchJob extends Job {
/**
* The search event request.
*/
protected final SearchEventRequest fSearchRequest;
/**
* Constructor
*
* @param findCriteria
* The search criteria
* @param window
* Time range to search in
*/
public SearchJob(Criteria findCriteria, TmfTimeRange window) {
super(Messages.TmfUml2SDSyncLoader_SearchJobDescrition);
fSearchRequest = new SearchEventRequest(window, ITmfEventRequest.ALL_DATA,
ITmfEventRequest.ExecutionType.FOREGROUND, findCriteria);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
fSearchRequest.setMonitor(monitor);
fTrace.sendRequest(fSearchRequest);
try {
fSearchRequest.waitForCompletion();
} catch (InterruptedException e) {
Activator.getDefault().logError("Search request interrupted!", e); //$NON-NLS-1$
Thread.currentThread().interrupt();
}
IStatus status = Status.OK_STATUS;
if (fSearchRequest.isFound() && (fSearchRequest.getFoundTime() != null)) {
fCurrentTime = fSearchRequest.getFoundTime();
/*
* Avoid double-selection. Selection will be done when calling
* find(criteria) after moving to relevant page
*/
fIsSelect = false;
if (!fView.getSDWidget().isDisposed()) {
fView.getSDWidget().getDisplay().asyncExec(() -> moveToMessage());
}
} else {
if (monitor.isCanceled()) {
status = Status.CANCEL_STATUS;
} else {
// String was not found
status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, Messages.TmfUml2SDSyncLoader_SearchNotFound);
}
setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
}
monitor.done();
fLock.lock();
try {
fView.toggleWaitCursorAsync(false);
fFindJob = null;
} finally {
fLock.unlock();
}
return status;
}
@Override
protected void canceling() {
fSearchRequest.cancel();
fLock.lock();
try {
fFindJob = null;
} finally {
fLock.unlock();
}
}
}
/**
* TMF event request for searching within trace.
*/
protected class SearchEventRequest extends TmfEventRequest {
/**
* The find criteria.
*/
private final Criteria fCriteria;
/**
* A progress monitor
*/
private IProgressMonitor fMonitor;
/**
* Flag to indicate that node was found according the criteria .
*/
private boolean fIsFound = false;
/**
* Time stamp of found item.
*/
private ITmfTimestamp fFoundTime = null;
/**
* Constructor
*
* @param range
* see
* {@link TmfEventRequest#TmfEventRequest(Class, TmfTimeRange, long, int, org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType)
* TmfEventRequest}
* @param nbRequested
* see
* {@link TmfEventRequest#TmfEventRequest(Class, TmfTimeRange, long, int, org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType)
* TmfEventRequest}
* @param priority
* {@link org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType#FOREGROUND}
* or
* {@link org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType#BACKGROUND}
* @param criteria
* The search criteria
*/
public SearchEventRequest(TmfTimeRange range, int nbRequested, ExecutionType priority, Criteria criteria) {
this(range, nbRequested, priority, criteria, null);
}
/**
* Constructor
*
* @param range
* see
* {@link TmfEventRequest#TmfEventRequest(Class, TmfTimeRange, long, int, org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType)
* TmfEventRequest}
* @param nbRequested
* see
* {@link TmfEventRequest#TmfEventRequest(Class, TmfTimeRange, long, int, org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType)
* TmfEventRequest}
* @param priority
* {@link org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType#FOREGROUND}
* or
* {@link org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType#BACKGROUND}
* @param criteria
* The search criteria
* @param monitor
* progress monitor
*/
public SearchEventRequest(TmfTimeRange range, int nbRequested, ExecutionType priority, Criteria criteria, IProgressMonitor monitor) {
super(ITmfEvent.class, range, 0, nbRequested, priority);
fCriteria = new Criteria(criteria);
fMonitor = monitor;
}
@Override
public void handleData(ITmfEvent event) {
super.handleData(event);
if ((fMonitor != null) && fMonitor.isCanceled()) {
super.cancel();
return;
}
ITmfSyncSequenceDiagramEvent sdEvent = getSequenceDiagramEvent(event);
if (sdEvent != null) {
if (fCriteria.isLifeLineSelected()) {
if (fCriteria.matches(sdEvent.getSender())) {
fFoundTime = event.getTimestamp();
fIsFound = true;
super.cancel();
}
if (fCriteria.matches(sdEvent.getReceiver())) {
fFoundTime = event.getTimestamp();
fIsFound = true;
super.cancel();
}
}
if (fCriteria.isSyncMessageSelected() && fCriteria.matches(sdEvent.getName())) {
fFoundTime = event.getTimestamp();
fIsFound = true;
super.cancel();
}
}
}
/**
* Set progress monitor.
*
* @param monitor
* The monitor to assign
*/
public void setMonitor(IProgressMonitor monitor) {
fMonitor = monitor;
}
/**
* Check if find criteria was met.
*
* @return true if find criteria was met.
*/
public boolean isFound() {
return fIsFound;
}
/**
* Returns timestamp of found time.
*
* @return timestamp of found time.
*/
public ITmfTimestamp getFoundTime() {
return fFoundTime;
}
}
/**
* Job class to provide progress monitor feedback.
*
* @author Bernd Hufmann
*/
protected static class IndexingJob extends Job {
/**
* @param name
* The job name
*/
public IndexingJob(String name) {
super(name);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
while (!monitor.isCanceled()) {
try {
Thread.sleep(INDEXING_THREAD_SLEEP_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Status.OK_STATUS;
}
}
monitor.done();
return Status.OK_STATUS;
}
}
/**
* Returns sequence diagram event if details in given event are available
* else null.
*
* @param tmfEvent
* Event to parse for sequence diagram event details
* @return sequence diagram event if details are available else null
*/
protected ITmfSyncSequenceDiagramEvent getSequenceDiagramEvent(ITmfEvent tmfEvent) {
// type = .*RECEIVE.* or .*SEND.*
// content = sender:<sender name>:receiver:<receiver
// name>,signal:<signal name>
String eventName = tmfEvent.getName();
if (eventName.contains(Messages.TmfUml2SDSyncLoader_EventTypeSend) || eventName.contains(Messages.TmfUml2SDSyncLoader_EventTypeReceive)) {
Object sender = tmfEvent.getContent().getFieldValue(Object.class, NonNullUtils.checkNotNull(Messages.TmfUml2SDSyncLoader_FieldSender));
Object receiver = tmfEvent.getContent().getFieldValue(Object.class, NonNullUtils.checkNotNull(Messages.TmfUml2SDSyncLoader_FieldReceiver));
ITmfEventField content = tmfEvent.getContent();
Object name = content.getFieldValue(Object.class, NonNullUtils.checkNotNull(Messages.TmfUml2SDSyncLoader_FieldSignal));
if ((sender != null) && (receiver != null) && (name != null)) {
ITmfSyncSequenceDiagramEvent sdEvent = new TmfSyncSequenceDiagramEvent(tmfEvent,
sender.toString(),
receiver.toString(),
name.toString());
return sdEvent;
}
}
return null;
}
}