/******************************************************************************* * Copyright (c) 2010, 2014 Ericsson, École Polytechnique de Montréal * * 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: * Patrick Tasse - Initial API and implementation * Geneviève Bastien - Experiment instantiated with experiment type *******************************************************************************/ package fr.inria.linuxtools.tmf.ui.editors; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.InvalidRegistryObjectException; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.IReusableEditor; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.ide.IGotoMarker; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.views.properties.IPropertySheetPage; import org.osgi.framework.Bundle; import fr.inria.linuxtools.internal.tmf.ui.Activator; import fr.inria.linuxtools.internal.tmf.ui.parsers.custom.CustomEventsTable; import fr.inria.linuxtools.tmf.core.TmfCommonConstants; import fr.inria.linuxtools.tmf.core.parsers.custom.CustomTxtTrace; import fr.inria.linuxtools.tmf.core.parsers.custom.CustomXmlTrace; import fr.inria.linuxtools.tmf.core.project.model.TmfTraceType.TraceElementType; import fr.inria.linuxtools.tmf.core.signal.TmfSignalHandler; import fr.inria.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal; import fr.inria.linuxtools.tmf.core.signal.TmfTraceClosedSignal; import fr.inria.linuxtools.tmf.core.signal.TmfTraceOpenedSignal; import fr.inria.linuxtools.tmf.core.signal.TmfTraceSelectedSignal; import fr.inria.linuxtools.tmf.core.trace.ITmfContext; import fr.inria.linuxtools.tmf.core.trace.ITmfTrace; import fr.inria.linuxtools.tmf.core.trace.TmfExperiment; import fr.inria.linuxtools.tmf.core.trace.TmfTrace; import fr.inria.linuxtools.tmf.ui.project.model.Messages; import fr.inria.linuxtools.tmf.ui.project.model.TmfExperimentElement; import fr.inria.linuxtools.tmf.ui.project.model.TmfOpenTraceHelper; import fr.inria.linuxtools.tmf.ui.project.model.TmfProjectElement; import fr.inria.linuxtools.tmf.ui.project.model.TmfProjectRegistry; import fr.inria.linuxtools.tmf.ui.project.model.TmfTraceElement; import fr.inria.linuxtools.tmf.ui.project.model.TmfTraceTypeUIUtils; import fr.inria.linuxtools.tmf.ui.viewers.events.TmfEventsTable; /** * Editor for TMF events * * @version 1.0 * @author Patrick Tasse * @since 2.0 */ public class TmfEventsEditor extends TmfEditor implements ITmfTraceEditor, IReusableEditor, IPropertyListener, IResourceChangeListener, ISelectionProvider, ISelectionChangedListener, IPartListener, IGotoMarker { /** ID for this class */ public static final String ID = "fr.inria.linuxtools.tmf.ui.editors.events"; //$NON-NLS-1$ private TmfEventsTable fEventsTable; private IFile fFile; private ITmfTrace fTrace; private Composite fParent; private ListenerList fSelectionChangedListeners = new ListenerList(); private boolean fTraceSelected; private IMarker fPendingGotoMarker; @Override public void doSave(final IProgressMonitor monitor) { } @Override public void doSaveAs() { } @Override public void init(final IEditorSite site, IEditorInput input) throws PartInitException { IFileEditorInput fileEditorInput; if (input instanceof TmfEditorInput) { fFile = ((TmfEditorInput) input).getFile(); fTrace = ((TmfEditorInput) input).getTrace(); /* change the input to a FileEditorInput to allow open handlers to find this editor */ fileEditorInput = new FileEditorInput(fFile); } else if (input instanceof IFileEditorInput) { fileEditorInput = (IFileEditorInput) input; fFile = fileEditorInput.getFile(); if (fFile == null) { throw new PartInitException("Invalid IFileEditorInput: " + fileEditorInput); //$NON-NLS-1$ } try { final String traceTypeId = fFile.getPersistentProperty(TmfCommonConstants.TRACETYPE); if (traceTypeId == null) { throw new PartInitException(Messages.TmfOpenTraceHelper_NoTraceType); } if (traceTypeId.equals(TmfExperiment.class.getCanonicalName())) { // Special case: experiment bookmark resource final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true); if (project == null) { throw new PartInitException(Messages.TmfOpenTraceHelper_NoTraceType); } for (final TmfExperimentElement experimentElement : project.getExperimentsFolder().getExperiments()) { if (experimentElement.getResource().equals(fFile.getParent())) { setPartName(experimentElement.getName()); super.setSite(site); super.setInput(fileEditorInput); TmfOpenTraceHelper.reopenTraceFromElement(experimentElement, this); return; } } } else if (traceTypeId.equals(TmfTrace.class.getCanonicalName())) { // Special case: trace bookmark resource final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true); for (final TmfTraceElement traceElement : project.getTracesFolder().getTraces()) { if (traceElement.getResource().equals(fFile.getParent())) { setPartName(traceElement.getName()); super.setSite(site); super.setInput(fileEditorInput); TmfOpenTraceHelper.reopenTraceFromElement(traceElement, this); return; } } } else { final TmfProjectElement project = TmfProjectRegistry.getProject(fFile.getProject(), true); for (final TmfTraceElement traceElement : project.getTracesFolder().getTraces()) { if (traceElement.getResource().equals(fFile)) { setPartName(traceElement.getName()); super.setSite(site); super.setInput(fileEditorInput); TmfOpenTraceHelper.reopenTraceFromElement(traceElement, this); return; } } } } catch (final PartInitException e) { throw e; } catch (final InvalidRegistryObjectException e) { Activator.getDefault().logError("Error initializing TmfEventsEditor", e); //$NON-NLS-1$ } catch (final CoreException e) { Activator.getDefault().logError("Error initializing TmfEventsEditor", e); //$NON-NLS-1$ } } else { throw new PartInitException("Invalid IEditorInput: " + input.getClass()); //$NON-NLS-1$ } if (fTrace == null) { throw new PartInitException("Invalid IEditorInput: " + fFile.getName()); //$NON-NLS-1$ } super.setSite(site); super.setInput(fileEditorInput); } @Override public boolean isDirty() { return false; } @Override public boolean isSaveAsAllowed() { return false; } @Override public void setInput(final IEditorInput input) { super.setInput(input); firePropertyChange(IEditorPart.PROP_INPUT); } @Override public void propertyChanged(final Object source, final int propId) { if (propId == IEditorPart.PROP_INPUT && getEditorInput() instanceof TmfEditorInput) { if (fTrace != null) { broadcast(new TmfTraceClosedSignal(this, fTrace)); } fTraceSelected = false; fFile = ((TmfEditorInput) getEditorInput()).getFile(); fTrace = ((TmfEditorInput) getEditorInput()).getTrace(); /* change the input to a FileEditorInput to allow open handlers to find this editor */ super.setInput(new FileEditorInput(fFile)); fEventsTable.dispose(); if (fTrace != null) { setPartName(fTrace.getName()); fEventsTable = createEventsTable(fParent, fTrace.getCacheSize()); fEventsTable.addSelectionChangedListener(this); fEventsTable.setTrace(fTrace, true); fEventsTable.refreshBookmarks(fFile); if (fPendingGotoMarker != null) { fEventsTable.gotoMarker(fPendingGotoMarker); fPendingGotoMarker = null; } /* ensure start time is set */ final ITmfContext context = fTrace.seekEvent(0); fTrace.getNext(context); context.dispose(); broadcast(new TmfTraceOpenedSignal(this, fTrace, fFile)); } else { setPartName(getEditorInput().getName()); fEventsTable = new TmfEventsTable(fParent, 0); fEventsTable.addSelectionChangedListener(this); } fParent.layout(); } } @Override public void createPartControl(final Composite parent) { fParent = parent; if (fTrace != null) { setPartName(fTrace.getName()); fEventsTable = createEventsTable(parent, fTrace.getCacheSize()); fEventsTable.addSelectionChangedListener(this); fEventsTable.setTrace(fTrace, true); fEventsTable.refreshBookmarks(fFile); /* ensure start time is set */ final ITmfContext context = fTrace.seekEvent(0); fTrace.getNext(context); context.dispose(); broadcast(new TmfTraceOpenedSignal(this, fTrace, fFile)); } else { fEventsTable = new TmfEventsTable(parent, 0); fEventsTable.addSelectionChangedListener(this); } IStatusLineManager statusLineManager = getEditorSite().getActionBars().getStatusLineManager(); fEventsTable.setStatusLineManager(statusLineManager); addPropertyListener(this); ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); // we need to wrap the ISelectionProvider interface in the editor because // the events table can be replaced later while the selection changed listener // is only added once by the platform to the selection provider set here getSite().setSelectionProvider(this); getSite().getPage().addPartListener(this); } @Override public void dispose() { if (getSite() != null) { getSite().getPage().removePartListener(this); } ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); removePropertyListener(this); if (fTrace != null) { broadcast(new TmfTraceClosedSignal(this, fTrace)); } if (fEventsTable != null) { fEventsTable.dispose(); } super.dispose(); } /** * Create the events table * * @param parent the parent composite * @param cacheSize the cache size * @return an events table instance */ protected TmfEventsTable createEventsTable(final Composite parent, final int cacheSize) { TmfEventsTable eventsTable = getEventsTable(parent, cacheSize); if (eventsTable == null) { eventsTable = new TmfEventsTable(parent, cacheSize); } return eventsTable; } private TmfEventsTable getEventsTable(final Composite parent, final int cacheSize) { TmfEventsTable eventsTable = null; try { if (fTrace.getResource() == null) { return null; } final String traceType = fTrace.getResource().getPersistentProperty(TmfCommonConstants.TRACETYPE); if (traceType == null) { return null; } if (traceType.startsWith(CustomTxtTrace.class.getCanonicalName())) { return new CustomEventsTable(((CustomTxtTrace) fTrace).getDefinition(), parent, cacheSize); } if (traceType.startsWith(CustomXmlTrace.class.getCanonicalName())) { return new CustomEventsTable(((CustomXmlTrace) fTrace).getDefinition(), parent, cacheSize); } TraceElementType type = TraceElementType.TRACE; if (fTrace instanceof TmfExperiment) { type = TraceElementType.EXPERIMENT; } for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(type)) { if (ce.getAttribute(TmfTraceTypeUIUtils.TRACETYPE_ATTR).equals(traceType)) { final IConfigurationElement[] eventsTableTypeCE = ce.getChildren(TmfTraceTypeUIUtils.EVENTS_TABLE_TYPE_ELEM); if (eventsTableTypeCE.length != 1) { break; } final String eventsTableType = eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR); if ((eventsTableType == null) || (eventsTableType.length() == 0)) { break; } final Bundle bundle = Platform.getBundle(ce.getContributor().getName()); final Class<?> c = bundle.loadClass(eventsTableType); final Class<?>[] constructorArgs = new Class[] { Composite.class, int.class }; final Constructor<?> constructor = c.getConstructor(constructorArgs); final Object[] args = new Object[] { parent, cacheSize }; eventsTable = (TmfEventsTable) constructor.newInstance(args); break; } } if (fTrace instanceof TmfExperiment && (eventsTable == null)) { eventsTable = getExperimentEventsTable((TmfExperiment) fTrace, parent, cacheSize); } } catch (final InvalidRegistryObjectException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final CoreException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final ClassNotFoundException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final SecurityException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final NoSuchMethodException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final IllegalArgumentException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final InstantiationException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final IllegalAccessException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } catch (final InvocationTargetException e) { Activator.getDefault().logError("Error getting TmfEventsTable", e); //$NON-NLS-1$ } return eventsTable; } /** * Get the events table for an experiment. If all traces in the experiment * are of the same type, use the extension point specified event table * * @param experiment * the experiment * @param parent * the parent Composite * @param cacheSize * the event table cache size * @return an events table of the appropriate type */ private static TmfEventsTable getExperimentEventsTable( final TmfExperiment experiment, final Composite parent, final int cacheSize) { TmfEventsTable eventsTable = null; String commonTraceType = null; try { for (final ITmfTrace trace : experiment.getTraces()) { final IResource resource = trace.getResource(); if (resource == null) { return null; } final String traceType = resource.getPersistentProperty(TmfCommonConstants.TRACETYPE); if ((commonTraceType != null) && !commonTraceType.equals(traceType)) { return null; } commonTraceType = traceType; } if (commonTraceType == null) { return null; } if (commonTraceType.startsWith(CustomTxtTrace.class.getCanonicalName())) { return new CustomEventsTable(((CustomTxtTrace) experiment.getTraces()[0]).getDefinition(), parent, cacheSize); } if (commonTraceType.startsWith(CustomXmlTrace.class.getCanonicalName())) { return new CustomEventsTable(((CustomXmlTrace) experiment.getTraces()[0]).getDefinition(), parent, cacheSize); } for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(TraceElementType.TRACE)) { if (ce.getAttribute(TmfTraceTypeUIUtils.TRACETYPE_ATTR).equals(commonTraceType)) { final IConfigurationElement[] eventsTableTypeCE = ce.getChildren(TmfTraceTypeUIUtils.EVENTS_TABLE_TYPE_ELEM); if (eventsTableTypeCE.length != 1) { break; } final String eventsTableType = eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR); if ((eventsTableType == null) || (eventsTableType.length() == 0)) { break; } final Bundle bundle = Platform.getBundle(ce.getContributor().getName()); final Class<?> c = bundle.loadClass(eventsTableType); final Class<?>[] constructorArgs = new Class[] { Composite.class, int.class }; final Constructor<?> constructor = c.getConstructor(constructorArgs); final Object[] args = new Object[] { parent, cacheSize }; eventsTable = (TmfEventsTable) constructor.newInstance(args); break; } } } catch (final CoreException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final InvalidRegistryObjectException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final SecurityException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final IllegalArgumentException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final ClassNotFoundException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final NoSuchMethodException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final InstantiationException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final IllegalAccessException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } catch (final InvocationTargetException e) { Activator.getDefault().logError("Error getting TmfEventsTable for experiment", e); //$NON-NLS-1$ } return eventsTable; } @Override public ITmfTrace getTrace() { return fTrace; } @Override public void setFocus() { fEventsTable.setFocus(); } @Override public Object getAdapter(final Class adapter) { if (IGotoMarker.class.equals(adapter)) { if (fTrace == null || fEventsTable == null) { return this; } return fEventsTable; } else if (IPropertySheetPage.class.equals(adapter)) { return new UnsortedPropertySheetPage(); } return super.getAdapter(adapter); } /** * @since 2.1 */ @Override public void gotoMarker(IMarker marker) { if (fTrace == null || fEventsTable == null) { fPendingGotoMarker = marker; } else { fEventsTable.gotoMarker(marker); } } @Override public void resourceChanged(final IResourceChangeEvent event) { for (final IMarkerDelta delta : event.findMarkerDeltas(IMarker.BOOKMARK, false)) { if (delta.getResource().equals(fFile)) { if (delta.getKind() == IResourceDelta.REMOVED) { final IMarker bookmark = delta.getMarker(); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { fEventsTable.removeBookmark(bookmark); } }); } else if (delta.getKind() == IResourceDelta.CHANGED) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { fEventsTable.getTable().refresh(); } }); } } } } // ------------------------------------------------------------------------ // ISelectionProvider // ------------------------------------------------------------------------ /** * @since 2.0 */ @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { fSelectionChangedListeners.add(listener); } /** * @since 2.0 */ @Override public ISelection getSelection() { if (fEventsTable == null) { return StructuredSelection.EMPTY; } return fEventsTable.getSelection(); } /** * @since 2.0 */ @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { fSelectionChangedListeners.remove(listener); } /** * @since 2.0 */ @Override public void setSelection(ISelection selection) { // not implemented } /** * Notifies any selection changed listeners that the viewer's selection has changed. * Only listeners registered at the time this method is called are notified. * * @param event a selection changed event * * @see ISelectionChangedListener#selectionChanged * @since 2.0 */ protected void fireSelectionChanged(final SelectionChangedEvent event) { Object[] listeners = fSelectionChangedListeners.getListeners(); for (int i = 0; i < listeners.length; ++i) { final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i]; SafeRunnable.run(new SafeRunnable() { @Override public void run() { l.selectionChanged(event); } }); } } // ------------------------------------------------------------------------ // ISelectionChangedListener // ------------------------------------------------------------------------ /** * @since 2.0 */ @Override public void selectionChanged(SelectionChangedEvent event) { fireSelectionChanged(event); } // ------------------------------------------------------------------------ // IPartListener // ------------------------------------------------------------------------ /** * @since 2.0 */ @Override public void partActivated(IWorkbenchPart part) { if (part == this && fTrace != null) { if (fTraceSelected) { return; } fTraceSelected = true; broadcast(new TmfTraceSelectedSignal(this, fTrace)); } } /** * @since 2.0 */ @Override public void partBroughtToTop(IWorkbenchPart part) { if (part == this && fTrace != null) { if (fTraceSelected) { return; } fTraceSelected = true; broadcast(new TmfTraceSelectedSignal(this, fTrace)); } } /** * @since 2.0 */ @Override public void partClosed(IWorkbenchPart part) { } /** * @since 2.0 */ @Override public void partDeactivated(IWorkbenchPart part) { } /** * @since 2.0 */ @Override public void partOpened(IWorkbenchPart part) { } // ------------------------------------------------------------------------ // Global commands // ------------------------------------------------------------------------ /** * Add a bookmark */ public void addBookmark() { fEventsTable.addBookmark(fFile); } // ------------------------------------------------------------------------ // Signal handlers // ------------------------------------------------------------------------ /** * Handler for the Trace Selected signal * * @param signal The incoming signal */ @TmfSignalHandler public void traceSelected(final TmfTraceSelectedSignal signal) { if ((signal.getSource() != this)) { if (signal.getTrace().equals(fTrace)) { getSite().getPage().bringToTop(this); } else { fTraceSelected = false; } } } /** * Update the display to use the updated timestamp format * * @param signal the incoming signal * @since 2.0 */ @TmfSignalHandler public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { if (fEventsTable != null) { fEventsTable.refresh(); } } }