/********************************************************************** * 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 v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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) { // ignore } } } /** * 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.size() > 0)) { 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.size() > 0)) { 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)); } } } @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(new Runnable() { @Override public void run() { 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(new Runnable() { @Override public void run() { 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)); } } /** * 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$ } 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(new Runnable() { @Override public void run() { 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) { 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; } }