| /******************************************************************************* |
| * Copyright (c) 2014, 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: |
| * Alexandre Montplaisir - Initial API and implementation |
| * Patrick Tasse - Add support for folder elements |
| * Bernd Hufmann - Update trace type auto-detection |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.ui.project.model; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.RowLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.tracecompass.tmf.core.TmfCommonConstants; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType.TraceElementType; |
| import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; |
| import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsTable; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * Utils class for the UI-specific parts of @link {@link TmfTraceType}. |
| * |
| * @author Alexandre Montplaisir |
| */ |
| public final class TmfTraceTypeUIUtils { |
| |
| /** Extension point ID */ |
| public static final String TMF_TRACE_TYPE_UI_ID = "org.eclipse.linuxtools.tmf.ui.tracetypeui"; //$NON-NLS-1$ |
| |
| /** Extension point element 'type' (should match the type in TmfTraceType) */ |
| public static final String TYPE_ELEM = "type"; //$NON-NLS-1$ |
| |
| /** |
| * Extension point element 'experiment' (should match the type in |
| * TmfTraceType) |
| */ |
| public static final String EXPERIMENT_ELEM = "experiment"; //$NON-NLS-1$ |
| |
| /** Extension point element 'Default editor' */ |
| public static final String DEFAULT_EDITOR_ELEM = "defaultEditor"; //$NON-NLS-1$ |
| |
| /** Extension point element 'Events table type' */ |
| public static final String EVENTS_TABLE_TYPE_ELEM = "eventsTableType"; //$NON-NLS-1$ |
| |
| /** Extension point element 'Event Table Columns' */ |
| public static final String EVENT_TABLE_COLUMNS = "eventTableColumns"; //$NON-NLS-1$ |
| |
| /** Extension point element 'perspective' |
| * @since 2.3*/ |
| public static final String PERSPECTIVE_ELEM = "perspective"; //$NON-NLS-1$ |
| |
| /** Extension point attribute 'id' |
| * @since 2.3*/ |
| public static final String ID_ATTR = "id"; //$NON-NLS-1$ |
| |
| /** Extension point attribute 'tracetype' */ |
| public static final String TRACETYPE_ATTR = "tracetype"; //$NON-NLS-1$ |
| |
| /** Extension point attribute 'icon' */ |
| public static final String ICON_ATTR = "icon"; //$NON-NLS-1$ |
| |
| /** Extension point attribute 'class' (attribute of other elements) */ |
| public static final String CLASS_ATTR = "class"; //$NON-NLS-1$ |
| |
| /** |
| * Extension point attribute 'useTraceAspects' |
| * @since 5.2 |
| */ |
| public static final String USE_TRACE_ASPECTS_ATTR = "useTraceAspects"; //$NON-NLS-1$ |
| |
| private TmfTraceTypeUIUtils() { |
| } |
| |
| private static TraceTypeHelper getTraceTypeToSet(List<TraceTypeHelper> candidates, Shell shell) { |
| final Map<String, String> names = new HashMap<>(); |
| Shell shellToShow = new Shell(shell); |
| shellToShow.setText(Messages.TmfTraceType_SelectTraceType); |
| final String candidatesToSet[] = new String[1]; |
| for (TraceTypeHelper candidate : candidates) { |
| Button b = new Button(shellToShow, SWT.RADIO); |
| final String displayName = candidate.getCategoryName() + ':' + candidate.getName(); |
| b.setText(displayName); |
| names.put(displayName, candidate.getTraceTypeId()); |
| |
| b.addSelectionListener(new SelectionListener() { |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| final Button source = (Button) e.getSource(); |
| candidatesToSet[0] = (names.get(source.getText())); |
| source.getParent().dispose(); |
| } |
| |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| // Do nothing |
| } |
| }); |
| } |
| shellToShow.setLayout(new RowLayout(SWT.VERTICAL)); |
| shellToShow.pack(); |
| shellToShow.open(); |
| |
| Display display = shellToShow.getDisplay(); |
| while (!shellToShow.isDisposed()) { |
| if (!display.readAndDispatch()) { |
| display.sleep(); |
| } |
| } |
| return TmfTraceType.getTraceType(candidatesToSet[0]); |
| } |
| |
| /** |
| * This member figures out the trace type of a given trace. It will prompt |
| * the user if it needs more information to properly pick the trace type. |
| * |
| * @param path |
| * The path of trace to import (file or directory for directory |
| * traces) |
| * @param shell |
| * a shell to query user in case of multiple valid trace types. |
| * If it is null, than the first one alphabetically is selected. |
| * @param traceTypeHint |
| * the ID of a trace (like "o.e.l.specifictrace" ) |
| * @return {@link TraceTypeHelper} for valid trace type or null if no valid |
| * trace type was found in case of single file trace |
| * @throws TmfTraceImportException |
| * if there are errors in the trace file or no trace type found |
| * for a directory trace |
| */ |
| public static @Nullable TraceTypeHelper selectTraceType(String path, Shell shell, String traceTypeHint) throws TmfTraceImportException { |
| List<TraceTypeHelper> candidates = TmfTraceType.selectTraceType(path, traceTypeHint); |
| |
| if (candidates.isEmpty()) { |
| return null; |
| } |
| |
| if ((candidates.size() == 1) || (shell == null)) { |
| return candidates.get(0); |
| } |
| |
| return getTraceTypeToSet(candidates, shell); |
| } |
| |
| /** |
| * Set the trace type of a {@link TraceTypeHelper}. Should only be |
| * used internally by this project. |
| * |
| * @param resource |
| * the resource to set |
| * @param traceType |
| * the {@link TraceTypeHelper} to set the trace type to. |
| * @return Status.OK_Status if successful, error is otherwise. |
| * @throws CoreException |
| * An exception caused by accessing eclipse project items. |
| */ |
| public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType) throws CoreException { |
| return setTraceType(resource, traceType, true); |
| } |
| |
| /** |
| * Set the trace type of a {@link TraceTypeHelper}. Should only be |
| * used internally by this project. |
| * |
| * @param resource |
| * the resource to set |
| * @param traceType |
| * the {@link TraceTypeHelper} to set the trace type to. |
| * @param refresh |
| * Flag for refreshing the project |
| * @return Status.OK_Status if successful, error is otherwise. |
| * @throws CoreException |
| * An exception caused by accessing eclipse project items. |
| */ |
| public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType, boolean refresh) throws CoreException { |
| String traceTypeId = traceType.getTraceTypeId(); |
| |
| resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceTypeId); |
| |
| TmfProjectElement tmfProject = TmfProjectRegistry.getProject(resource.getProject(), true); |
| if (tmfProject == null) { |
| return Status.CANCEL_STATUS; |
| } |
| TmfTraceFolder tracesFolder = tmfProject.getTracesFolder(); |
| TmfExperimentFolder experimentsFolder = tmfProject.getExperimentsFolder(); |
| if (tracesFolder != null) { |
| if (tracesFolder.getPath().isPrefixOf(resource.getFullPath())) { |
| String elementPath = resource.getFullPath().makeRelativeTo(tracesFolder.getPath()).toString(); |
| refreshTraceElement(tracesFolder.getTraces(), elementPath); |
| } |
| } |
| if ((tracesFolder == null) || (experimentsFolder != null)) { |
| if (experimentsFolder != null) { |
| if (resource.getParent().equals(experimentsFolder.getResource())) { |
| /* The trace type to set is for an experiment */ |
| for (TmfExperimentElement experimentElement : experimentsFolder.getExperiments()) { |
| if (resource.equals(experimentElement.getResource())) { |
| experimentElement.refreshTraceType(); |
| break; |
| } |
| } |
| } else { |
| for (TmfExperimentElement experimentElement : experimentsFolder.getExperiments()) { |
| if (experimentElement.getPath().isPrefixOf(resource.getFullPath())) { |
| String elementPath = resource.getFullPath().makeRelativeTo(experimentElement.getPath()).toString(); |
| refreshTraceElement(experimentElement.getTraces(), elementPath); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (refresh) { |
| tmfProject.refresh(); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| private static void refreshTraceElement(List<TmfTraceElement> traceElements, String elementPath) { |
| for (TmfTraceElement traceElement : traceElements) { |
| if (traceElement.getElementPath().equals(elementPath)) { |
| traceElement.refreshTraceType(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Retrieves all configuration elements from the platform extension registry |
| * for the trace type UI extension. |
| * |
| * @param elType |
| * The type of trace type requested, either TRACE or EXPERIMENT |
| * @return An array of trace type configuration elements |
| */ |
| public static IConfigurationElement[] getTypeUIElements(TraceElementType elType) { |
| String elementName = TYPE_ELEM; |
| if (elType == TraceElementType.EXPERIMENT) { |
| elementName = EXPERIMENT_ELEM; |
| } |
| IConfigurationElement[] elements = |
| Platform.getExtensionRegistry().getConfigurationElementsFor(TMF_TRACE_TYPE_UI_ID); |
| List<IConfigurationElement> typeElements = new LinkedList<>(); |
| for (IConfigurationElement element : elements) { |
| if (element.getName().equals(elementName)) { |
| typeElements.add(element); |
| } |
| } |
| return typeElements.toArray(new IConfigurationElement[typeElements.size()]); |
| } |
| |
| /** |
| * Get the UI elements for the given trace type |
| * |
| * @param traceType |
| * The tracetype ID |
| * @param elType |
| * The type of trace type requested, either TRACE or EXPERIMENT |
| * @return The top-level configuration element (access its children with |
| * .getChildren()). Or null if there is no such element. |
| */ |
| @Nullable |
| public static IConfigurationElement getTraceUIAttributes(String traceType, TraceElementType elType) { |
| IConfigurationElement[] elements = getTypeUIElements(elType); |
| for (IConfigurationElement ce : elements) { |
| if (traceType.equals(ce.getAttribute(TRACETYPE_ATTR))) { |
| return ce; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the Event Table type specified by the trace type's extension point, |
| * if there is one. |
| * |
| * @param trace |
| * The trace for which we want the events table. |
| * @param parent |
| * The parent composite that the event table will have |
| * @param cacheSize |
| * The cache size to use with this event table. Should be defined |
| * by the trace type. |
| * @return The corresponding Event Table, or 'null' if this trace type did |
| * not specify any. |
| */ |
| public static @Nullable TmfEventsTable getEventTable(ITmfTrace trace, Composite parent, int cacheSize) { |
| final String traceType = getTraceType(trace); |
| if (traceType == null) { |
| return null; |
| } |
| |
| TraceElementType elType = (trace instanceof TmfExperiment) ? TraceElementType.EXPERIMENT : TraceElementType.TRACE; |
| for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(elType)) { |
| 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); |
| final boolean useTraceAspects = Boolean.parseBoolean(eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.USE_TRACE_ASPECTS_ATTR)); |
| |
| if ((eventsTableType == null) || eventsTableType.isEmpty()) { |
| break; |
| } |
| try { |
| final Bundle bundle = Platform.getBundle(ce.getContributor().getName()); |
| final Class<?> c = bundle.loadClass(eventsTableType); |
| Class<?>[] constructorArgs = null; |
| Object[] args = null; |
| if (useTraceAspects) { |
| args = new Object[] { parent, cacheSize, trace.getEventAspects() }; |
| constructorArgs = new Class[] { Composite.class, int.class, Iterable.class }; |
| } else { |
| args = new Object[] { parent, cacheSize }; |
| constructorArgs = new Class[] { Composite.class, int.class }; |
| } |
| final Constructor<?> constructor = c.getConstructor(constructorArgs); |
| return (TmfEventsTable) constructor.newInstance(args); |
| } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | |
| IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the perspective id specified by the trace type's extension point, if |
| * there is one. |
| * |
| * @param trace |
| * The trace for which we want the perspective id. |
| * @return The corresponding perspective id, or 'null' if this trace type |
| * did not specify any. |
| * @since 2.3 |
| */ |
| public static @Nullable String getPerspectiveId(ITmfTrace trace) { |
| final String traceType = getTraceType(trace); |
| if (traceType == null) { |
| return null; |
| } |
| |
| TraceElementType elType = (trace instanceof TmfExperiment) ? TraceElementType.EXPERIMENT : TraceElementType.TRACE; |
| for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(elType)) { |
| if (ce.getAttribute(TRACETYPE_ATTR).equals(traceType)) { |
| final IConfigurationElement[] perspectiveCE = ce.getChildren(PERSPECTIVE_ELEM); |
| |
| if (perspectiveCE.length != 1) { |
| break; |
| } |
| final String perspectiveId = perspectiveCE[0].getAttribute(ID_ATTR); |
| if (!perspectiveId.isEmpty()) { |
| return perspectiveId; |
| } |
| break; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the trace type (as a String) for the given trace |
| * |
| * @param trace |
| * The trace object |
| * @return The String representing the trace type, or 'null' if this trace |
| * does not advertise it. |
| */ |
| private static @Nullable String getTraceType(ITmfTrace trace) { |
| IResource res = trace.getResource(); |
| if (res == null) { |
| return null; |
| } |
| try { |
| String traceType = res.getPersistentProperty(TmfCommonConstants.TRACETYPE); |
| /* May be null here too */ |
| return traceType; |
| |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| } |